From 892cb0e14109c687223c765ac67821c54cb07a1c Mon Sep 17 00:00:00 2001 From: Morgan McMillian Date: Sun, 15 Jul 2018 19:24:48 -0700 Subject: [PATCH] add native android build sources --- .gitignore | 4 - Makefile | 7 + README.md | 8 +- config.xml | 2 +- platforms/android/.gitignore | 14 + .../android/CordovaLib/AndroidManifest.xml | 23 + platforms/android/CordovaLib/build.gradle | 137 + platforms/android/CordovaLib/cordova.gradle | 205 + .../android/CordovaLib/project.properties | 16 + .../apache/cordova/AuthenticationToken.java | 69 + .../src/org/apache/cordova/BuildHelper.java | 70 + .../org/apache/cordova/CallbackContext.java | 142 + .../src/org/apache/cordova/CallbackMap.java | 65 + .../src/org/apache/cordova/Config.java | 71 + .../org/apache/cordova/ConfigXmlParser.java | 145 + .../org/apache/cordova/CordovaActivity.java | 519 ++ .../src/org/apache/cordova/CordovaArgs.java | 113 + .../src/org/apache/cordova/CordovaBridge.java | 187 + .../cordova/CordovaClientCertRequest.java | 105 + .../apache/cordova/CordovaDialogsHelper.java | 152 + .../cordova/CordovaHttpAuthHandler.java | 51 + .../org/apache/cordova/CordovaInterface.java | 97 + .../apache/cordova/CordovaInterfaceImpl.java | 249 + .../src/org/apache/cordova/CordovaPlugin.java | 422 ++ .../apache/cordova/CordovaPreferences.java | 101 + .../apache/cordova/CordovaResourceApi.java | 471 ++ .../org/apache/cordova/CordovaWebView.java | 142 + .../apache/cordova/CordovaWebViewEngine.java | 85 + .../apache/cordova/CordovaWebViewImpl.java | 615 +++ .../src/org/apache/cordova/CoreAndroid.java | 390 ++ .../src/org/apache/cordova/ExposedJsApi.java | 31 + .../cordova/ICordovaClientCertRequest.java | 66 + .../apache/cordova/ICordovaCookieManager.java | 33 + .../cordova/ICordovaHttpAuthHandler.java | 38 + .../src/org/apache/cordova/LOG.java | 244 + .../cordova/NativeToJsMessageQueue.java | 542 +++ .../org/apache/cordova/PermissionHelper.java | 87 + .../src/org/apache/cordova/PluginEntry.java | 70 + .../src/org/apache/cordova/PluginManager.java | 526 +++ .../src/org/apache/cordova/PluginResult.java | 198 + .../org/apache/cordova/ResumeCallback.java | 76 + .../src/org/apache/cordova/Whitelist.java | 170 + .../cordova/engine/SystemCookieManager.java | 69 + .../cordova/engine/SystemExposedJsApi.java | 53 + .../cordova/engine/SystemWebChromeClient.java | 277 ++ .../apache/cordova/engine/SystemWebView.java | 88 + .../cordova/engine/SystemWebViewClient.java | 367 ++ .../cordova/engine/SystemWebViewEngine.java | 319 ++ platforms/android/android.json | 439 ++ platforms/android/app/build.gradle | 327 ++ .../properties.gradle | 1 + .../android/app/src/main/AndroidManifest.xml | 24 + .../plugin/intent/IntentShim.java | 794 ++++ .../android/ShareContentPlugin.java | 62 + .../hiddentao/cordova/filepath/FilePath.java | 431 ++ .../java/com/megster/cordova/FileChooser.java | 82 + .../com/monkeystew/goober_m/MainActivity.java | 41 + .../java/io/ionic/keyboard/IonicKeyboard.java | 130 + .../org/apache/cordova/device/Device.java | 174 + .../apache/cordova/file/AssetFilesystem.java | 294 ++ .../cordova/file/ContentFilesystem.java | 223 + .../apache/cordova/file/DirectoryManager.java | 134 + .../cordova/file/EncodingException.java | 29 + .../cordova/file/FileExistsException.java | 29 + .../org/apache/cordova/file/FileUtils.java | 1225 +++++ .../org/apache/cordova/file/Filesystem.java | 331 ++ .../file/InvalidModificationException.java | 30 + .../apache/cordova/file/LocalFilesystem.java | 513 ++ .../cordova/file/LocalFilesystemURL.java | 64 + .../file/NoModificationAllowedException.java | 29 + .../apache/cordova/file/PendingRequests.java | 94 + .../cordova/file/TypeMismatchException.java | 30 + .../filetransfer/FileProgressResult.java | 63 + .../cordova/filetransfer/FileTransfer.java | 932 ++++ .../filetransfer/FileUploadResult.java | 73 + .../cordova/inappbrowser/InAppBrowser.java | 1259 +++++ .../inappbrowser/InAppBrowserDialog.java | 57 + .../inappbrowser/InAppChromeClient.java | 133 + .../cordova/splashscreen/SplashScreen.java | 413 ++ .../apache/cordova/statusbar/StatusBar.java | 276 ++ .../cordova/whitelist/WhitelistPlugin.java | 161 + .../res/drawable-hdpi/ic_action_next_item.png | Bin 0 -> 593 bytes .../drawable-hdpi/ic_action_previous_item.png | Bin 0 -> 599 bytes .../res/drawable-hdpi/ic_action_remove.png | Bin 0 -> 438 bytes .../main/res/drawable-land-hdpi/screen.png | Bin 0 -> 35031 bytes .../main/res/drawable-land-ldpi/screen.png | Bin 0 -> 13297 bytes .../main/res/drawable-land-mdpi/screen.png | Bin 0 -> 20237 bytes .../main/res/drawable-land-xhdpi/screen.png | Bin 0 -> 61863 bytes .../main/res/drawable-land-xxhdpi/screen.png | Bin 0 -> 79257 bytes .../main/res/drawable-land-xxxhdpi/screen.png | Bin 0 -> 101829 bytes .../res/drawable-mdpi/ic_action_next_item.png | Bin 0 -> 427 bytes .../drawable-mdpi/ic_action_previous_item.png | Bin 0 -> 438 bytes .../res/drawable-mdpi/ic_action_remove.png | Bin 0 -> 328 bytes .../main/res/drawable-port-hdpi/screen.png | Bin 0 -> 35236 bytes .../main/res/drawable-port-ldpi/screen.png | Bin 0 -> 13345 bytes .../main/res/drawable-port-mdpi/screen.png | Bin 0 -> 20274 bytes .../main/res/drawable-port-xhdpi/screen.png | Bin 0 -> 61157 bytes .../main/res/drawable-port-xxhdpi/screen.png | Bin 0 -> 77572 bytes .../main/res/drawable-port-xxxhdpi/screen.png | Bin 0 -> 100982 bytes .../drawable-xhdpi/ic_action_next_item.png | Bin 0 -> 727 bytes .../ic_action_previous_item.png | Bin 0 -> 744 bytes .../res/drawable-xhdpi/ic_action_remove.png | Bin 0 -> 536 bytes .../drawable-xxhdpi/ic_action_next_item.png | Bin 0 -> 1021 bytes .../ic_action_previous_item.png | Bin 0 -> 1038 bytes .../res/drawable-xxhdpi/ic_action_remove.png | Bin 0 -> 681 bytes .../app/src/main/res/mipmap-hdpi/icon.png | Bin 0 -> 5483 bytes .../app/src/main/res/mipmap-ldpi/icon.png | Bin 0 -> 1427 bytes .../app/src/main/res/mipmap-mdpi/icon.png | Bin 0 -> 1851 bytes .../app/src/main/res/mipmap-xhdpi/icon.png | Bin 0 -> 7468 bytes .../app/src/main/res/mipmap-xxhdpi/icon.png | Bin 0 -> 11620 bytes .../app/src/main/res/mipmap-xxxhdpi/icon.png | Bin 0 -> 15704 bytes .../app/src/main/res/values/strings.xml | 6 + .../android/app/src/main/res/xml/config.xml | 90 + .../app/src/main/res/xml/provider_paths.xml | 3 + platforms/android/build.gradle | 54 + ...dova-android-support-gradle-release.gradle | 31 + .../properties.gradle | 1 + platforms/android/cordova/Api.js | 411 ++ platforms/android/cordova/android_sdk_version | 29 + .../android/cordova/android_sdk_version.bat | 26 + platforms/android/cordova/build | 50 + platforms/android/cordova/build.bat | 26 + platforms/android/cordova/check_reqs | 31 + platforms/android/cordova/check_reqs.bat | 26 + platforms/android/cordova/clean | 51 + platforms/android/cordova/clean.bat | 26 + platforms/android/cordova/defaults.xml | 26 + platforms/android/cordova/lib/Adb.js | 101 + .../android/cordova/lib/AndroidManifest.js | 160 + .../android/cordova/lib/AndroidProject.js | 209 + .../android/cordova/lib/AndroidStudio.js | 42 + platforms/android/cordova/lib/android_sdk.js | 102 + platforms/android/cordova/lib/build.js | 294 ++ .../cordova/lib/builders/GenericBuilder.js | 124 + .../cordova/lib/builders/GradleBuilder.js | 330 ++ .../cordova/lib/builders/StudioBuilder.js | 302 ++ .../android/cordova/lib/builders/builders.js | 46 + platforms/android/cordova/lib/check_reqs.js | 432 ++ platforms/android/cordova/lib/device.js | 112 + platforms/android/cordova/lib/emulator.js | 533 +++ platforms/android/cordova/lib/getASPath.bat | 3 + platforms/android/cordova/lib/install-device | 42 + .../android/cordova/lib/install-device.bat | 26 + .../android/cordova/lib/install-emulator | 38 + .../android/cordova/lib/install-emulator.bat | 26 + platforms/android/cordova/lib/list-devices | 34 + .../android/cordova/lib/list-devices.bat | 26 + .../android/cordova/lib/list-emulator-images | 34 + .../cordova/lib/list-emulator-images.bat | 26 + .../cordova/lib/list-started-emulators | 34 + .../cordova/lib/list-started-emulators.bat | 26 + platforms/android/cordova/lib/log.js | 56 + .../android/cordova/lib/plugin-build.gradle | 72 + .../android/cordova/lib/pluginHandlers.js | 320 ++ platforms/android/cordova/lib/prepare.js | 480 ++ platforms/android/cordova/lib/retry.js | 68 + platforms/android/cordova/lib/run.js | 132 + platforms/android/cordova/lib/start-emulator | 39 + .../android/cordova/lib/start-emulator.bat | 26 + platforms/android/cordova/log | 36 + platforms/android/cordova/log.bat | 26 + platforms/android/cordova/loggingHelper.js | 18 + platforms/android/cordova/run | 53 + platforms/android/cordova/run.bat | 26 + platforms/android/cordova/version | 29 + platforms/android/cordova/version.bat | 26 + .../android/nativeapiprovider.js | 36 + .../android/promptbasednativeapi.js | 35 + .../platform_www/cordova-js-src/exec.js | 297 ++ .../platform_www/cordova-js-src/platform.js | 125 + .../cordova-js-src/plugin/android/app.js | 108 + platforms/android/platform_www/cordova.js | 2188 +++++++++ .../android/platform_www/cordova_plugins.js | 286 ++ platforms/android/project.properties | 17 + platforms/android/settings.gradle | 4 + platforms/android/wrapper.gradle | 1 + plugins/android.json | 55 + .../LICENSE | 21 + .../README.md | 271 ++ .../package.json | 55 + .../plugin.xml | 44 + .../src/android/IntentShim.java | 794 ++++ .../src/android/res/xml/provider_paths.xml | 3 + .../README.md | 172 + ...dova-android-support-gradle-release.gradle | 31 + .../package.json | 39 + .../plugin.xml | 26 + .../properties.gradle | 1 + .../scripts/apply-changes.js | 90 + .../cordova-plugin-console/CONTRIBUTING.md | 37 + plugins/cordova-plugin-console/LICENSE | 202 + plugins/cordova-plugin-console/NOTICE | 5 + plugins/cordova-plugin-console/README.md | 116 + .../cordova-plugin-console/RELEASENOTES.md | 126 + .../cordova-plugin-console/doc/de/README.md | 43 + .../cordova-plugin-console/doc/de/index.md | 41 + .../cordova-plugin-console/doc/es/README.md | 41 + .../cordova-plugin-console/doc/es/index.md | 39 + .../cordova-plugin-console/doc/fr/README.md | 41 + .../cordova-plugin-console/doc/fr/index.md | 39 + .../cordova-plugin-console/doc/it/README.md | 43 + .../cordova-plugin-console/doc/it/index.md | 41 + .../cordova-plugin-console/doc/ja/README.md | 43 + .../cordova-plugin-console/doc/ja/index.md | 41 + .../cordova-plugin-console/doc/ko/README.md | 43 + .../cordova-plugin-console/doc/ko/index.md | 41 + .../cordova-plugin-console/doc/pl/README.md | 43 + .../cordova-plugin-console/doc/pl/index.md | 41 + .../cordova-plugin-console/doc/ru/index.md | 31 + .../cordova-plugin-console/doc/zh/README.md | 43 + .../cordova-plugin-console/doc/zh/index.md | 41 + plugins/cordova-plugin-console/package.json | 80 + plugins/cordova-plugin-console/plugin.xml | 132 + .../src/ios/CDVLogger.h | 26 + .../src/ios/CDVLogger.m | 38 + .../src/ubuntu/console.cpp | 29 + .../src/ubuntu/console.h | 43 + .../src/wp/DebugConsole.cs | 47 + .../cordova-plugin-console/tests/package.json | 14 + .../cordova-plugin-console/tests/plugin.xml | 31 + plugins/cordova-plugin-console/tests/tests.js | 43 + plugins/cordova-plugin-device/CONTRIBUTING.md | 37 + plugins/cordova-plugin-device/LICENSE | 202 + plugins/cordova-plugin-device/NOTICE | 5 + plugins/cordova-plugin-device/README.md | 267 ++ plugins/cordova-plugin-device/RELEASENOTES.md | 181 + .../cordova-plugin-device/doc/de/README.md | 203 + plugins/cordova-plugin-device/doc/de/index.md | 206 + .../cordova-plugin-device/doc/es/README.md | 216 + plugins/cordova-plugin-device/doc/es/index.md | 220 + .../cordova-plugin-device/doc/fr/README.md | 215 + plugins/cordova-plugin-device/doc/fr/index.md | 218 + .../cordova-plugin-device/doc/it/README.md | 203 + plugins/cordova-plugin-device/doc/it/index.md | 206 + .../cordova-plugin-device/doc/ja/README.md | 203 + plugins/cordova-plugin-device/doc/ja/index.md | 206 + .../cordova-plugin-device/doc/ko/README.md | 203 + plugins/cordova-plugin-device/doc/ko/index.md | 206 + .../cordova-plugin-device/doc/pl/README.md | 214 + plugins/cordova-plugin-device/doc/pl/index.md | 206 + plugins/cordova-plugin-device/doc/ru/index.md | 219 + .../cordova-plugin-device/doc/zh/README.md | 203 + plugins/cordova-plugin-device/doc/zh/index.md | 206 + plugins/cordova-plugin-device/package.json | 84 + plugins/cordova-plugin-device/plugin.xml | 93 + .../src/android/Device.java | 174 + .../src/browser/DeviceProxy.js | 84 + .../cordova-plugin-device/src/ios/CDVDevice.h | 30 + .../cordova-plugin-device/src/ios/CDVDevice.m | 112 + .../cordova-plugin-device/src/osx/CDVDevice.h | 28 + .../cordova-plugin-device/src/osx/CDVDevice.m | 113 + .../src/windows/DeviceProxy.js | 96 + .../cordova-plugin-device/tests/package.json | 14 + .../cordova-plugin-device/tests/plugin.xml | 31 + plugins/cordova-plugin-device/tests/tests.js | 113 + .../cordova-plugin-device/types/index.d.ts | 36 + .../CONTRIBUTING.md | 37 + plugins/cordova-plugin-file-transfer/LICENSE | 202 + plugins/cordova-plugin-file-transfer/NOTICE | 8 + .../cordova-plugin-file-transfer/README.md | 598 +++ .../RELEASENOTES.md | 319 ++ .../doc/de/README.md | 311 ++ .../doc/de/index.md | 302 ++ .../doc/es/README.md | 311 ++ .../doc/es/index.md | 262 ++ .../doc/fr/README.md | 270 ++ .../doc/fr/index.md | 261 ++ .../doc/it/README.md | 311 ++ .../doc/it/index.md | 302 ++ .../doc/ja/README.md | 311 ++ .../doc/ja/index.md | 302 ++ .../doc/ko/README.md | 311 ++ .../doc/ko/index.md | 302 ++ .../doc/pl/README.md | 311 ++ .../doc/pl/index.md | 302 ++ .../doc/ru/index.md | 290 ++ .../doc/zh/README.md | 311 ++ .../doc/zh/index.md | 302 ++ .../cordova-plugin-file-transfer/package.json | 93 + .../cordova-plugin-file-transfer/plugin.xml | 170 + .../src/amazon/FileTransfer.java | 898 ++++ .../src/android/FileProgressResult.java | 63 + .../src/android/FileTransfer.java | 932 ++++ .../src/android/FileUploadResult.java | 73 + .../src/ios/CDVFileTransfer.h | 89 + .../src/ios/CDVFileTransfer.m | 861 ++++ .../src/ubuntu/file-transfer.cpp | 265 ++ .../src/ubuntu/file-transfer.h | 103 + .../src/windows/FileTransferProxy.js | 579 +++ .../src/wp/FileTransfer.cs | 994 ++++ .../tests/package.json | 14 + .../tests/plugin.xml | 34 + .../tests/tests.js | 1739 +++++++ .../types/index.d.ts | 136 + plugins/cordova-plugin-file/CONTRIBUTING.md | 37 + plugins/cordova-plugin-file/LICENSE | 202 + plugins/cordova-plugin-file/NOTICE | 5 + plugins/cordova-plugin-file/README.md | 835 ++++ plugins/cordova-plugin-file/RELEASENOTES.md | 3 + plugins/cordova-plugin-file/doc/de/README.md | 335 ++ plugins/cordova-plugin-file/doc/de/index.md | 338 ++ plugins/cordova-plugin-file/doc/de/plugins.md | 101 + plugins/cordova-plugin-file/doc/es/README.md | 335 ++ plugins/cordova-plugin-file/doc/es/index.md | 336 ++ plugins/cordova-plugin-file/doc/es/plugins.md | 101 + plugins/cordova-plugin-file/doc/fr/README.md | 328 ++ plugins/cordova-plugin-file/doc/fr/index.md | 331 ++ plugins/cordova-plugin-file/doc/fr/plugins.md | 101 + plugins/cordova-plugin-file/doc/it/README.md | 335 ++ plugins/cordova-plugin-file/doc/it/index.md | 338 ++ plugins/cordova-plugin-file/doc/it/plugins.md | 101 + plugins/cordova-plugin-file/doc/ja/README.md | 335 ++ plugins/cordova-plugin-file/doc/ja/index.md | 338 ++ plugins/cordova-plugin-file/doc/ja/plugins.md | 101 + plugins/cordova-plugin-file/doc/ko/README.md | 335 ++ plugins/cordova-plugin-file/doc/ko/index.md | 338 ++ plugins/cordova-plugin-file/doc/ko/plugins.md | 101 + plugins/cordova-plugin-file/doc/pl/README.md | 335 ++ plugins/cordova-plugin-file/doc/pl/index.md | 338 ++ plugins/cordova-plugin-file/doc/pl/plugins.md | 101 + plugins/cordova-plugin-file/doc/plugins.md | 120 + plugins/cordova-plugin-file/doc/ru/index.md | 275 ++ plugins/cordova-plugin-file/doc/ru/plugins.md | 124 + plugins/cordova-plugin-file/doc/zh/README.md | 335 ++ plugins/cordova-plugin-file/doc/zh/index.md | 343 ++ plugins/cordova-plugin-file/doc/zh/plugins.md | 101 + plugins/cordova-plugin-file/package.json | 87 + plugins/cordova-plugin-file/plugin.xml | 260 + .../src/android/AssetFilesystem.java | 294 ++ .../src/android/ContentFilesystem.java | 223 + .../src/android/DirectoryManager.java | 134 + .../src/android/EncodingException.java | 29 + .../src/android/FileExistsException.java | 29 + .../src/android/FileUtils.java | 1225 +++++ .../src/android/Filesystem.java | 331 ++ .../android/InvalidModificationException.java | 30 + .../src/android/LocalFilesystem.java | 513 ++ .../src/android/LocalFilesystemURL.java | 64 + .../NoModificationAllowedException.java | 29 + .../src/android/PendingRequests.java | 94 + .../src/android/TypeMismatchException.java | 30 + .../src/android/build-extras.gradle | 47 + .../src/browser/FileProxy.js | 984 ++++ .../src/ios/CDVAssetLibraryFilesystem.h | 30 + .../src/ios/CDVAssetLibraryFilesystem.m | 253 + plugins/cordova-plugin-file/src/ios/CDVFile.h | 157 + plugins/cordova-plugin-file/src/ios/CDVFile.m | 1119 +++++ .../src/ios/CDVLocalFilesystem.h | 32 + .../src/ios/CDVLocalFilesystem.m | 750 +++ plugins/cordova-plugin-file/src/osx/CDVFile.h | 189 + plugins/cordova-plugin-file/src/osx/CDVFile.m | 1056 +++++ .../src/osx/CDVLocalFilesystem.h | 32 + .../src/osx/CDVLocalFilesystem.m | 733 +++ .../src/windows/FileProxy.js | 1190 +++++ .../cordova-plugin-file/tests/package.json | 17 + plugins/cordova-plugin-file/tests/plugin.xml | 44 + .../src/android/TestContentProvider.java | 93 + plugins/cordova-plugin-file/tests/tests.js | 4163 +++++++++++++++++ plugins/cordova-plugin-file/types/index.d.ts | 378 ++ .../cordova-plugin-filechooser/LICENSE.txt | 13 + plugins/cordova-plugin-filechooser/README.md | 28 + .../filechooser.png | Bin 0 -> 38968 bytes .../cordova-plugin-filechooser/package.json | 45 + plugins/cordova-plugin-filechooser/plugin.xml | 30 + .../src/android/FileChooser.java | 82 + plugins/cordova-plugin-filepath/CHANGELOG.md | 18 + plugins/cordova-plugin-filepath/LICENSE.md | 202 + plugins/cordova-plugin-filepath/README.md | 44 + plugins/cordova-plugin-filepath/package.json | 56 + plugins/cordova-plugin-filepath/plugin.xml | 58 + .../src/android/FilePath.java | 431 ++ .../CONTRIBUTING.md | 64 + plugins/cordova-plugin-inappbrowser/LICENSE | 202 + plugins/cordova-plugin-inappbrowser/NOTICE | 5 + plugins/cordova-plugin-inappbrowser/README.md | 676 +++ .../RELEASENOTES.md | 677 +++ .../doc/de/README.md | 388 ++ .../doc/de/index.md | 357 ++ .../doc/es/README.md | 388 ++ .../doc/es/index.md | 357 ++ .../doc/fr/README.md | 388 ++ .../doc/fr/index.md | 357 ++ .../doc/it/README.md | 388 ++ .../doc/it/index.md | 357 ++ .../doc/ja/README.md | 388 ++ .../doc/ja/index.md | 357 ++ .../doc/ko/README.md | 388 ++ .../doc/ko/index.md | 357 ++ .../doc/pl/README.md | 388 ++ .../doc/pl/index.md | 357 ++ .../doc/ru/index.md | 330 ++ .../doc/zh/README.md | 388 ++ .../doc/zh/index.md | 357 ++ .../cordova-plugin-inappbrowser/package.json | 90 + .../cordova-plugin-inappbrowser/plugin.xml | 126 + .../src/android/InAppBrowser.java | 1259 +++++ .../src/android/InAppBrowserDialog.java | 57 + .../src/android/InAppChromeClient.java | 133 + .../res/drawable-hdpi/ic_action_next_item.png | Bin 0 -> 593 bytes .../drawable-hdpi/ic_action_previous_item.png | Bin 0 -> 599 bytes .../res/drawable-hdpi/ic_action_remove.png | Bin 0 -> 438 bytes .../res/drawable-mdpi/ic_action_next_item.png | Bin 0 -> 427 bytes .../drawable-mdpi/ic_action_previous_item.png | Bin 0 -> 438 bytes .../res/drawable-mdpi/ic_action_remove.png | Bin 0 -> 328 bytes .../drawable-xhdpi/ic_action_next_item.png | Bin 0 -> 727 bytes .../ic_action_previous_item.png | Bin 0 -> 744 bytes .../res/drawable-xhdpi/ic_action_remove.png | Bin 0 -> 536 bytes .../drawable-xxhdpi/ic_action_next_item.png | Bin 0 -> 1021 bytes .../ic_action_previous_item.png | Bin 0 -> 1038 bytes .../res/drawable-xxhdpi/ic_action_remove.png | Bin 0 -> 681 bytes .../src/browser/InAppBrowserProxy.js | 247 + .../src/ios/CDVInAppBrowser.h | 118 + .../src/ios/CDVInAppBrowser.m | 1144 +++++ .../src/osx/CDVInAppBrowser.h | 30 + .../src/osx/CDVInAppBrowser.m | 89 + .../src/windows/InAppBrowserProxy.js | 385 ++ .../tests/.eslintrc.yml | 2 + .../tests/package.json | 14 + .../tests/plugin.xml | 33 + .../tests/resources/inject.css | 21 + .../tests/resources/inject.html | 44 + .../tests/resources/inject.js | 20 + .../tests/resources/local.html | 67 + .../tests/resources/local.pdf | Bin 0 -> 8568 bytes .../tests/resources/video.html | 45 + .../tests/tests.js | 696 +++ .../types/index.d.ts | 221 + .../cordova-plugin-share-content/README.rst | 21 + .../cordova-plugin-share-content/package.json | 64 + .../cordova-plugin-share-content/plugin.xml | 43 + .../scripts/add_swift_support.js | 173 + .../src/android/ShareContentPlugin.java | 62 + .../ios/ShareContentPlugin-Bridging-Header.h | 6 + .../src/ios/ShareContentPlugin.swift | 25 + .../CONTRIBUTING.md | 37 + plugins/cordova-plugin-splashscreen/LICENSE | 202 + plugins/cordova-plugin-splashscreen/NOTICE | 5 + plugins/cordova-plugin-splashscreen/README.md | 522 +++ .../RELEASENOTES.md | 238 + .../doc/de/README.md | 119 + .../doc/de/index.md | 78 + .../doc/es/README.md | 119 + .../doc/es/index.md | 76 + .../doc/fr/README.md | 119 + .../doc/fr/index.md | 78 + .../doc/it/README.md | 119 + .../doc/it/index.md | 78 + .../doc/ja/README.md | 119 + .../doc/ja/index.md | 78 + .../doc/ko/README.md | 119 + .../doc/ko/index.md | 78 + .../doc/pl/README.md | 119 + .../doc/pl/index.md | 78 + .../doc/ru/index.md | 75 + .../doc/zh/README.md | 119 + .../doc/zh/index.md | 78 + .../cordova-plugin-splashscreen/package.json | 83 + .../cordova-plugin-splashscreen/plugin.xml | 82 + .../src/android/SplashScreen.java | 413 ++ .../src/browser/SplashScreenProxy.js | 170 + .../src/ios/CDVSplashScreen.h | 46 + .../src/ios/CDVSplashScreen.m | 514 ++ .../src/ios/CDVViewController+SplashScreen.h | 28 + .../src/ios/CDVViewController+SplashScreen.m | 89 + .../src/wp/ResolutionHelper.cs | 39 + .../src/wp/SplashScreen.cs | 255 + .../contents.xcworkspacedata | 7 + .../CDVSplashScreenTest.xccheckout | 41 + .../xcschemes/CordovaLib.xcscheme | 77 + .../tests/ios/CDVSplashScreenTest/.npmignore | 1 + .../CDVSplashScreenLibTests/ImageNameTest.m | 702 +++ .../ImageNameTestDelegates.h | 57 + .../ImageNameTestDelegates.m | 200 + .../CDVSplashScreenLibTests/Info.plist | 44 + .../project.pbxproj | 505 ++ .../contents.xcworkspacedata | 7 + .../CDVSplashScreenTest.xccheckout | 41 + .../xcschemes/CDVSplashScreenLib.xcscheme | 77 + .../CDVSplashScreenLibTests.xcscheme | 96 + .../tests/ios/README.md | 40 + .../tests/ios/doc/de/README.md | 39 + .../tests/ios/doc/es/README.md | 39 + .../tests/ios/doc/fr/README.md | 39 + .../tests/ios/doc/it/README.md | 39 + .../tests/ios/doc/ja/README.md | 39 + .../tests/ios/doc/ko/README.md | 39 + .../tests/ios/doc/pl/README.md | 39 + .../tests/ios/doc/zh/README.md | 39 + .../tests/ios/package.json | 13 + .../tests/package.json | 14 + .../tests/plugin.xml | 29 + .../tests/tests.js | 64 + .../types/index.d.ts | 17 + .../cordova-plugin-statusbar/CONTRIBUTING.md | 37 + plugins/cordova-plugin-statusbar/LICENSE | 202 + plugins/cordova-plugin-statusbar/NOTICE | 5 + plugins/cordova-plugin-statusbar/README.md | 332 ++ .../cordova-plugin-statusbar/RELEASENOTES.md | 172 + .../cordova-plugin-statusbar/doc/de/README.md | 276 ++ .../cordova-plugin-statusbar/doc/de/index.md | 262 ++ .../cordova-plugin-statusbar/doc/es/README.md | 276 ++ .../cordova-plugin-statusbar/doc/es/index.md | 252 + .../cordova-plugin-statusbar/doc/fr/README.md | 276 ++ .../cordova-plugin-statusbar/doc/fr/index.md | 262 ++ .../cordova-plugin-statusbar/doc/it/README.md | 276 ++ .../cordova-plugin-statusbar/doc/it/index.md | 262 ++ .../cordova-plugin-statusbar/doc/ja/README.md | 276 ++ .../cordova-plugin-statusbar/doc/ja/index.md | 262 ++ .../cordova-plugin-statusbar/doc/ko/README.md | 276 ++ .../cordova-plugin-statusbar/doc/ko/index.md | 262 ++ .../cordova-plugin-statusbar/doc/pl/README.md | 276 ++ .../cordova-plugin-statusbar/doc/pl/index.md | 262 ++ .../cordova-plugin-statusbar/doc/ru/index.md | 238 + .../cordova-plugin-statusbar/doc/zh/README.md | 276 ++ .../cordova-plugin-statusbar/doc/zh/index.md | 262 ++ plugins/cordova-plugin-statusbar/package.json | 81 + plugins/cordova-plugin-statusbar/plugin.xml | 99 + .../src/android/StatusBar.java | 276 ++ .../src/browser/StatusBarProxy.js | 50 + .../src/ios/CDVStatusBar.h | 50 + .../src/ios/CDVStatusBar.m | 479 ++ .../src/windows/StatusBarProxy.js | 114 + .../src/wp/StatusBar.cs | 141 + .../tests/package.json | 14 + .../cordova-plugin-statusbar/tests/plugin.xml | 31 + .../cordova-plugin-statusbar/tests/tests.js | 151 + .../cordova-plugin-statusbar/types/index.d.ts | 77 + .../cordova-plugin-whitelist/CONTRIBUTING.md | 37 + plugins/cordova-plugin-whitelist/LICENSE | 202 + plugins/cordova-plugin-whitelist/NOTICE | 5 + plugins/cordova-plugin-whitelist/README.md | 163 + .../cordova-plugin-whitelist/RELEASENOTES.md | 75 + .../cordova-plugin-whitelist/doc/de/README.md | 148 + .../cordova-plugin-whitelist/doc/es/README.md | 148 + .../cordova-plugin-whitelist/doc/fr/README.md | 148 + .../cordova-plugin-whitelist/doc/it/README.md | 148 + .../cordova-plugin-whitelist/doc/ja/README.md | 148 + .../cordova-plugin-whitelist/doc/ko/README.md | 148 + .../cordova-plugin-whitelist/doc/pl/README.md | 148 + .../cordova-plugin-whitelist/doc/zh/README.md | 148 + plugins/cordova-plugin-whitelist/package.json | 64 + plugins/cordova-plugin-whitelist/plugin.xml | 48 + .../src/android/WhitelistPlugin.java | 161 + plugins/fetch.json | 116 + plugins/ionic-plugin-keyboard/LICENSE | 202 + plugins/ionic-plugin-keyboard/README.md | 128 + plugins/ionic-plugin-keyboard/package.json | 65 + plugins/ionic-plugin-keyboard/plugin.xml | 71 + .../src/android/IonicKeyboard.java | 130 + .../src/blackberry10/index.js | 125 + .../src/blackberry10/native/.cproject | 222 + .../src/blackberry10/native/.project | 76 + .../blackberry10/native/device/libKeyboard.so | Bin 0 -> 289588 bytes .../native/device/public/json_reader.o | Bin 0 -> 81556 bytes .../native/device/public/json_value.o | Bin 0 -> 91652 bytes .../native/device/public/json_writer.o | Bin 0 -> 133040 bytes .../native/device/public/plugin.o | Bin 0 -> 50576 bytes .../native/device/public/tokenizer.o | Bin 0 -> 23640 bytes .../native/device/src/CallKeyboard.o | Bin 0 -> 5988 bytes .../blackberry10/native/device/src/Logger.o | Bin 0 -> 5192 bytes .../native/device/src/keyboard_js.o | Bin 0 -> 17352 bytes .../native/device/src/keyboard_ndk.o | Bin 0 -> 95672 bytes .../native/public/json/autolink.h | 19 + .../blackberry10/native/public/json/config.h | 43 + .../native/public/json/features.h | 42 + .../native/public/json/forwards.h | 39 + .../blackberry10/native/public/json/json.h | 10 + .../blackberry10/native/public/json/reader.h | 196 + .../blackberry10/native/public/json/value.h | 1069 +++++ .../blackberry10/native/public/json/writer.h | 174 + .../native/public/json_batchallocator.h | 125 + .../native/public/json_internalarray.inl | 448 ++ .../native/public/json_internalmap.inl | 607 +++ .../native/public/json_reader.cpp | 892 ++++ .../blackberry10/native/public/json_value.cpp | 1726 +++++++ .../native/public/json_valueiterator.inl | 292 ++ .../native/public/json_writer.cpp | 829 ++++ .../src/blackberry10/native/public/plugin.cpp | 320 ++ .../src/blackberry10/native/public/plugin.h | 70 + .../blackberry10/native/public/tokenizer.cpp | 222 + .../blackberry10/native/public/tokenizer.h | 55 + .../native/simulator/libKeyboard.so | Bin 0 -> 1455634 bytes .../native/simulator/public/json_reader.o | Bin 0 -> 389836 bytes .../native/simulator/public/json_value.o | Bin 0 -> 526036 bytes .../native/simulator/public/json_writer.o | Bin 0 -> 471916 bytes .../native/simulator/public/plugin.o | Bin 0 -> 400112 bytes .../native/simulator/public/tokenizer.o | Bin 0 -> 145048 bytes .../native/simulator/src/CallKeyboard.o | Bin 0 -> 75616 bytes .../native/simulator/src/Logger.o | Bin 0 -> 55676 bytes .../native/simulator/src/keyboard_js.o | Bin 0 -> 95696 bytes .../native/simulator/src/keyboard_ndk.o | Bin 0 -> 373764 bytes .../src/blackberry10/native/src/Logger.cpp | 104 + .../src/blackberry10/native/src/Logger.hpp | 49 + .../blackberry10/native/src/keyboard_js.cpp | 117 + .../blackberry10/native/src/keyboard_js.hpp | 42 + .../blackberry10/native/src/keyboard_ndk.cpp | 155 + .../blackberry10/native/src/keyboard_ndk.hpp | 71 + .../src/ios/IonicKeyboard.h | 16 + .../src/ios/IonicKeyboard.m | 176 + .../src/windows/KeyboardProxy.js | 37 + 600 files changed, 108263 insertions(+), 11 deletions(-) create mode 100644 platforms/android/.gitignore create mode 100755 platforms/android/CordovaLib/AndroidManifest.xml create mode 100644 platforms/android/CordovaLib/build.gradle create mode 100644 platforms/android/CordovaLib/cordova.gradle create mode 100644 platforms/android/CordovaLib/project.properties create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/BuildHelper.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/Config.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaPlugin.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaPreferences.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaResourceApi.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebView.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewEngine.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewImpl.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/CoreAndroid.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/ExposedJsApi.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/ICordovaClientCertRequest.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/ICordovaCookieManager.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/ICordovaHttpAuthHandler.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/LOG.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/NativeToJsMessageQueue.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/PermissionHelper.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/PluginEntry.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/PluginManager.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/PluginResult.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/ResumeCallback.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/Whitelist.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemCookieManager.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemExposedJsApi.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemWebChromeClient.java create mode 100644 platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemWebView.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemWebViewClient.java create mode 100755 platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemWebViewEngine.java create mode 100644 platforms/android/android.json create mode 100644 platforms/android/app/build.gradle create mode 100644 platforms/android/app/cordova-android-support-gradle-release/properties.gradle create mode 100644 platforms/android/app/src/main/AndroidManifest.xml create mode 100644 platforms/android/app/src/main/java/com/darryncampbell/plugin/intent/IntentShim.java create mode 100644 platforms/android/app/src/main/java/com/ferdinandsilva/android/ShareContentPlugin.java create mode 100644 platforms/android/app/src/main/java/com/hiddentao/cordova/filepath/FilePath.java create mode 100644 platforms/android/app/src/main/java/com/megster/cordova/FileChooser.java create mode 100644 platforms/android/app/src/main/java/com/monkeystew/goober_m/MainActivity.java create mode 100644 platforms/android/app/src/main/java/io/ionic/keyboard/IonicKeyboard.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/device/Device.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/AssetFilesystem.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/ContentFilesystem.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/DirectoryManager.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/EncodingException.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/FileExistsException.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/FileUtils.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/Filesystem.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/InvalidModificationException.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/LocalFilesystem.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/LocalFilesystemURL.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/NoModificationAllowedException.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/PendingRequests.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/file/TypeMismatchException.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/filetransfer/FileProgressResult.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/filetransfer/FileTransfer.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/filetransfer/FileUploadResult.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/inappbrowser/InAppBrowser.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/inappbrowser/InAppBrowserDialog.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/inappbrowser/InAppChromeClient.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/splashscreen/SplashScreen.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/statusbar/StatusBar.java create mode 100644 platforms/android/app/src/main/java/org/apache/cordova/whitelist/WhitelistPlugin.java create mode 100644 platforms/android/app/src/main/res/drawable-hdpi/ic_action_next_item.png create mode 100644 platforms/android/app/src/main/res/drawable-hdpi/ic_action_previous_item.png create mode 100644 platforms/android/app/src/main/res/drawable-hdpi/ic_action_remove.png create mode 100644 platforms/android/app/src/main/res/drawable-land-hdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-land-ldpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-land-mdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-land-xhdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-land-xxhdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-land-xxxhdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-mdpi/ic_action_next_item.png create mode 100644 platforms/android/app/src/main/res/drawable-mdpi/ic_action_previous_item.png create mode 100644 platforms/android/app/src/main/res/drawable-mdpi/ic_action_remove.png create mode 100644 platforms/android/app/src/main/res/drawable-port-hdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-port-ldpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-port-mdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-port-xhdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-port-xxhdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-port-xxxhdpi/screen.png create mode 100644 platforms/android/app/src/main/res/drawable-xhdpi/ic_action_next_item.png create mode 100644 platforms/android/app/src/main/res/drawable-xhdpi/ic_action_previous_item.png create mode 100644 platforms/android/app/src/main/res/drawable-xhdpi/ic_action_remove.png create mode 100644 platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_next_item.png create mode 100644 platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_previous_item.png create mode 100644 platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_remove.png create mode 100644 platforms/android/app/src/main/res/mipmap-hdpi/icon.png create mode 100644 platforms/android/app/src/main/res/mipmap-ldpi/icon.png create mode 100644 platforms/android/app/src/main/res/mipmap-mdpi/icon.png create mode 100644 platforms/android/app/src/main/res/mipmap-xhdpi/icon.png create mode 100644 platforms/android/app/src/main/res/mipmap-xxhdpi/icon.png create mode 100644 platforms/android/app/src/main/res/mipmap-xxxhdpi/icon.png create mode 100644 platforms/android/app/src/main/res/values/strings.xml create mode 100644 platforms/android/app/src/main/res/xml/config.xml create mode 100644 platforms/android/app/src/main/res/xml/provider_paths.xml create mode 100644 platforms/android/build.gradle create mode 100644 platforms/android/cordova-android-support-gradle-release/goober_m-cordova-android-support-gradle-release.gradle create mode 100644 platforms/android/cordova-android-support-gradle-release/properties.gradle create mode 100644 platforms/android/cordova/Api.js create mode 100755 platforms/android/cordova/android_sdk_version create mode 100644 platforms/android/cordova/android_sdk_version.bat create mode 100755 platforms/android/cordova/build create mode 100644 platforms/android/cordova/build.bat create mode 100755 platforms/android/cordova/check_reqs create mode 100644 platforms/android/cordova/check_reqs.bat create mode 100755 platforms/android/cordova/clean create mode 100644 platforms/android/cordova/clean.bat create mode 100644 platforms/android/cordova/defaults.xml create mode 100644 platforms/android/cordova/lib/Adb.js create mode 100644 platforms/android/cordova/lib/AndroidManifest.js create mode 100644 platforms/android/cordova/lib/AndroidProject.js create mode 100644 platforms/android/cordova/lib/AndroidStudio.js create mode 100755 platforms/android/cordova/lib/android_sdk.js create mode 100644 platforms/android/cordova/lib/build.js create mode 100644 platforms/android/cordova/lib/builders/GenericBuilder.js create mode 100644 platforms/android/cordova/lib/builders/GradleBuilder.js create mode 100644 platforms/android/cordova/lib/builders/StudioBuilder.js create mode 100644 platforms/android/cordova/lib/builders/builders.js create mode 100644 platforms/android/cordova/lib/check_reqs.js create mode 100644 platforms/android/cordova/lib/device.js create mode 100644 platforms/android/cordova/lib/emulator.js create mode 100644 platforms/android/cordova/lib/getASPath.bat create mode 100755 platforms/android/cordova/lib/install-device create mode 100644 platforms/android/cordova/lib/install-device.bat create mode 100755 platforms/android/cordova/lib/install-emulator create mode 100644 platforms/android/cordova/lib/install-emulator.bat create mode 100755 platforms/android/cordova/lib/list-devices create mode 100644 platforms/android/cordova/lib/list-devices.bat create mode 100755 platforms/android/cordova/lib/list-emulator-images create mode 100644 platforms/android/cordova/lib/list-emulator-images.bat create mode 100755 platforms/android/cordova/lib/list-started-emulators create mode 100644 platforms/android/cordova/lib/list-started-emulators.bat create mode 100644 platforms/android/cordova/lib/log.js create mode 100644 platforms/android/cordova/lib/plugin-build.gradle create mode 100644 platforms/android/cordova/lib/pluginHandlers.js create mode 100644 platforms/android/cordova/lib/prepare.js create mode 100644 platforms/android/cordova/lib/retry.js create mode 100644 platforms/android/cordova/lib/run.js create mode 100755 platforms/android/cordova/lib/start-emulator create mode 100644 platforms/android/cordova/lib/start-emulator.bat create mode 100755 platforms/android/cordova/log create mode 100644 platforms/android/cordova/log.bat create mode 100644 platforms/android/cordova/loggingHelper.js create mode 100755 platforms/android/cordova/run create mode 100644 platforms/android/cordova/run.bat create mode 100755 platforms/android/cordova/version create mode 100644 platforms/android/cordova/version.bat create mode 100644 platforms/android/platform_www/cordova-js-src/android/nativeapiprovider.js create mode 100644 platforms/android/platform_www/cordova-js-src/android/promptbasednativeapi.js create mode 100644 platforms/android/platform_www/cordova-js-src/exec.js create mode 100644 platforms/android/platform_www/cordova-js-src/platform.js create mode 100644 platforms/android/platform_www/cordova-js-src/plugin/android/app.js create mode 100644 platforms/android/platform_www/cordova.js create mode 100644 platforms/android/platform_www/cordova_plugins.js create mode 100644 platforms/android/project.properties create mode 100644 platforms/android/settings.gradle create mode 100644 platforms/android/wrapper.gradle create mode 100644 plugins/android.json create mode 100644 plugins/com-darryncampbell-cordova-plugin-intent/LICENSE create mode 100644 plugins/com-darryncampbell-cordova-plugin-intent/README.md create mode 100644 plugins/com-darryncampbell-cordova-plugin-intent/package.json create mode 100644 plugins/com-darryncampbell-cordova-plugin-intent/plugin.xml create mode 100644 plugins/com-darryncampbell-cordova-plugin-intent/src/android/IntentShim.java create mode 100644 plugins/com-darryncampbell-cordova-plugin-intent/src/android/res/xml/provider_paths.xml create mode 100644 plugins/cordova-android-support-gradle-release/README.md create mode 100644 plugins/cordova-android-support-gradle-release/cordova-android-support-gradle-release.gradle create mode 100644 plugins/cordova-android-support-gradle-release/package.json create mode 100644 plugins/cordova-android-support-gradle-release/plugin.xml create mode 100644 plugins/cordova-android-support-gradle-release/properties.gradle create mode 100644 plugins/cordova-android-support-gradle-release/scripts/apply-changes.js create mode 100644 plugins/cordova-plugin-console/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-console/LICENSE create mode 100644 plugins/cordova-plugin-console/NOTICE create mode 100644 plugins/cordova-plugin-console/README.md create mode 100644 plugins/cordova-plugin-console/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-console/doc/de/README.md create mode 100644 plugins/cordova-plugin-console/doc/de/index.md create mode 100644 plugins/cordova-plugin-console/doc/es/README.md create mode 100644 plugins/cordova-plugin-console/doc/es/index.md create mode 100644 plugins/cordova-plugin-console/doc/fr/README.md create mode 100644 plugins/cordova-plugin-console/doc/fr/index.md create mode 100644 plugins/cordova-plugin-console/doc/it/README.md create mode 100644 plugins/cordova-plugin-console/doc/it/index.md create mode 100644 plugins/cordova-plugin-console/doc/ja/README.md create mode 100644 plugins/cordova-plugin-console/doc/ja/index.md create mode 100644 plugins/cordova-plugin-console/doc/ko/README.md create mode 100644 plugins/cordova-plugin-console/doc/ko/index.md create mode 100644 plugins/cordova-plugin-console/doc/pl/README.md create mode 100644 plugins/cordova-plugin-console/doc/pl/index.md create mode 100644 plugins/cordova-plugin-console/doc/ru/index.md create mode 100644 plugins/cordova-plugin-console/doc/zh/README.md create mode 100644 plugins/cordova-plugin-console/doc/zh/index.md create mode 100644 plugins/cordova-plugin-console/package.json create mode 100644 plugins/cordova-plugin-console/plugin.xml create mode 100644 plugins/cordova-plugin-console/src/ios/CDVLogger.h create mode 100644 plugins/cordova-plugin-console/src/ios/CDVLogger.m create mode 100644 plugins/cordova-plugin-console/src/ubuntu/console.cpp create mode 100644 plugins/cordova-plugin-console/src/ubuntu/console.h create mode 100644 plugins/cordova-plugin-console/src/wp/DebugConsole.cs create mode 100644 plugins/cordova-plugin-console/tests/package.json create mode 100644 plugins/cordova-plugin-console/tests/plugin.xml create mode 100644 plugins/cordova-plugin-console/tests/tests.js create mode 100644 plugins/cordova-plugin-device/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-device/LICENSE create mode 100644 plugins/cordova-plugin-device/NOTICE create mode 100644 plugins/cordova-plugin-device/README.md create mode 100644 plugins/cordova-plugin-device/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-device/doc/de/README.md create mode 100644 plugins/cordova-plugin-device/doc/de/index.md create mode 100644 plugins/cordova-plugin-device/doc/es/README.md create mode 100644 plugins/cordova-plugin-device/doc/es/index.md create mode 100644 plugins/cordova-plugin-device/doc/fr/README.md create mode 100644 plugins/cordova-plugin-device/doc/fr/index.md create mode 100644 plugins/cordova-plugin-device/doc/it/README.md create mode 100644 plugins/cordova-plugin-device/doc/it/index.md create mode 100644 plugins/cordova-plugin-device/doc/ja/README.md create mode 100644 plugins/cordova-plugin-device/doc/ja/index.md create mode 100644 plugins/cordova-plugin-device/doc/ko/README.md create mode 100644 plugins/cordova-plugin-device/doc/ko/index.md create mode 100644 plugins/cordova-plugin-device/doc/pl/README.md create mode 100644 plugins/cordova-plugin-device/doc/pl/index.md create mode 100644 plugins/cordova-plugin-device/doc/ru/index.md create mode 100644 plugins/cordova-plugin-device/doc/zh/README.md create mode 100644 plugins/cordova-plugin-device/doc/zh/index.md create mode 100644 plugins/cordova-plugin-device/package.json create mode 100644 plugins/cordova-plugin-device/plugin.xml create mode 100644 plugins/cordova-plugin-device/src/android/Device.java create mode 100644 plugins/cordova-plugin-device/src/browser/DeviceProxy.js create mode 100644 plugins/cordova-plugin-device/src/ios/CDVDevice.h create mode 100644 plugins/cordova-plugin-device/src/ios/CDVDevice.m create mode 100644 plugins/cordova-plugin-device/src/osx/CDVDevice.h create mode 100644 plugins/cordova-plugin-device/src/osx/CDVDevice.m create mode 100644 plugins/cordova-plugin-device/src/windows/DeviceProxy.js create mode 100644 plugins/cordova-plugin-device/tests/package.json create mode 100644 plugins/cordova-plugin-device/tests/plugin.xml create mode 100644 plugins/cordova-plugin-device/tests/tests.js create mode 100644 plugins/cordova-plugin-device/types/index.d.ts create mode 100644 plugins/cordova-plugin-file-transfer/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-file-transfer/LICENSE create mode 100644 plugins/cordova-plugin-file-transfer/NOTICE create mode 100644 plugins/cordova-plugin-file-transfer/README.md create mode 100644 plugins/cordova-plugin-file-transfer/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/de/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/de/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/es/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/es/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/fr/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/fr/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/it/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/it/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/ja/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/ja/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/ko/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/ko/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/pl/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/pl/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/ru/index.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/zh/README.md create mode 100644 plugins/cordova-plugin-file-transfer/doc/zh/index.md create mode 100644 plugins/cordova-plugin-file-transfer/package.json create mode 100644 plugins/cordova-plugin-file-transfer/plugin.xml create mode 100644 plugins/cordova-plugin-file-transfer/src/amazon/FileTransfer.java create mode 100644 plugins/cordova-plugin-file-transfer/src/android/FileProgressResult.java create mode 100644 plugins/cordova-plugin-file-transfer/src/android/FileTransfer.java create mode 100644 plugins/cordova-plugin-file-transfer/src/android/FileUploadResult.java create mode 100644 plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.h create mode 100644 plugins/cordova-plugin-file-transfer/src/ios/CDVFileTransfer.m create mode 100644 plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.cpp create mode 100644 plugins/cordova-plugin-file-transfer/src/ubuntu/file-transfer.h create mode 100644 plugins/cordova-plugin-file-transfer/src/windows/FileTransferProxy.js create mode 100644 plugins/cordova-plugin-file-transfer/src/wp/FileTransfer.cs create mode 100644 plugins/cordova-plugin-file-transfer/tests/package.json create mode 100644 plugins/cordova-plugin-file-transfer/tests/plugin.xml create mode 100644 plugins/cordova-plugin-file-transfer/tests/tests.js create mode 100644 plugins/cordova-plugin-file-transfer/types/index.d.ts create mode 100644 plugins/cordova-plugin-file/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-file/LICENSE create mode 100644 plugins/cordova-plugin-file/NOTICE create mode 100644 plugins/cordova-plugin-file/README.md create mode 100644 plugins/cordova-plugin-file/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-file/doc/de/README.md create mode 100644 plugins/cordova-plugin-file/doc/de/index.md create mode 100644 plugins/cordova-plugin-file/doc/de/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/es/README.md create mode 100644 plugins/cordova-plugin-file/doc/es/index.md create mode 100644 plugins/cordova-plugin-file/doc/es/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/fr/README.md create mode 100644 plugins/cordova-plugin-file/doc/fr/index.md create mode 100644 plugins/cordova-plugin-file/doc/fr/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/it/README.md create mode 100644 plugins/cordova-plugin-file/doc/it/index.md create mode 100644 plugins/cordova-plugin-file/doc/it/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/ja/README.md create mode 100644 plugins/cordova-plugin-file/doc/ja/index.md create mode 100644 plugins/cordova-plugin-file/doc/ja/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/ko/README.md create mode 100644 plugins/cordova-plugin-file/doc/ko/index.md create mode 100644 plugins/cordova-plugin-file/doc/ko/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/pl/README.md create mode 100644 plugins/cordova-plugin-file/doc/pl/index.md create mode 100644 plugins/cordova-plugin-file/doc/pl/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/ru/index.md create mode 100644 plugins/cordova-plugin-file/doc/ru/plugins.md create mode 100644 plugins/cordova-plugin-file/doc/zh/README.md create mode 100644 plugins/cordova-plugin-file/doc/zh/index.md create mode 100644 plugins/cordova-plugin-file/doc/zh/plugins.md create mode 100644 plugins/cordova-plugin-file/package.json create mode 100644 plugins/cordova-plugin-file/plugin.xml create mode 100644 plugins/cordova-plugin-file/src/android/AssetFilesystem.java create mode 100644 plugins/cordova-plugin-file/src/android/ContentFilesystem.java create mode 100644 plugins/cordova-plugin-file/src/android/DirectoryManager.java create mode 100644 plugins/cordova-plugin-file/src/android/EncodingException.java create mode 100644 plugins/cordova-plugin-file/src/android/FileExistsException.java create mode 100644 plugins/cordova-plugin-file/src/android/FileUtils.java create mode 100644 plugins/cordova-plugin-file/src/android/Filesystem.java create mode 100644 plugins/cordova-plugin-file/src/android/InvalidModificationException.java create mode 100644 plugins/cordova-plugin-file/src/android/LocalFilesystem.java create mode 100644 plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java create mode 100644 plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java create mode 100644 plugins/cordova-plugin-file/src/android/PendingRequests.java create mode 100644 plugins/cordova-plugin-file/src/android/TypeMismatchException.java create mode 100644 plugins/cordova-plugin-file/src/android/build-extras.gradle create mode 100644 plugins/cordova-plugin-file/src/browser/FileProxy.js create mode 100644 plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.h create mode 100644 plugins/cordova-plugin-file/src/ios/CDVAssetLibraryFilesystem.m create mode 100644 plugins/cordova-plugin-file/src/ios/CDVFile.h create mode 100644 plugins/cordova-plugin-file/src/ios/CDVFile.m create mode 100644 plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.h create mode 100644 plugins/cordova-plugin-file/src/ios/CDVLocalFilesystem.m create mode 100644 plugins/cordova-plugin-file/src/osx/CDVFile.h create mode 100644 plugins/cordova-plugin-file/src/osx/CDVFile.m create mode 100644 plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.h create mode 100644 plugins/cordova-plugin-file/src/osx/CDVLocalFilesystem.m create mode 100644 plugins/cordova-plugin-file/src/windows/FileProxy.js create mode 100644 plugins/cordova-plugin-file/tests/package.json create mode 100644 plugins/cordova-plugin-file/tests/plugin.xml create mode 100644 plugins/cordova-plugin-file/tests/src/android/TestContentProvider.java create mode 100644 plugins/cordova-plugin-file/tests/tests.js create mode 100644 plugins/cordova-plugin-file/types/index.d.ts create mode 100644 plugins/cordova-plugin-filechooser/LICENSE.txt create mode 100644 plugins/cordova-plugin-filechooser/README.md create mode 100644 plugins/cordova-plugin-filechooser/filechooser.png create mode 100644 plugins/cordova-plugin-filechooser/package.json create mode 100644 plugins/cordova-plugin-filechooser/plugin.xml create mode 100644 plugins/cordova-plugin-filechooser/src/android/FileChooser.java create mode 100644 plugins/cordova-plugin-filepath/CHANGELOG.md create mode 100644 plugins/cordova-plugin-filepath/LICENSE.md create mode 100644 plugins/cordova-plugin-filepath/README.md create mode 100644 plugins/cordova-plugin-filepath/package.json create mode 100644 plugins/cordova-plugin-filepath/plugin.xml create mode 100644 plugins/cordova-plugin-filepath/src/android/FilePath.java create mode 100644 plugins/cordova-plugin-inappbrowser/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-inappbrowser/LICENSE create mode 100644 plugins/cordova-plugin-inappbrowser/NOTICE create mode 100644 plugins/cordova-plugin-inappbrowser/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/de/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/de/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/es/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/es/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/fr/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/fr/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/it/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/it/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/ja/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/ja/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/ko/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/ko/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/pl/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/pl/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/ru/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/zh/README.md create mode 100644 plugins/cordova-plugin-inappbrowser/doc/zh/index.md create mode 100644 plugins/cordova-plugin-inappbrowser/package.json create mode 100644 plugins/cordova-plugin-inappbrowser/plugin.xml create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/InAppBrowserDialog.java create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/InAppChromeClient.java create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-hdpi/ic_action_next_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-hdpi/ic_action_previous_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-hdpi/ic_action_remove.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-mdpi/ic_action_next_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-mdpi/ic_action_previous_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-mdpi/ic_action_remove.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-xhdpi/ic_action_next_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-xhdpi/ic_action_previous_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-xhdpi/ic_action_remove.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-xxhdpi/ic_action_next_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-xxhdpi/ic_action_previous_item.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/android/res/drawable-xxhdpi/ic_action_remove.png create mode 100644 plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js create mode 100644 plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowser.h create mode 100644 plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowser.m create mode 100644 plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.h create mode 100644 plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.m create mode 100644 plugins/cordova-plugin-inappbrowser/src/windows/InAppBrowserProxy.js create mode 100644 plugins/cordova-plugin-inappbrowser/tests/.eslintrc.yml create mode 100644 plugins/cordova-plugin-inappbrowser/tests/package.json create mode 100644 plugins/cordova-plugin-inappbrowser/tests/plugin.xml create mode 100644 plugins/cordova-plugin-inappbrowser/tests/resources/inject.css create mode 100644 plugins/cordova-plugin-inappbrowser/tests/resources/inject.html create mode 100644 plugins/cordova-plugin-inappbrowser/tests/resources/inject.js create mode 100644 plugins/cordova-plugin-inappbrowser/tests/resources/local.html create mode 100644 plugins/cordova-plugin-inappbrowser/tests/resources/local.pdf create mode 100644 plugins/cordova-plugin-inappbrowser/tests/resources/video.html create mode 100644 plugins/cordova-plugin-inappbrowser/tests/tests.js create mode 100644 plugins/cordova-plugin-inappbrowser/types/index.d.ts create mode 100644 plugins/cordova-plugin-share-content/README.rst create mode 100644 plugins/cordova-plugin-share-content/package.json create mode 100644 plugins/cordova-plugin-share-content/plugin.xml create mode 100644 plugins/cordova-plugin-share-content/scripts/add_swift_support.js create mode 100644 plugins/cordova-plugin-share-content/src/android/ShareContentPlugin.java create mode 100644 plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin-Bridging-Header.h create mode 100644 plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin.swift create mode 100644 plugins/cordova-plugin-splashscreen/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-splashscreen/LICENSE create mode 100644 plugins/cordova-plugin-splashscreen/NOTICE create mode 100644 plugins/cordova-plugin-splashscreen/README.md create mode 100644 plugins/cordova-plugin-splashscreen/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/de/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/de/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/es/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/es/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/fr/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/fr/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/it/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/it/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/ja/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/ja/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/ko/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/ko/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/pl/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/pl/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/ru/index.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/zh/README.md create mode 100644 plugins/cordova-plugin-splashscreen/doc/zh/index.md create mode 100644 plugins/cordova-plugin-splashscreen/package.json create mode 100644 plugins/cordova-plugin-splashscreen/plugin.xml create mode 100644 plugins/cordova-plugin-splashscreen/src/android/SplashScreen.java create mode 100644 plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js create mode 100644 plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.h create mode 100644 plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.m create mode 100644 plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.h create mode 100644 plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.m create mode 100644 plugins/cordova-plugin-splashscreen/src/wp/ResolutionHelper.cs create mode 100644 plugins/cordova-plugin-splashscreen/src/wp/SplashScreen.cs create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/contents.xcworkspacedata create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/xcschemes/CordovaLib.xcscheme create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/.npmignore create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTest.m create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.h create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.m create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/Info.plist create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.pbxproj create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLib.xcscheme create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLibTests.xcscheme create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/de/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/es/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/fr/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/it/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/ja/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/ko/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/pl/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/doc/zh/README.md create mode 100644 plugins/cordova-plugin-splashscreen/tests/ios/package.json create mode 100644 plugins/cordova-plugin-splashscreen/tests/package.json create mode 100644 plugins/cordova-plugin-splashscreen/tests/plugin.xml create mode 100644 plugins/cordova-plugin-splashscreen/tests/tests.js create mode 100644 plugins/cordova-plugin-splashscreen/types/index.d.ts create mode 100644 plugins/cordova-plugin-statusbar/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-statusbar/LICENSE create mode 100644 plugins/cordova-plugin-statusbar/NOTICE create mode 100644 plugins/cordova-plugin-statusbar/README.md create mode 100644 plugins/cordova-plugin-statusbar/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-statusbar/doc/de/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/de/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/es/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/es/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/fr/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/fr/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/it/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/it/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/ja/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/ja/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/ko/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/ko/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/pl/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/pl/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/ru/index.md create mode 100644 plugins/cordova-plugin-statusbar/doc/zh/README.md create mode 100644 plugins/cordova-plugin-statusbar/doc/zh/index.md create mode 100644 plugins/cordova-plugin-statusbar/package.json create mode 100644 plugins/cordova-plugin-statusbar/plugin.xml create mode 100644 plugins/cordova-plugin-statusbar/src/android/StatusBar.java create mode 100644 plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js create mode 100644 plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.h create mode 100644 plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.m create mode 100644 plugins/cordova-plugin-statusbar/src/windows/StatusBarProxy.js create mode 100644 plugins/cordova-plugin-statusbar/src/wp/StatusBar.cs create mode 100644 plugins/cordova-plugin-statusbar/tests/package.json create mode 100644 plugins/cordova-plugin-statusbar/tests/plugin.xml create mode 100644 plugins/cordova-plugin-statusbar/tests/tests.js create mode 100644 plugins/cordova-plugin-statusbar/types/index.d.ts create mode 100644 plugins/cordova-plugin-whitelist/CONTRIBUTING.md create mode 100644 plugins/cordova-plugin-whitelist/LICENSE create mode 100644 plugins/cordova-plugin-whitelist/NOTICE create mode 100644 plugins/cordova-plugin-whitelist/README.md create mode 100644 plugins/cordova-plugin-whitelist/RELEASENOTES.md create mode 100644 plugins/cordova-plugin-whitelist/doc/de/README.md create mode 100644 plugins/cordova-plugin-whitelist/doc/es/README.md create mode 100644 plugins/cordova-plugin-whitelist/doc/fr/README.md create mode 100644 plugins/cordova-plugin-whitelist/doc/it/README.md create mode 100644 plugins/cordova-plugin-whitelist/doc/ja/README.md create mode 100644 plugins/cordova-plugin-whitelist/doc/ko/README.md create mode 100644 plugins/cordova-plugin-whitelist/doc/pl/README.md create mode 100644 plugins/cordova-plugin-whitelist/doc/zh/README.md create mode 100644 plugins/cordova-plugin-whitelist/package.json create mode 100644 plugins/cordova-plugin-whitelist/plugin.xml create mode 100644 plugins/cordova-plugin-whitelist/src/android/WhitelistPlugin.java create mode 100644 plugins/fetch.json create mode 100644 plugins/ionic-plugin-keyboard/LICENSE create mode 100644 plugins/ionic-plugin-keyboard/README.md create mode 100644 plugins/ionic-plugin-keyboard/package.json create mode 100644 plugins/ionic-plugin-keyboard/plugin.xml create mode 100644 plugins/ionic-plugin-keyboard/src/android/IonicKeyboard.java create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/index.js create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/.cproject create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/.project create mode 100755 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/libKeyboard.so create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/public/json_reader.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/public/json_value.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/public/json_writer.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/public/plugin.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/public/tokenizer.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/src/CallKeyboard.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/src/Logger.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/src/keyboard_js.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/device/src/keyboard_ndk.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/autolink.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/config.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/features.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/forwards.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/json.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/reader.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/value.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json/writer.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json_batchallocator.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json_internalarray.inl create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json_internalmap.inl create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json_reader.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json_value.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json_valueiterator.inl create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/json_writer.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/plugin.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/plugin.h create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/tokenizer.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/public/tokenizer.h create mode 100755 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/libKeyboard.so create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/public/json_reader.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/public/json_value.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/public/json_writer.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/public/plugin.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/public/tokenizer.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/src/CallKeyboard.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/src/Logger.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/src/keyboard_js.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/simulator/src/keyboard_ndk.o create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/src/Logger.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/src/Logger.hpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/src/keyboard_js.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/src/keyboard_js.hpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/src/keyboard_ndk.cpp create mode 100644 plugins/ionic-plugin-keyboard/src/blackberry10/native/src/keyboard_ndk.hpp create mode 100644 plugins/ionic-plugin-keyboard/src/ios/IonicKeyboard.h create mode 100644 plugins/ionic-plugin-keyboard/src/ios/IonicKeyboard.m create mode 100644 plugins/ionic-plugin-keyboard/src/windows/KeyboardProxy.js diff --git a/.gitignore b/.gitignore index a49931b..3353e6b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,10 +23,6 @@ node_modules/ tmp/ temp/ hooks/ -platforms/ -plugins/ -plugins/android.json -plugins/ios.json www/ $RECYCLE.BIN/ diff --git a/Makefile b/Makefile index 4527ac9..70640b6 100644 --- a/Makefile +++ b/Makefile @@ -37,3 +37,10 @@ release: $(APK) clean: rm $(OUT_DIR)/*.apk + +distclean: + rm -r node_modules platforms plugins www + +init: + npm install + ionic cordova platform add android diff --git a/README.md b/README.md index c41d7a5..5059a3a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Goober, a mobile app for pnut.io -Copyright 2017 Morgan McMillian +Copyright 2017 - 2018 Morgan McMillian Goober is a cross platform mobile client for pnut.io built using the Ionic framework (http://ionicframework.com/). @@ -40,10 +40,6 @@ npm install -g ionic cordova * Install Gradle (needed if you only installed the command line tools) * https://gradle.org/ -#### Windows -* Install VisualStudio -* https://www.visualstudio.com/ - ## Other build dependencies @@ -60,7 +56,7 @@ ionic cordova platform add android ``` -## Build and run +## Build and run using ionic framework #### Browser ```bash diff --git a/config.xml b/config.xml index 3a32ec4..51bd9b1 100644 --- a/config.xml +++ b/config.xml @@ -101,5 +101,5 @@ - + diff --git a/platforms/android/.gitignore b/platforms/android/.gitignore new file mode 100644 index 0000000..6e52445 --- /dev/null +++ b/platforms/android/.gitignore @@ -0,0 +1,14 @@ +# 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 diff --git a/platforms/android/CordovaLib/AndroidManifest.xml b/platforms/android/CordovaLib/AndroidManifest.xml new file mode 100755 index 0000000..1625b89 --- /dev/null +++ b/platforms/android/CordovaLib/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/platforms/android/CordovaLib/build.gradle b/platforms/android/CordovaLib/build.gradle new file mode 100644 index 0000000..51c7c95 --- /dev/null +++ b/platforms/android/CordovaLib/build.gradle @@ -0,0 +1,137 @@ +/* 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 { + jcenter() + maven { + url "https://maven.google.com" + } + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.0.1' + 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 = '7.1.0' + +android { + compileSdkVersion cdvCompileSdkVersion + buildToolsVersion cdvBuildToolsVersion + publishNonDefault true + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + 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 = '7.1.0' + released = new Date() + vcsTag = '7.1.0' + } + } +} diff --git a/platforms/android/CordovaLib/cordova.gradle b/platforms/android/CordovaLib/cordova.gradle new file mode 100644 index 0000000..6c6819a --- /dev/null +++ b/platforms/android/CordovaLib/cordova.gradle @@ -0,0 +1,205 @@ +/* + 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() + def propertiesFile = 'project.properties'; + if(!(file(propertiesFile).exists())) { + propertiesFile = '../project.properties'; + } + file(propertiesFile).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("src/main/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) } +} + diff --git a/platforms/android/CordovaLib/project.properties b/platforms/android/CordovaLib/project.properties new file mode 100644 index 0000000..0a372ee --- /dev/null +++ b/platforms/android/CordovaLib/project.properties @@ -0,0 +1,16 @@ +# 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-27 +apk-configurations= +renderscript.opt.level=O0 +android.library=true diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java b/platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java new file mode 100644 index 0000000..d3a231a --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java @@ -0,0 +1,69 @@ +/* + 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; + } + + + + +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/BuildHelper.java b/platforms/android/CordovaLib/src/org/apache/cordova/BuildHelper.java new file mode 100644 index 0000000..6d9daa4 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/BuildHelper.java @@ -0,0 +1,70 @@ +/* + 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; + +/* + * This is a utility class that allows us to get the BuildConfig variable, which is required + * for the use of different providers. This is not guaranteed to work, and it's better for this + * to be set in the build step in config.xml + * + */ + +import android.app.Activity; +import android.content.Context; + +import java.lang.reflect.Field; + + +public class BuildHelper { + + + private static String TAG="BuildHelper"; + + /* + * This needs to be implemented if you wish to use the Camera Plugin or other plugins + * that read the Build Configuration. + * + * Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to + * StackOverflow. This is annoying as hell! However, this method does not work with + * ProGuard, and you should use the config.xml to define the application_id + * + */ + + public static Object getBuildConfigValue(Context ctx, String key) + { + try + { + Class clazz = Class.forName(ctx.getPackageName() + ".BuildConfig"); + Field field = clazz.getField(key); + return field.get(null); + } catch (ClassNotFoundException e) { + LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?"); + e.printStackTrace(); + } catch (NoSuchFieldException e) { + LOG.d(TAG, key + " is not a valid field. Check your build.gradle"); + } catch (IllegalAccessException e) { + LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace."); + e.printStackTrace(); + } + + return null; + } + +} \ No newline at end of file diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java b/platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java new file mode 100644 index 0000000..4336386 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java @@ -0,0 +1,142 @@ +/* + 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)); + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java b/platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java new file mode 100644 index 0000000..050daa0 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java @@ -0,0 +1,65 @@ +/* + 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; + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/Config.java b/platforms/android/CordovaLib/src/org/apache/cordova/Config.java new file mode 100644 index 0000000..0739795 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/Config.java @@ -0,0 +1,71 @@ +/* + 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; + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java b/platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java new file mode 100644 index 0000000..01a97f2 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java @@ -0,0 +1,145 @@ +/* + 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; + } + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java new file mode 100755 index 0000000..dbbb48f --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java @@ -0,0 +1,519 @@ +/* + 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 (!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 + */ + @SuppressLint("InlinedApi") + @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(); + } + + } + +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java new file mode 100644 index 0000000..d40d26e --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java @@ -0,0 +1,113 @@ +/* + 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); + } +} + + diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java new file mode 100644 index 0000000..28c407f --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java @@ -0,0 +1,187 @@ +/* + 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.annotation.SuppressLint; + +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. */ + //On old Androids SecureRandom isn't really secure, this is the least of your problems if + //you're running Android 4.3 and below in 2017 + @SuppressLint("TrulyRandom") + 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; + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java new file mode 100644 index 0000000..ccda027 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java @@ -0,0 +1,105 @@ +/* + 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.annotation.SuppressLint; +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 + */ + @SuppressLint("NewApi") + public void cancel() + { + request.cancel(); + } + + /* + * Returns the host name of the server requesting the certificate. + */ + @SuppressLint("NewApi") + public String getHost() + { + return request.getHost(); + } + + /* + * Returns the acceptable types of asymmetric keys (can be null). + */ + @SuppressLint("NewApi") + public String[] getKeyTypes() + { + return request.getKeyTypes(); + } + + /* + * Returns the port number of the server requesting the certificate. + */ + @SuppressLint("NewApi") + public int getPort() + { + return request.getPort(); + } + + /* + * Returns the acceptable certificate issuers for the certificate matching the private key (can be null). + */ + @SuppressLint("NewApi") + public Principal[] getPrincipals() + { + return request.getPrincipals(); + } + + /* + * Ignore the request for now. Do not remember user's choice. + */ + @SuppressLint("NewApi") + 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 + */ + @SuppressLint("NewApi") + public void proceed(PrivateKey privateKey, X509Certificate[] chain) + { + request.proceed(privateKey, chain); + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java new file mode 100644 index 0000000..a219c99 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java @@ -0,0 +1,152 @@ +/* + 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); + } +} \ No newline at end of file diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java new file mode 100644 index 0000000..724381e --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java @@ -0,0 +1,51 @@ +/* + 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); + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java new file mode 100755 index 0000000..ff90683 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java @@ -0,0 +1,97 @@ +/* + 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.Context; +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. + * + * If a custom engine lives outside of the Activity's lifecycle the return value may be null. + * + * @return the Activity + */ + public abstract Activity getActivity(); + + /** + * Get the Android context. + * + * @return the Context + */ + public Context getContext(); + + /** + * 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); + +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java new file mode 100644 index 0000000..9a6e924 --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java @@ -0,0 +1,249 @@ +/* + 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.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +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 Context getContext() { + 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); + } + + @SuppressLint("NewApi") + 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; + } + } +} diff --git a/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPlugin.java b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPlugin.java new file mode 100644 index 0000000..41af1db --- /dev/null +++ b/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPlugin.java @@ -0,0 +1,422 @@ +/* + 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

8jdd-TCByoPrum+W1>5y=X~x%S~90nZ8lPV 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 XtHiD0*JMY~v2+Zcu6{1-oD!Mr`Z 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�)^Qp5@zkGhW)0=K8CVy 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 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-land-xxhdpi/screen.png b/platforms/android/app/src/main/res/drawable-land-xxhdpi/screen.png new file mode 100644 index 0000000000000000000000000000000000000000..2903a88960cfea3609dee01357be5a2bfa10e4b5 GIT binary patch literal 79257 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

uvW43O-s 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�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 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-mdpi/ic_action_next_item.png b/platforms/android/app/src/main/res/drawable-mdpi/ic_action_next_item.png new file mode 100644 index 0000000000000000000000000000000000000000..47365a300110db23c24d8471b4412cff3299335e GIT binary patch literal 427 zcmV;c0aX5pP)}4 zNQyzD&z|KhmJh*V!SWC28k$rgFbJCTOw9>SV0e#=m{d{d4v_RE6S|M4@9pf)?AB@( zD!dzlAQ+YTy?O!R7Jg<39}z}Tv@F9wqrkgk5hq4vW#Z%37t=J|d!9GIV_T>J#tHae zs|*lx#8HhW$b@f_0m;G{vp(Q)h-gX-!EN#1zjFg(;tztmoQbZOIJ;hgD^LMkhP9%& z3{#T~s7xf7HPhY~s)O+-_}IjNm^e^Wm8VZr!UUxt|dS5ABh@z}BtY}8#KEK=()eJQO_;pw;tFaVN+ VbL}20dqe;L002ovPDHLkV1lYyvRnWF literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-mdpi/ic_action_previous_item.png b/platforms/android/app/src/main/res/drawable-mdpi/ic_action_previous_item.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad2df42755874f331733cfab5ad0931d5baf0a1 GIT binary patch literal 438 zcmV;n0ZIOeP)F6XU^oF|V`EEF^)OP= z@&q6Sa=>FC?mK(->^EZcF;UUyFyG4pu`{uHnW$j70gy^S5_$~u{bss20BAYLxADl9 zgMyzFGngo_To_0d0WlX;2o&cFiH&=T900T&WN9Xv<)DQ3m{haK4S`H(VuOKIN}9B4HXgw@Cj>q2Ew4lGVMbE8VbLGzP<|0lB`f{0u;Ljl>0<;2S6S19b_TMVkAc>0>#dN z97vaJ2rYgWA_>VtD=6BB08&W203|{o(*P>O59Bif#c$EAAcEEf$C26udk3UcBd1Y1 g3P!PbXFRCwBAU>F6XU=$26V8kbBY;0@^ zq{4u>@9f#Lk4Z5Ys8<$96#?-opx)!S)G-k-2V^-H$RMbJq*~4eHG_d%2lN5)H&Psd zX8AX$88`tmKFR0LpMR2)lDYX-@nh*(FEV>wX{AjT2oT23$* zkPyFQSx&M8a5#d2T+4}$0s_&&O0MO^WJ8(*W-3I%Z(_12RUCjL5gsSDoS?`7xUw3l zg%w#2Ae7H3aRf7^mQ(5oW@=bYi6aPQLy}84s2Y@7l#+5_6*+|rnj=YcL aAiw|+C&DJQP0oz~0000D{HNH<8w&_i?g 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 G#{U7C#eYfw literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-port-ldpi/screen.png b/platforms/android/app/src/main/res/drawable-port-ldpi/screen.png new file mode 100644 index 0000000000000000000000000000000000000000..80b314a9d30fd8227d2659d8332e306bfeffbc66 GIT binary patch literal 13345 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 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-port-mdpi/screen.png b/platforms/android/app/src/main/res/drawable-port-mdpi/screen.png new file mode 100644 index 0000000000000000000000000000000000000000..f5181dc36608b07bcbcb3fd01f9826f16bf96306 GIT binary patch literal 20274 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 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-port-xhdpi/screen.png b/platforms/android/app/src/main/res/drawable-port-xhdpi/screen.png new file mode 100644 index 0000000000000000000000000000000000000000..43df4e80c0e8a16d35bf321a1cc37a4617853166 GIT binary patch literal 61157 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 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-port-xxxhdpi/screen.png b/platforms/android/app/src/main/res/drawable-port-xxxhdpi/screen.png new file mode 100644 index 0000000000000000000000000000000000000000..27e017a4a7bab5bbebb3acb5a602fa6dc2473b03 GIT binary patch literal 100982 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�N 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 R8pvbmoV)yYrk2T*{{wo>2!Q|q literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-xhdpi/ic_action_next_item.png b/platforms/android/app/src/main/res/drawable-xhdpi/ic_action_next_item.png new file mode 100644 index 0000000000000000000000000000000000000000..5f304742f6b035d94ecc9bbfd781e73df9f8f139 GIT binary patch literal 727 zcmV;|0x127P)YQCYi7Ht0k1EPi|MH~WyXp>^jAd0~7hD?*o|7+zUSOmJ?LZ_^s!$gZ3u%7L@{I=7VP% z_a!2<(cVB@qEY}B;TUmjD@L`X^1Yi-;U)bXkac?RG$k<~ew|5Zb_7R-fV{idjgha>@ws{Ht;KCkj zQFSm8?6Kj>9;+mPy8VE^q9>%u-?;9XQRxD|Aiv-xvsxRx6znc_IpYTx zfWH#Qg58aMlJ_bA+ufNEGInWqK?Pv)KiS=j%2b%`t|x@t`LMen0`RADX~rk7pQv06 zWlo5c+MO@257h$(g5A+%H&VMCX1nVMc6aOB?z{=WIWqa27|$1Fc=Jme~K>w1^{LC0JpuQ^5_5n002ov JPDHLkV1f=sN^1ZB literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-xhdpi/ic_action_previous_item.png b/platforms/android/app/src/main/res/drawable-xhdpi/ic_action_previous_item.png new file mode 100644 index 0000000000000000000000000000000000000000..ed8ac91dec4b072dc40804c4b4178b065f7ed708 GIT binary patch literal 744 zcmVP)0=8N@8J`wu9@%$5uRVrBx8ftW!IF~j%BBb>Mv#M(Z}`;z8v z>q~mycWuAdYfB}CLZMJ76bgkxAqlNkt4qaoJ9|ROcW^o9p9_@XY&P4tXlq{}`3#qf z++S6c<1|elR;$%})B=Ef9ha+ustQU55vIEqArVHtZwa}_Fj()p-9!laGA@@WWkbjU z!sO<7JdXAU71YH0fFF zu}1R+4Vfqf$l2o(x2L9_;L{$1Y>txc{lXr-h!AXdnC+1!y(2?N!-qWv-5#e@*pefB z+2hp3xd0IYlKW(J^?H-V`$EC#{tFG)(%n=#3M_NLk z8A7Tq<^xe@gjj?kd!*svWUE}@34V@t9P5IwAq?PUsz((uHG(SFn?P~L$y-sCAB98M`&=jFYBD)$Dt=Dzu!75(wV*}TUW0~lD*P>x|&Qd7_P_ms8#d44)Bd2C{&(UWzlACh z2h+J7JT|oGD;)@GGOaOW6cZBo;L18>N<)*r-hvhul|7veGMpI|#*EiJJ~Wt%evs#k z`(T)NVD|L*hR@nt*!TS8@Q~!>{+{sRLemKr5utrj6SlHuxN0~$If#cv2)meV+a%%C zt)<5#^dMHnC&XAtwAM4=^N6;oJNO3DF++P6=euvz@Yh2KA?RyjT2C~1{B@8Z!OR?g=u^>1hj z_@^A1Ecxr>f}d?`p480di?4S%CphygyLXa^10xFBaPo&p0z-;&MeJ|;OL4%cX7F_N Kb6Mw<&;$S^P}b@I literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_next_item.png b/platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_next_item.png new file mode 100644 index 0000000000000000000000000000000000000000..51479d8ddfdeccaf4af263b3420af6d536cab5ab GIT binary patch literal 1021 zcmVAgR7s3xqaChTCq{={$6$9pJ`w$`@*0PUS8yqfTW{_#D)Lv`px@2`t;Arl^M+khT`w(Vfi?T|Mb^%iv_Rq4U1O zfOIC@a@?e^XMrZPbBL#^h9X!@67>gteHBogO**6UmzLwHy0IW`R!G#{y&;LRXM`)O zCKMVkO2&U;;^`1IU_bGc#jS@ts9BAth4Sm0M#j^(r~&EY>6LWKA;S>dAMEJ74Gc(Y z!YU%3wk*Zds)dV`c)F&quOs5A2@|STsw%&fiHxT&EyUBp8A9UeJR+WsPy^D}glmqR zB5)Z;+T*D?1H5MhZiC zVZRAHMcoVw(*}4Z#Ezb-nPFkx05M@hqTWiMP)5xu8p6)P05M@56Hocj5Kpr)z>BA$ zGc1}Ko)HR{AEstl_fJn3par{*9*JP#6~f&!WE2MH{@} zPu)8K%QavW5jQ_;U2Rzyz554|S^pCr#L|i1kBOTavVJxONZbsae#o8e7()^1#BW8$ z&8eoiY1#mZo5Kk4$J*kiSpy_)mM!M?nOPBEy#d}H62}qZo7&t%dkyGE#?84iMxprb zA4J5>mG-!4$^eO*F+=ALh(D1adT`TZkjMa;^z5t5nq)7Ug8Ojn^Q}1Q?&vA zAU+Q*XR%|7mKtDcCPP*RSe(g_jR96?GUQYPc9Ld7%9&3*4{^}Rxw11#*&abG`bQE! z%e_N2>>0-kEH2&S>%BgZ_-mIbnzk(CMR#@g_wFbhH@~`!sc5?(ZxC)y;?Ep7hzT92 r000000000000000004ko_$|Nymn?!0<{Ma600000NkvXXu0mjfBNxJV literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_previous_item.png b/platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_previous_item.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8ff1241ade1ac7ad40427f4843b84fac947e62 GIT binary patch literal 1038 zcmV+p1o8WcP)mo zA`K~MkRSyq+|VX1EpADX!ubtpX-R2X{)3w|G(jRoT+p~M5_UmDVHa*Pqf8=#jI$FR zx|!vD(&-T1laBYiw{P!uE(8Do000000000000000fJA6En+=QS_547@@6xVYsZ?Gr zm&=)@b9;6G;5yEkAX@suJD;TiC?rQeVpkoe~${><9%@Z1CX%D6eP_#-l>|NT7R)*_qFEX2)Z z3?T74%D6eQ{ChHI;>$@{R;^sR=)*pDC&SwV2=Nay) zB5sa6#7!3lNZjlz4jFifo8}FWxY<|6P1oX^F+jPJff;*?o7Q_kH#l0!kdy(ka5gq> z&b`J>lLkoK>?q>qFeFBb3$tKAEHaSB4yM|O>Vm=-=W@XW7fwVD!p9biEK;}xjpZ62 zLt7q8q6g&I3m&*&f(PFoyy8#?wos z2`=KPn?h|dVXcU#JX@YvkEgCGOTo}PAv_Ple<6<}HMg*P=mGnQr>{fY zL;SMkIfCaWV#A=jh22IE$UmO)E$kCTGFlY*W|X81*f2p8PwUp=sr&Z@Nj$v`zaHkq z#M71}qk9;Tp9we0Ti7)#@idubDPC7tB=yQNiKi@XIik6V6&5Kj%o+tpD=d;TK#Dc^ z7Iw_t(7VWXxCH%2JpI=Si{I}Kn!|wo#nW9YEcg=jnZz&rnL*NT&fT%Xf(P9%R_e-< ze|Iz^H3&S|PExBc@MJ;*DgXcg00000000000001>2!0AM0Ol)zPaPAXwEzGB07*qo IM6N<$f+VNg^Z)<= literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_remove.png b/platforms/android/app/src/main/res/drawable-xxhdpi/ic_action_remove.png new file mode 100644 index 0000000000000000000000000000000000000000..331c545b8cb07a97ee63cb4f1256d1dba5557a82 GIT binary patch literal 681 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGok|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n7ln*978H@y_sX^mF+0u=KlZOu|s+fxN=%TuH_vu-63%O z0bh(`mh@(>)(|r>v-Oq66AmapP&R+I|NHM}^O&8P#J-=iwAdNbrpXGl5g7zooZfe! zXy=~Q^508s)?R;|&#w7(p8l)j$+D}f&aa<&y-Vlf4wmJ z{m;$!KDWm<)IWJYp?`yVy?a)Rgy+7R7WpU3-7WlQo&Q_^@0!|=9dkD{|8ah%BK}}y zUwJzR`>Fc-=T0VbW}WtqPP|eM_FyzT{_JE!R zD)mzsyaca2=@)ZQY+Cdukl9DlG4$UH#$_D~bm}KDB)bJnw~uTvRI)fR-^3w}Mf~Sv z_7fcsRN|EmXe#eGIi0hhi=*$;Ql>v@O{@PLixhm)QT}Dxr&MO1o`9IAr*$3O_1veg z`?Qpqrz1f--tK_DQpAbv90e{c(m#7YGyP$o^P)53LpC?loVb0lEiVH0g|-+>zh76d zzw5&@{nHH0@veKsKFU^vF}CfCD89*-Dfr;V_F3ly55?}QZh7Ijuejxf>%Q!k7oPi) zTVD9?i*9)lxG%WnMd&{7mKTxxoLgSR?yG8<(OmPC!%+O^D#0YbPom04?mSuT+F^Mr zylcW%?L%qnpM8_#v%0PH_*W!j7~dy$w}_iMj&Eb;mhJQVTeVv6|AD73EMLFabmp+t zH{}!WBYrC$Dt@-J@Me4I8>JZ0$#0vg1>-jHzhy>_Y6gY|m493Y3}5QcuZR|#@EIiM M>FVdQ&MBb@07pA8tN;K2 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/mipmap-hdpi/icon.png b/platforms/android/app/src/main/res/mipmap-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f1232b6ccb3b95f1d73cb995fcacd529055958b0 GIT binary patch literal 5483 zcmZ{IcQ~70+;^zGSE*U6F-nW75o&}86_ld2M`J5mu_-EwqQodcs1YNInzculs!h$> z^cP!=Q2V+2=led_lj}M;_qa}S-{*YR_ryIggfr6d(1Ac8Mm=3^6QI}q*J!AKZ?mmh zIM7kJXc}mOK-Ed~XAYFWIhn5s915x&;9CPu1fS@d7=S>*U=Sz*4Fa72mm*d{AU_!p zXx$zJf@Fd~Ts}F?#;U*#Do1^|HqZl~W385OpriHCweSUj=-K}@GEi2|H4uoyUQb)o zG;n$=$K_$R#oO-iBMh32JW^MO>WTt4!Ux1l+|I?$l~t@~oU$H@Z1$cjSe-h0_=mXo zH-vnfHMH*p^I$zYDQ=``D60n8sX`U}1EOj@dQ0ds9%u}nug$Sv5WhQ~s9(bNzP=bs zGYTWiYY8Jmq5uE3rcR=4jiVJ}@)DxZ1(i8Tn7qjJ{9Q{eiq@dfypjZH;>b@3)E zHQXFHrov(LF!CExE`O98bZqMo))0YaDJ9$&zh>{^aphhr_P5oG)B29*`wEtLn6X(IPGa{5y$SbH zwNjTx1j*%QX1}w$QEow7Iaf)o0IVdWgr8(V#s*DBfPMN67qW1ooQaVj80%vx=ll1! zChu=PmNGw}xVG96FZkXto8Gp)DNr0IDB@6EVYOD2jII z5YJB`w{`G4>0KI+xI~w&`8QHk``4hopS)TpE5P)jwKQK`{L0&H{74@^DxlIj8TIS6 zFde-aM`ZovtHOe;=Wt3+eOb`yDQkn`1nuSt#>B+^_VRy<;i1d8gSezjLEL@Wi$Aw= zaZg$7s~?w^67pw^zj5*lilswAgDXv_cRVGtLKxwiJKaPYyqp6*`uciXMuP4LhF!*02>RHte}1pSim zUuIr57!%9oZ?0FypS%eZUU@EADVF-h@igapqmD&W;fFwvXS77>%BB70Dbnempc%wH z0jJdyMe!fIuVamiAkION_iP3^;^@6NTWC`i^o z-Sh1w7N~e%OR+^LL|-`9-sxGSkNB5#-g%BUm^8_8M_#cFo=MU%3*CFEd-z?%O+S)B z_m5OHH|d7D1z|Q?A&NS`oZskL!zd97EaKH}%u#0UHIeLoNBOMAz%`zUj+3`Fdvu#l zzZsZjSF*i33X2;!DK<~qW-b;xEW#VC#r4rFX_LKlYEoE)th%DXt!(A-yD!)P=sFIE zjhk~}*Z^eL1&{>y7WMmoX9k}oH%;l8ntk=GmO-6+1fNh3@xI<`(NfVFIMHdpI8@29 zQelC9xE}XsVLPeuqauBBQ5s($AFT6moGQ|0UcY|Uh2}ePR)}rfKdGW94FxfP;IwJF zM$XusOTHQVD)%|lYfZEl*SBS#xigD}&cCz4a1Ofa&eL*I+DlJLSlV@)SB#&Ugnq#SpGj!2Z2 zqN~@|+1FALJksrgcF>$_ZFPNlWXuX#Yw?!LX7i*qLA|w`WEBnoUW5!LE~wE@2%NE^ z>^QU_8u1?+h7fTBbJ;-my7ICw-h*2yjngx&p z>uasAdK|$Sy^C4d!p$8?EJH&-6ABCHB9L;La;)lm!RqTwY|yUdImg{K&o1S>>y6N+ zLbjw3-l~8Y6vU= zAC*C6(tih3|k*3J)?oN)a_=_cczBVYM>}Vo7q;l22?pTkd*6y^rP#yF zK4$=}Ra+n4RI1&-uTSq$rS8WAhnWd!rkNBFB;jAHlWL;SEJZ1_Ix*qS9==*F3$MV_ zkPrZM7oOO3d{7bkH9>N~rZJ%hS$u*{joW1fkZjILr2QNArNDTkhr>A@vz|5m$%pAZtiQ%J=nKzCKhQl!Ku!Up_y&E z6haziT^m#2)E<^3Sbg*c<28fgc~#r%97B=UcN-OhBB0uDj(o+{s@*RghbgFEf1&{EL)y z-y&%<0-a?kM!dz4Ke53qJxxNUG-2b;pp@i#;*GtN-}(PhP^WMWzi#_M=@Q)o7XgHg zv;1o96x4pgNSM{d`k6@jP$O@o zSH?-ntd{v3#>IU7Oj|M?S~cPOVu3C$Fd<^zNFmiZ$l;BtWL$wpMXdnNHpw)+#XbzK zVHqyTNB5ylrh9sw4dWB*sqiRSO@GTD(tP5&vDxKV?I_()T_+P0;!@7yu(6&EIN60H zUq8{hq_!n5m6SMhF~1Y1bA-(fLOA&`GA2q(H9BA8Z`TrLg&T7W9NN4qEa#fo?&ZE=1bXO8<#EZKMrUxW_NVCmO7ZCs#gG2afk(z4?!9~uQFu;W zMf~u*v2~@OwIJn9WxE~vjuI*g0Q^6mDF-}@T?`2q1N2x{At59rm2!fi%#;o`JUljP zbQ#?%{#t;9cLn3exy!tj65HONJuaBCB7-FBbzh%zcHiXecw)MT~H~%zn2sR-o zNtR0ln5Gz(0CL|4ptoO0UtIcTVP!k!H@-xaUGLkg9gbUpp)1=SKDM?<54fW3AQQcj z%M>Z(&uMGK7ke3>h8^KB;%x^R*Rbk9`C#3=22QTgxv|q;ri(BTWRf<(`?6jz)v|)q zQe|LdcuZdsyE1r5+7Vr%4c&gKy8Sd)4U2Sg%SA~tlDoTmG@(d02_89Vk4)thLSq0O zSdGdf`;L^$MCef2M*9A_=+P!`)3gJ8DH+iI0cPubd|S&9eedt*6j2viRa>gR;7(@! zF&JxR%R2#Hm&~?DhPL2)T?<3fhDsYOP$8%L56u4)WSw{1Kjsk_RH!~Hag|wh^V(RM ziOzqirKSWaWPTx>I;<@fr8)SucY?WVczV!c+&lxD8~D9)KETyQKGzN3R<0PHAo{vs zVZV9zRCU_d)YTPYK%DZ8=2&SXhq$gMs{N(i88FnezutVI;%=y9Y<5+`EJ24oeN4<} z#}m_2L|<t?yN03?s#{L!Dbl+!`nS!?qz9>n zD);pEl7XDD?>=UTSf93}s+#$PN*KB@k3g!C{d#N%6SwrYG&YMWEFcJ`5}I;~2c>Se z)}7}Uwq*Eny`tmZD<5_bUAcuta7J~3T;ZOfD_;4Z}J1A)Dl8p#b)*# zVW5f(`WG=YMA^Qi$?c;IM+wWjfkNFQMV8&XUCIiALKJ`kO>81AP6G=!N&xw8-5o> z4}-x>%_eGU>NFrYXVhP&;q!aKi!vtnQIP0wqy5&csO3rHjS5g1;YX{7PI*H*m&#{% zX&stq6r|_x=a8&dIlT_@JZ=DA+uGs=m!19Gavm_+W2&E(T zkiq02n3%lg=3am6AIe3aAb}vhhpX;{*>&4Ke@;#|H#*t15g>V|MgJO{8wC;hQSx+iWk(g9ak~P$WP_ z1Le*e7trMdBcHADkCUD^L{s(NauGALu|V1u9M1V*TUGeeR%dyQ({j4`Lx%ED=pl8^ zHegP*^_o%*Nd+{9KgXB%x03aobivDgg9dk0NUN};#pwx3#-?pk_H(XVcXqP!+w*C0 zh5@2{z*^+RR1iHn!>TH{Cax?!KW2_Ej%oWJ&} z!j39WFcHt2__lEdrdOpn+54a3(9jm-2zPFUREn-BU0AvPBQJio$!He9?=|#86m<&Y zf+h|yK7dK;Y|n!^Xx^=I5C}#rOq6rX!+^nWZ_o@%>G&s)0OT}| zvjT0-X5B)fR>+mU139}>W}0FuN$cuxy0maN$B!}kNflv7&G?YOFVO@}r3HdL&4+YX zyfzFrmJg{~L-68UPVExxTOB*7;AUf5{6qC)PMpNN2sDszscy}8I^SGG*Ip$1`!+Wv zdbgjyNiE5WU{>r5V$gVLDhX4_TNFU}n~f>aQ@ZK7Nf2)_ImmFFJNljJ`%J5ZSlFU= zKigeI9XGj|-h~52!@(DEfT&T`vHMpZ`IwrfAizPY{&p(nK}wIj&X}?*4s^?w!X{ba z49}+!`NC?Mo71Ew=4zIu4CTw|fgWeMM(2{rxoO7`-zz}cYU}E(oR4SmDaPZzjY{j% z_Qw0FTG>$0j@26i<`@0d@%&xOe@>_U{Gw#Al2J&C>+u7b1(&d=c#zL+Hix;p=h=D- z)R!zrLpJdG60HKY9zt&*!ZYN^R5lVut{&Y ztb{C&!RkSr(nccGEx|b;Y!ziw_=+ZVg@+Ov8 zJ-de$W=*=I7R+oFhXX(h*SR^AvsYH^?B9^LydO+Q>mZ4B2DAsLR;9IcdVQxM1MS9F zFN{HY60%rC+H@~16*WIIkWOSGP!dd6l>6TzUe2VrbO`VvX4{c46 z0Grzv86ddrs1;@Sl&s|-8?onMX8(U&82#itaQbBLk7v26>2{5WK6+(1U@EHy$^Ogx zmyrMmAEe)e07;<;-^MF(L#NQcsPHr-`XbEFU`uT3UN^ZY{>x%%(cwzjXE(z+7gb!g zFds@hqBGy2KwKW4DcCbbUv_JNy21~9$9V_}8bM@K>J z)%WGF09$TgVt4;^8TIuWrt~0(Hb@5tb}MI{>CluiaXJ;NLGJLt3=|}4*7n;%I&Ws* zTO)GnH)Yg%WlN~>(V6KUV5wEj%zOvHmJs9K|U6lVgPs z;;6gQ<~M;dd1!bnzF-w&as#MT_M5r+FR+2CuMA6urbhEJq||2LpH|;^1S=^#ngOZh z@#g(9ki=@-7nhV=8W}QxG@r7P|KKv z6K+2dWBNP4>go#KgS_Ph`HRc^9Wy>vYI(-vCH%%t5k>p$kmVhB8XAY_!oV z#|0$8GOc#`2aw zCXVMk^nS4q*V&%^M)d1gwO_e3M!zg9NO@K4%#&pa2u%Ly8jylEVV^cVy+5?ROB{A1 z&!B40Uz3k|YzExGBCb+#q6;3_+C~=E@YGJdED#3@ z*`;dn(+~%s3NR@{E?S|-9d81vxuzPjop@@VC;jekApHX#qAMZ=hh0QAh`r6XeNrC!hn#$;c{6%iNWgQ8blR wfZUaZ+>yH}BLk6RKvk^^uqpj)i>USbTR@d4fd2oC~~kp;lEEX}z3g-HRv%NxbH)ptpa!ur3nQU{3fvVx7 zy9@m%S`vn9B8%e9lf7ZQ02NWVeij9h;3h2~sW+x$9$jb7^a^^4h#WsF>G(x(3Oa4 z9-8R^o$g9`dXsnaVgS!yB|FmSVQbRuN^*j`^o1BR1_4?pJXI@jWO*sHWV-cV3hv1B zE=fGro+)$mK*CS0>wLQ4Am7U~&AE@@a=^_$O0?_OrcUjptlSlPp^@vF9TYDZcR1 zq+>K^kQISQbeE>%<*IoOlD4SdsXib9B+3(qy(vy?wvrfmCjN9X05J7$f`IB;ZAp7r z1T2PhVrruNJz33Pd*XxsArrY;mi8bNGg8&+98)q_N^SAl*V5B}*m^o?CKf+!F_Adi zGvU!^GxfvfZ;U65#=^ADjjvm^TT5?GeSo~)gBhz-8ugfp){ibDUlKD%>#Pn3lYYT! z`W!v7;8Pyjyzs}u2Wyh<#a11!Y7wnqfcsz?i=ZeO^gYK5F}S*0OA|x7pRbPLiVRMQ z2^i@7JDKmCv|iVU4*A&C2YA4-WS6cLe0gpx`APb=BH_nUQ(T<1O0d&Tv17$5P4^@eoY32ipp1gKIp7M2xlD7 zn74N)8ah_4Vff-CVn}l~L!?C8SsnKa3scvr8U2m_i8>G?{YM-sj`@UQ6%vhdc z%9cuPAsh?8tgdnt`bTilEa6!3&`xt%m|X&Zi7J)OpS~CL&vWSGfkmN8D*|?eGD&l= z*ljT%71r(d9z~HFJ2+pBee2=U9yS)Va=v1!!Jl4L3)w4^!&S81*_F$muIb721P^^a zzH;DAwk{nBo_zDZC)GHDaC7ds8pHJ%u|u7t+CsVU$UlS)iC9wF%;OZN2SZ#7ny zWA&`eUd*E_HNK2AEz1oHT7QZyYnrr-AU_a%hMTYb+<8lEbR}_NdkZmDSYh{~_3ion z8%p+~F5PoF9e+pVN1(gh;Vm+OwR75GCY==ug1md#Av?T1=M-6FIHU#%@w`3mG>7Od z&3Pfyv$!s4q*;nvNJ>m(3KbpArb+;gqpVIKQMO2wJ;}-rZEJ6?ChKlH=Bzw_L4p8M%O=U&5NJX93HiU0tpc%qOv zDWU(AysWenUn=xUae(52b^(B{LZz=HIjIJ5a2^PteGalE6$W7_92x-lCIFCn6#za< zN2zZBfGgdyO#}ePN&wJiiSGHH0RR~S*2m9nzB7KID`8s99&exySCPi+DUVxY7P|Oz z?Qw&G;7YDtYM?F$qa1}g$VMwA2OKMB+w_(aX4_)N>L?{qmMkxM8WI$HUL`$P?>gBu zk7^d_E=O?z@~GeSl?5bT)S@DQIN!rJ;_Um%10LL@i?5-xLJi5zKu10<&R31*3eeqT z7;Z8wZ~1tfI>Sx2fc||+G?eLaknN*1(LlRG&}O(DXv*;DD)ehicjKN{rMZG)ycJur zyk^_Dt=ZmBI(QXq8>$N+h=tF!a~JO>UW+hE_CHp}vX1gR#6%se=AW6l!;y$WuUtC% z@K)s0Zhl6H{=`ihAFoMsl_`s{o)L5ElANcSqo#Qrq`PdgzfNZXF7e{g$E~bKw<1#l!SfwF zmbXGC>3C`&nC+uDR!5#}Vx|Q_x{L55HQ|%ZQMYq^m?*g+Au*R?mPs_2XrSg&Ooyw8 z_ilu6Fe*dB%TK!#1+lgiXCT_^P-+k)JM2UfLFZP6I~^&L5&$k`Sy2(7ERrD?t6CXr zH`NlIPdoXbkzUNSWVp-H5TNU16G;{6T7+@aRgYmIF+D^t7Ne3D4B`7}aL`KSY#0Zv z+?a;Ur=MhbDLm|fODJ3Kkv@5yxg^}j>tHUZdHskfwwp?r>(l(y6?>aqB#LnMf6@>Y3N(ID2`hfjEG@>U657S z2}?Xz`+jI|n6oC)*-f!Dcu`uH+qW6N6X%215t$|2_CuElyV*=P5YssV&qcSjV=f0A z_EcOi0fE8lbUIvz*YnJkN~f=;gOte{l2TGla#qB2oPWT|%6nMq61h3e+TUXRY-krvLRPi!i&_+)sS|1a z?`Mf}Wuq~}uiiLJK^!6HN?hTU7$l|ffOs>Yz$9CA$Y}J-%NeV0I~r9Q&c&I1Y8Z>m z3qijbfhQhWjk#5wpDk&&GnQXGYEe+@Aa=5Q!PA!{St~%H8=2ZYj_4hC8SJ~&zg4v< zueE>71(0UohXpbQxcUO#*EMh+unG`P z|DyJpk^@tcpKY43c6ut!R8N$ zpSLq?ga+MzsE~(Diy+U85Zk#(zSj5>6nOFB)H7o_!?5f4{X|3TF_`aFwL#GGGY4Q? z@i^LNO*yIYQ@rCwSCwwJrkY01+G>O(z%?cP$#?u$#W==pVE?VtziEy9M4lSYC^^4? z$+pVDF{ppt&ADSI!W<7DnqL0`8INz~PADb$E-W;IqPff5*}00~W{p`44#S99Bgl%T_H*kQruu`%l}@-qM?e zFw)}aW_a}uW5t;GcE9}!%+U0A_}{6?Mz2t2m~CF?{A9;3C)UYDLNscRFDc|CrG=um zcr$xvVOv~D`s zWK$S|OH2NamoSmYQGZ z_dVW5?|%C|ecs`Oxis;{v2KLea8hi9V;DO^3cwm_Wp56(HHX^yS-~A`tsG%i-$S8} kP-yPlr_}#5Fr&k1V6cvydI;26myM}tM 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 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/mipmap-xxhdpi/icon.png b/platforms/android/app/src/main/res/mipmap-xxhdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6edb68fb78c8f037508f54e52081957ec0d52cdd GIT binary patch literal 11620 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 literal 0 HcmV?d00001 diff --git a/platforms/android/app/src/main/res/mipmap-xxxhdpi/icon.png b/platforms/android/app/src/main/res/mipmap-xxxhdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6a18693e1dbcfb806bb5e10056e5877d49126a88 GIT binary patch literal 15704 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�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 jOlwd6#|vJ#+CI1U``=$M + + Goober + @string/app_name + @string/launcher_name + diff --git a/platforms/android/app/src/main/res/xml/config.xml b/platforms/android/app/src/main/res/xml/config.xml new file mode 100644 index 0000000..c6801ce --- /dev/null +++ b/platforms/android/app/src/main/res/xml/config.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Goober + Goober, a mobile app for pnut.io + Morgan McMillian + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/android/app/src/main/res/xml/provider_paths.xml b/platforms/android/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000..3785fcc --- /dev/null +++ b/platforms/android/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/platforms/android/build.gradle b/platforms/android/build.gradle new file mode 100644 index 0000000..d982091 --- /dev/null +++ b/platforms/android/build.gradle @@ -0,0 +1,54 @@ +/* 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. +*/ + +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + maven { + url "https://maven.google.com" + } + } + dependencies { + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + classpath 'com.android.tools.build:gradle:3.0.1' + } +} + +allprojects { + repositories { + jcenter() + maven { + url "https://maven.google.com" + } + } + //This replaces project.properties w.r.t. build settings + project.ext { + defaultBuildToolsVersion="27.0.1" //String + defaultMinSdkVersion=19 //Integer - Minimum requirement is Android 4.4 + defaultTargetSdkVersion=27 //Integer - We ALWAYS target the latest by default + defaultCompileSdkVersion=27 //Integer - We ALWAYS compile with the latest by default + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/platforms/android/cordova-android-support-gradle-release/goober_m-cordova-android-support-gradle-release.gradle b/platforms/android/cordova-android-support-gradle-release/goober_m-cordova-android-support-gradle-release.gradle new file mode 100644 index 0000000..1b0bd72 --- /dev/null +++ b/platforms/android/cordova-android-support-gradle-release/goober_m-cordova-android-support-gradle-release.gradle @@ -0,0 +1,31 @@ +repositories{ + // Google APIs are now hosted at Maven + maven { + url 'https://maven.google.com' + } +} + +def PLUGIN_NAME = "cordova-android-support-gradle-release" + +// Fetch ANDROID_SUPPORT_VERSION var from properties.gradle +apply from: PLUGIN_NAME + '/properties.gradle' + +// List of libs to search for. +def LIBS = [ + 'com.android.support' +] + +def IGNORED = [ + 'multidex', + 'multidex-instrumentation' +] + +println("+-----------------------------------------------------------------"); +println("| " + PLUGIN_NAME + ": " + ANDROID_SUPPORT_VERSION); +println("+-----------------------------------------------------------------"); + +configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (details.requested.group in LIBS && !(details.requested.name in IGNORED)) { details.useVersion ANDROID_SUPPORT_VERSION } + } +} diff --git a/platforms/android/cordova-android-support-gradle-release/properties.gradle b/platforms/android/cordova-android-support-gradle-release/properties.gradle new file mode 100644 index 0000000..5d42a85 --- /dev/null +++ b/platforms/android/cordova-android-support-gradle-release/properties.gradle @@ -0,0 +1 @@ +ext {ANDROID_SUPPORT_VERSION = "27.+"} \ No newline at end of file diff --git a/platforms/android/cordova/Api.js b/platforms/android/cordova/Api.js new file mode 100644 index 0000000..e97f538 --- /dev/null +++ b/platforms/android/cordova/Api.js @@ -0,0 +1,411 @@ +/** + 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. +*/ + +var path = require('path'); +var Q = require('q'); + +var AndroidProject = require('./lib/AndroidProject'); +var AndroidStudio = require('./lib/AndroidStudio'); +var PluginManager = require('cordova-common').PluginManager; + +var CordovaLogger = require('cordova-common').CordovaLogger; +var selfEvents = require('cordova-common').events; + +var PLATFORM = 'android'; + +function setupEvents (externalEventEmitter) { + if (externalEventEmitter) { + // This will make the platform internal events visible outside + selfEvents.forwardEventsTo(externalEventEmitter); + return externalEventEmitter; + } + + // There is no logger if external emitter is not present, + // so attach a console logger + CordovaLogger.get().subscribe(selfEvents); + return selfEvents; +} + +/** + * Class, that acts as abstraction over particular platform. Encapsulates the + * platform's properties and methods. + * + * Platform that implements own PlatformApi instance _should implement all + * prototype methods_ of this class to be fully compatible with cordova-lib. + * + * The PlatformApi instance also should define the following field: + * + * * platform: String that defines a platform name. + */ +function Api (platform, platformRootDir, events) { + this.platform = PLATFORM; + this.root = path.resolve(__dirname, '..'); + this.builder = 'gradle'; + + setupEvents(events); + + var self = this; + + this.locations = { + root: self.root, + www: path.join(self.root, 'assets/www'), + res: path.join(self.root, 'res'), + platformWww: path.join(self.root, 'platform_www'), + configXml: path.join(self.root, 'res/xml/config.xml'), + defaultConfigXml: path.join(self.root, 'cordova/defaults.xml'), + strings: path.join(self.root, 'res/values/strings.xml'), + manifest: path.join(self.root, 'AndroidManifest.xml'), + build: path.join(self.root, 'build'), + javaSrc: path.join(self.root, 'src'), + // NOTE: Due to platformApi spec we need to return relative paths here + cordovaJs: 'bin/templates/project/assets/www/cordova.js', + cordovaJsSrc: 'cordova-js-src' + }; + + // XXX Override some locations for Android Studio projects + if (AndroidStudio.isAndroidStudioProject(self.root) === true) { + selfEvents.emit('log', 'Android Studio project detected'); + this.builder = 'studio'; + this.android_studio = true; + this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml'); + this.locations.strings = path.join(self.root, 'app/src/main/res/values/strings.xml'); + this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml'); + // We could have Java Source, we could have other languages + this.locations.javaSrc = path.join(self.root, 'app/src/main/java/'); + this.locations.www = path.join(self.root, 'app/src/main/assets/www'); + this.locations.res = path.join(self.root, 'app/src/main/res'); + } +} + +/** + * Installs platform to specified directory and creates a platform project. + * + * @param {String} destination Destination directory, where insatll platform to + * @param {ConfigParser} [config] ConfgiParser instance, used to retrieve + * project creation options, such as package id and project name. + * @param {Object} [options] An options object. The most common options are: + * @param {String} [options.customTemplate] A path to custom template, that + * should override the default one from platform. + * @param {Boolean} [options.link] Flag that indicates that platform's + * sources will be linked to installed platform instead of copying. + * @param {EventEmitter} [events] An EventEmitter instance that will be used for + * logging purposes. If no EventEmitter provided, all events will be logged to + * console + * + * @return {Promise} Promise either fulfilled with PlatformApi + * instance or rejected with CordovaError. + */ +Api.createPlatform = function (destination, config, options, events) { + events = setupEvents(events); + var result; + try { + result = require('../../lib/create').create(destination, config, options, events).then(function (destination) { + var PlatformApi = require(path.resolve(destination, 'cordova/Api')); + return new PlatformApi(PLATFORM, destination, events); + }); + } catch (e) { + events.emit('error', 'createPlatform is not callable from the android project API.'); + throw (e); + } + return result; +}; + +/** + * Updates already installed platform. + * + * @param {String} destination Destination directory, where platform installed + * @param {Object} [options] An options object. The most common options are: + * @param {String} [options.customTemplate] A path to custom template, that + * should override the default one from platform. + * @param {Boolean} [options.link] Flag that indicates that platform's + * sources will be linked to installed platform instead of copying. + * @param {EventEmitter} [events] An EventEmitter instance that will be used for + * logging purposes. If no EventEmitter provided, all events will be logged to + * console + * + * @return {Promise} Promise either fulfilled with PlatformApi + * instance or rejected with CordovaError. + */ +Api.updatePlatform = function (destination, options, events) { + events = setupEvents(events); + var result; + try { + result = require('../../lib/create').update(destination, options, events).then(function (destination) { + var PlatformApi = require(path.resolve(destination, 'cordova/Api')); + return new PlatformApi('android', destination, events); + }); + } catch (e) { + events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.'); + throw (e); + } + return result; +}; + +/** + * Gets a CordovaPlatform object, that represents the platform structure. + * + * @return {CordovaPlatform} A structure that contains the description of + * platform's file structure and other properties of platform. + */ +Api.prototype.getPlatformInfo = function () { + var result = {}; + result.locations = this.locations; + result.root = this.root; + result.name = this.platform; + result.version = require('./version'); + result.projectConfig = this._config; + + return result; +}; + +/** + * Updates installed platform with provided www assets and new app + * configuration. This method is required for CLI workflow and will be called + * each time before build, so the changes, made to app configuration and www + * code, will be applied to platform. + * + * @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a + * project structure and configuration, that should be applied to platform + * (contains project's www location and ConfigParser instance for project's + * config). + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ +Api.prototype.prepare = function (cordovaProject, prepareOptions) { + return require('./lib/prepare').prepare.call(this, cordovaProject, prepareOptions); +}; + +/** + * Installs a new plugin into platform. This method only copies non-www files + * (sources, libs, etc.) to platform. It also doesn't resolves the + * dependencies of plugin. Both of handling of www files, such as assets and + * js-files and resolving dependencies are the responsibility of caller. + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * @param {Object} installOptions An options object. Possible options below: + * @param {Boolean} installOptions.link: Flag that specifies that plugin + * sources will be symlinked to app's directory instead of copying (if + * possible). + * @param {Object} installOptions.variables An object that represents + * variables that will be used to install plugin. See more details on plugin + * variables in documentation: + * https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ +Api.prototype.addPlugin = function (plugin, installOptions) { + var project = AndroidProject.getProjectFile(this.root); + var self = this; + + installOptions = installOptions || {}; + installOptions.variables = installOptions.variables || {}; + // Add PACKAGE_NAME variable into vars + if (!installOptions.variables.PACKAGE_NAME) { + installOptions.variables.PACKAGE_NAME = project.getPackageName(); + } + + if (this.android_studio === true) { + installOptions.android_studio = true; + } + + return Q().then(function () { + // CB-11964: Do a clean when installing the plugin code to get around + // the Gradle bug introduced by the Android Gradle Plugin Version 2.2 + // TODO: Delete when the next version of Android Gradle plugin comes out + // Since clean doesn't just clean the build, it also wipes out www, we need + // to pass additional options. + + // Do some basic argument parsing + var opts = {}; + + // Skip cleaning prepared files when not invoking via cordova CLI. + opts.noPrepare = true; + + if (!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) { + return self.clean(opts); + } + }).then(function () { + return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions); + }).then(function () { + if (plugin.getFrameworks(this.platform).length === 0) return; + selfEvents.emit('verbose', 'Updating build files since android plugin contained '); + // This should pick the correct builder, not just get gradle + require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles(); + }.bind(this)) + // CB-11022 Return truthy value to prevent running prepare after + .thenResolve(true); +}; + +/** + * Removes an installed plugin from platform. + * + * Since method accepts PluginInfo instance as input parameter instead of plugin + * id, caller shoud take care of managing/storing PluginInfo instances for + * future uninstalls. + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ +Api.prototype.removePlugin = function (plugin, uninstallOptions) { + var project = AndroidProject.getProjectFile(this.root); + + if (uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) { + uninstallOptions.usePlatformWww = false; + uninstallOptions.android_studio = true; + } + + return PluginManager.get(this.platform, this.locations, project) + .removePlugin(plugin, uninstallOptions) + .then(function () { + if (plugin.getFrameworks(this.platform).length === 0) return; + + selfEvents.emit('verbose', 'Updating build files since android plugin contained '); + require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles(); + }.bind(this)) + // CB-11022 Return truthy value to prevent running prepare after + .thenResolve(true); +}; + +/** + * Builds an application package for current platform. + * + * @param {Object} buildOptions A build options. This object's structure is + * highly depends on platform's specific. The most common options are: + * @param {Boolean} buildOptions.debug Indicates that packages should be + * built with debug configuration. This is set to true by default unless the + * 'release' option is not specified. + * @param {Boolean} buildOptions.release Indicates that packages should be + * built with release configuration. If not set to true, debug configuration + * will be used. + * @param {Boolean} buildOptions.device Specifies that built app is intended + * to run on device + * @param {Boolean} buildOptions.emulator: Specifies that built app is + * intended to run on emulator + * @param {String} buildOptions.target Specifies the device id that will be + * used to run built application. + * @param {Boolean} buildOptions.nobuild Indicates that this should be a + * dry-run call, so no build artifacts will be produced. + * @param {String[]} buildOptions.archs Specifies chip architectures which + * app packages should be built for. List of valid architectures is depends on + * platform. + * @param {String} buildOptions.buildConfig The path to build configuration + * file. The format of this file is depends on platform. + * @param {String[]} buildOptions.argv Raw array of command-line arguments, + * passed to `build` command. The purpose of this property is to pass a + * platform-specific arguments, and eventually let platform define own + * arguments processing logic. + * + * @return {Promise} A promise either fulfilled with an array of build + * artifacts (application packages) if package was built successfully, + * or rejected with CordovaError. The resultant build artifact objects is not + * strictly typed and may conatin arbitrary set of fields as in sample below. + * + * { + * architecture: 'x86', + * buildType: 'debug', + * path: '/path/to/build', + * type: 'app' + * } + * + * The return value in most cases will contain only one item but in some cases + * there could be multiple items in output array, e.g. when multiple + * arhcitectures is specified. + */ +Api.prototype.build = function (buildOptions) { + var self = this; + if (this.android_studio) { + buildOptions.studio = true; + } + return require('./lib/check_reqs').run().then(function () { + return require('./lib/build').run.call(self, buildOptions); + }).then(function (buildResults) { + // Cast build result to array of build artifacts + return buildResults.apkPaths.map(function (apkPath) { + return { + buildType: buildResults.buildType, + buildMethod: buildResults.buildMethod, + path: apkPath, + type: 'apk' + }; + }); + }); +}; + +/** + * Builds an application package for current platform and runs it on + * specified/default device. If no 'device'/'emulator'/'target' options are + * specified, then tries to run app on default device if connected, otherwise + * runs the app on emulator. + * + * @param {Object} runOptions An options object. The structure is the same + * as for build options. + * + * @return {Promise} A promise either fulfilled if package was built and ran + * successfully, or rejected with CordovaError. + */ +Api.prototype.run = function (runOptions) { + var self = this; + return require('./lib/check_reqs').run().then(function () { + return require('./lib/run').run.call(self, runOptions); + }); +}; + +/** + * Cleans out the build artifacts from platform's directory, and also + * cleans out the platform www directory if called without options specified. + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError. + */ +Api.prototype.clean = function (cleanOptions) { + var self = this; + if (this.android_studio) { + // This will lint, checking for null won't + if (typeof cleanOptions === 'undefined') { + cleanOptions = {}; + } + cleanOptions.studio = true; + } + + return require('./lib/check_reqs').run().then(function () { + return require('./lib/build').runClean.call(self, cleanOptions); + }).then(function () { + return require('./lib/prepare').clean.call(self, cleanOptions); + }); +}; + +/** + * Performs a requirements check for current platform. Each platform defines its + * own set of requirements, which should be resolved before platform can be + * built successfully. + * + * @return {Promise} Promise, resolved with set of Requirement + * objects for current platform. + */ +Api.prototype.requirements = function () { + return require('./lib/check_reqs').check_all(); +}; + +module.exports = Api; diff --git a/platforms/android/cordova/android_sdk_version b/platforms/android/cordova/android_sdk_version new file mode 100755 index 0000000..34ed28f --- /dev/null +++ b/platforms/android/cordova/android_sdk_version @@ -0,0 +1,29 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var android_sdk = require('./lib/android_sdk'); + +android_sdk.print_newest_available_sdk_target().done(null, function(err) { + console.error(err); + process.exit(2); +}); + + diff --git a/platforms/android/cordova/android_sdk_version.bat b/platforms/android/cordova/android_sdk_version.bat new file mode 100644 index 0000000..a6bc104 --- /dev/null +++ b/platforms/android/cordova/android_sdk_version.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0android_sdk_version" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2 + EXIT /B 1 +) diff --git a/platforms/android/cordova/build b/platforms/android/cordova/build new file mode 100755 index 0000000..222e84a --- /dev/null +++ b/platforms/android/cordova/build @@ -0,0 +1,50 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var args = process.argv; +var Api = require('./Api'); +var nopt = require('nopt'); +var path = require('path'); + +// Support basic help commands +if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) + require('./lib/build').help(); + +// Do some basic argument parsing +var buildOpts = nopt({ + 'verbose' : Boolean, + 'silent' : Boolean, + 'debug' : Boolean, + 'release' : Boolean, + 'nobuild': Boolean, + 'buildConfig' : path +}, { 'd' : '--verbose' }); + +// Make buildOptions compatible with PlatformApi build method spec +buildOpts.argv = buildOpts.argv.original; + +require('./loggingHelper').adjustLoggerLevel(buildOpts); + +new Api().build(buildOpts) +.catch(function(err) { + console.error(err.stack); + process.exit(2); +}); diff --git a/platforms/android/cordova/build.bat b/platforms/android/cordova/build.bat new file mode 100644 index 0000000..46e966a --- /dev/null +++ b/platforms/android/cordova/build.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0build" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'build' script in 'cordova' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/check_reqs b/platforms/android/cordova/check_reqs new file mode 100755 index 0000000..372a383 --- /dev/null +++ b/platforms/android/cordova/check_reqs @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var check_reqs = require('./lib/check_reqs'); + +check_reqs.run().done( + function success() { + console.log('Looks like your environment fully supports cordova-android development!'); + }, function fail(err) { + console.log(err); + process.exit(2); + } +); diff --git a/platforms/android/cordova/check_reqs.bat b/platforms/android/cordova/check_reqs.bat new file mode 100644 index 0000000..846dfa1 --- /dev/null +++ b/platforms/android/cordova/check_reqs.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0check_reqs" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2 + EXIT /B 1 +) diff --git a/platforms/android/cordova/clean b/platforms/android/cordova/clean new file mode 100755 index 0000000..22065cc --- /dev/null +++ b/platforms/android/cordova/clean @@ -0,0 +1,51 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var Api = require('./Api'); +var path = require('path'); +var nopt = require('nopt'); + +// Support basic help commands +if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) { + console.log('Usage: ' + path.relative(process.cwd(), process.argv[1])); + console.log('Cleans the project directory.'); + process.exit(0); +} + +// Do some basic argument parsing +var opts = nopt({ + 'verbose' : Boolean, + 'silent' : Boolean +}, { 'd' : '--verbose' }); + +// Make buildOptions compatible with PlatformApi clean method spec +opts.argv = opts.argv.original; + +// Skip cleaning prepared files when not invoking via cordova CLI. +opts.noPrepare = true; + +require('./loggingHelper').adjustLoggerLevel(opts); + +new Api().clean(opts) +.catch(function(err) { + console.error(err.stack); + process.exit(2); +}); diff --git a/platforms/android/cordova/clean.bat b/platforms/android/cordova/clean.bat new file mode 100644 index 0000000..445ef6e --- /dev/null +++ b/platforms/android/cordova/clean.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0clean" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'clean' script in 'cordova' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/defaults.xml b/platforms/android/cordova/defaults.xml new file mode 100644 index 0000000..5286ab9 --- /dev/null +++ b/platforms/android/cordova/defaults.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/platforms/android/cordova/lib/Adb.js b/platforms/android/cordova/lib/Adb.js new file mode 100644 index 0000000..038c67c --- /dev/null +++ b/platforms/android/cordova/lib/Adb.js @@ -0,0 +1,101 @@ +/** + 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. +*/ + +var Q = require('q'); +var os = require('os'); +var events = require('cordova-common').events; +var spawn = require('cordova-common').superspawn.spawn; +var CordovaError = require('cordova-common').CordovaError; + +var Adb = {}; + +function isDevice (line) { + return line.match(/\w+\tdevice/) && !line.match(/emulator/); +} + +function isEmulator (line) { + return line.match(/device/) && line.match(/emulator/); +} + +/** + * Lists available/connected devices and emulators + * + * @param {Object} opts Various options + * @param {Boolean} opts.emulators Specifies whether this method returns + * emulators only + * + * @return {Promise} list of available/connected + * devices/emulators + */ +Adb.devices = function (opts) { + return spawn('adb', ['devices'], {cwd: os.tmpdir()}).then(function (output) { + return output.split('\n').filter(function (line) { + // Filter out either real devices or emulators, depending on options + return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line); + }).map(function (line) { + return line.replace(/\tdevice/, '').replace('\r', ''); + }); + }); +}; + +Adb.install = function (target, packagePath, opts) { + events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...'); + var args = ['-s', target, 'install']; + if (opts && opts.replace) args.push('-r'); + return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}).then(function (output) { + // 'adb install' seems to always returns no error, even if installation fails + // so we catching output to detect installation failure + if (output.match(/Failure/)) { + if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) { + output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' + + ' or sign and deploy the unsigned apk manually using Android tools.'; + } else if (output.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) { + output += '\n\n' + 'You\'re trying to install apk with a lower versionCode that is already installed.' + + '\nEither uninstall an app or increment the versionCode.'; + } + + return Q.reject(new CordovaError('Failed to install apk to device: ' + output)); + } + }); +}; + +Adb.uninstall = function (target, packageId) { + events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...'); + return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()}); +}; + +Adb.shell = function (target, shellCommand) { + events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...'); + var args = ['-s', target, 'shell']; + shellCommand = shellCommand.split(/\s+/); + return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}).catch(function (output) { + return Q.reject(new CordovaError('Failed to execute shell command "' + + shellCommand + '"" on device: ' + output)); + }); +}; + +Adb.start = function (target, activityName) { + events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...'); + return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) { + return Q.reject(new CordovaError('Failed to start application "' + + activityName + '"" on device: ' + output)); + }); +}; + +module.exports = Adb; diff --git a/platforms/android/cordova/lib/AndroidManifest.js b/platforms/android/cordova/lib/AndroidManifest.js new file mode 100644 index 0000000..5b7077a --- /dev/null +++ b/platforms/android/cordova/lib/AndroidManifest.js @@ -0,0 +1,160 @@ +/** + 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. +*/ + +var fs = require('fs'); +var et = require('elementtree'); +var xml = require('cordova-common').xmlHelpers; + +var DEFAULT_ORIENTATION = 'default'; + +/** Wraps an AndroidManifest file */ +function AndroidManifest (path) { + this.path = path; + this.doc = xml.parseElementtreeSync(path); + if (this.doc.getroot().tag !== 'manifest') { + throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")'); + } +} + +AndroidManifest.prototype.getVersionName = function () { + return this.doc.getroot().attrib['android:versionName']; +}; + +AndroidManifest.prototype.setVersionName = function (versionName) { + this.doc.getroot().attrib['android:versionName'] = versionName; + return this; +}; + +AndroidManifest.prototype.getVersionCode = function () { + return this.doc.getroot().attrib['android:versionCode']; +}; + +AndroidManifest.prototype.setVersionCode = function (versionCode) { + this.doc.getroot().attrib['android:versionCode'] = versionCode; + return this; +}; + +AndroidManifest.prototype.getPackageId = function () { + /* jshint -W069 */ + return this.doc.getroot().attrib['package']; + /* jshint +W069 */ +}; + +AndroidManifest.prototype.setPackageId = function (pkgId) { + /* jshint -W069 */ + this.doc.getroot().attrib['package'] = pkgId; + /* jshint +W069 */ + return this; +}; + +AndroidManifest.prototype.getActivity = function () { + var activity = this.doc.getroot().find('./application/activity'); + return { + getName: function () { + return activity.attrib['android:name']; + }, + setName: function (name) { + if (!name) { + delete activity.attrib['android:name']; + } else { + activity.attrib['android:name'] = name; + } + return this; + }, + getOrientation: function () { + return activity.attrib['android:screenOrientation']; + }, + setOrientation: function (orientation) { + if (!orientation || orientation.toLowerCase() === DEFAULT_ORIENTATION) { + delete activity.attrib['android:screenOrientation']; + } else { + activity.attrib['android:screenOrientation'] = orientation; + } + return this; + }, + getLaunchMode: function () { + return activity.attrib['android:launchMode']; + }, + setLaunchMode: function (launchMode) { + if (!launchMode) { + delete activity.attrib['android:launchMode']; + } else { + activity.attrib['android:launchMode'] = launchMode; + } + return this; + } + }; +}; + +['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) { + // Copy variable reference to avoid closure issues + var prefName = sdkPrefName; + + AndroidManifest.prototype['get' + capitalize(prefName)] = function () { + var usesSdk = this.doc.getroot().find('./uses-sdk'); + return usesSdk && usesSdk.attrib['android:' + prefName]; + }; + + AndroidManifest.prototype['set' + capitalize(prefName)] = function (prefValue) { + var usesSdk = this.doc.getroot().find('./uses-sdk'); + + if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first + usesSdk = new et.Element('uses-sdk'); + this.doc.getroot().append(usesSdk); + } + + if (prefValue) { + usesSdk.attrib['android:' + prefName] = prefValue; + } + + return this; + }; +}); + +AndroidManifest.prototype.getDebuggable = function () { + return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true'; +}; + +AndroidManifest.prototype.setDebuggable = function (value) { + var application = this.doc.getroot().find('./application'); + if (value) { + application.attrib['android:debuggable'] = 'true'; + } else { + // The default value is "false", so we can remove attribute at all. + delete application.attrib['android:debuggable']; + } + return this; +}; + +/** + * Writes manifest to disk syncronously. If filename is specified, then manifest + * will be written to that file + * + * @param {String} [destPath] File to write manifest to. If omitted, + * manifest will be written to file it has been read from. + */ +AndroidManifest.prototype.write = function (destPath) { + fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8'); +}; + +module.exports = AndroidManifest; + +function capitalize (str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} diff --git a/platforms/android/cordova/lib/AndroidProject.js b/platforms/android/cordova/lib/AndroidProject.js new file mode 100644 index 0000000..bf55cad --- /dev/null +++ b/platforms/android/cordova/lib/AndroidProject.js @@ -0,0 +1,209 @@ +/** + 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. +*/ + +var fs = require('fs'); +var path = require('path'); +var properties_parser = require('properties-parser'); +var AndroidManifest = require('./AndroidManifest'); +var AndroidStudio = require('./AndroidStudio'); +var pluginHandlers = require('./pluginHandlers'); + +var projectFileCache = {}; + +function addToPropertyList (projectProperties, key, value) { + var i = 1; + while (projectProperties.get(key + '.' + i)) { i++; } + + projectProperties.set(key + '.' + i, value); + projectProperties.dirty = true; +} + +function removeFromPropertyList (projectProperties, key, value) { + var i = 1; + var currentValue; + while ((currentValue = projectProperties.get(key + '.' + i))) { + if (currentValue === value) { + while ((currentValue = projectProperties.get(key + '.' + (i + 1)))) { + projectProperties.set(key + '.' + i, currentValue); + i++; + } + projectProperties.set(key + '.' + i); + break; + } + i++; + } + projectProperties.dirty = true; +} + +function getRelativeLibraryPath (parentDir, subDir) { + var libraryPath = path.relative(parentDir, subDir); + return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath; +} + +function AndroidProject (projectDir) { + this._propertiesEditors = {}; + this._subProjectDirs = {}; + this._dirty = false; + this.projectDir = projectDir; + this.platformWww = path.join(this.projectDir, 'platform_www'); + this.www = path.join(this.projectDir, 'assets/www'); + if (AndroidStudio.isAndroidStudioProject(projectDir) === true) { + this.www = path.join(this.projectDir, 'app/src/main/assets/www'); + } +} + +AndroidProject.getProjectFile = function (projectDir) { + if (!projectFileCache[projectDir]) { + projectFileCache[projectDir] = new AndroidProject(projectDir); + } + + return projectFileCache[projectDir]; +}; + +AndroidProject.purgeCache = function (projectDir) { + if (projectDir) { + delete projectFileCache[projectDir]; + } else { + projectFileCache = {}; + } +}; + +/** + * Reads the package name out of the Android Manifest file + * + * @param {String} projectDir The absolute path to the directory containing the project + * + * @return {String} The name of the package + */ +AndroidProject.prototype.getPackageName = function () { + var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml'); + if (AndroidStudio.isAndroidStudioProject(this.projectDir) === true) { + manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml'); + } + return new AndroidManifest(manifestPath).getPackageId(); +}; + +AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, src) { + // All custom subprojects are prefixed with the last portion of the package id. + // This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name. + var packageName = this.getPackageName(); + var lastDotIndex = packageName.lastIndexOf('.'); + var prefix = packageName.substring(lastDotIndex + 1); + var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src)); + return subRelativeDir; +}; + +AndroidProject.prototype.addSubProject = function (parentDir, subDir) { + var parentProjectFile = path.resolve(parentDir, 'project.properties'); + var subProjectFile = path.resolve(subDir, 'project.properties'); + var parentProperties = this._getPropertiesFile(parentProjectFile); + // TODO: Setting the target needs to happen only for pre-3.7.0 projects + if (fs.existsSync(subProjectFile)) { + var subProperties = this._getPropertiesFile(subProjectFile); + subProperties.set('target', parentProperties.get('target')); + subProperties.dirty = true; + this._subProjectDirs[subDir] = true; + } + addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir)); + + this._dirty = true; +}; + +AndroidProject.prototype.removeSubProject = function (parentDir, subDir) { + var parentProjectFile = path.resolve(parentDir, 'project.properties'); + var parentProperties = this._getPropertiesFile(parentProjectFile); + removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir)); + delete this._subProjectDirs[subDir]; + this._dirty = true; +}; + +AndroidProject.prototype.addGradleReference = function (parentDir, subDir) { + var parentProjectFile = path.resolve(parentDir, 'project.properties'); + var parentProperties = this._getPropertiesFile(parentProjectFile); + addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir)); + this._dirty = true; +}; + +AndroidProject.prototype.removeGradleReference = function (parentDir, subDir) { + var parentProjectFile = path.resolve(parentDir, 'project.properties'); + var parentProperties = this._getPropertiesFile(parentProjectFile); + removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir)); + this._dirty = true; +}; + +AndroidProject.prototype.addSystemLibrary = function (parentDir, value) { + var parentProjectFile = path.resolve(parentDir, 'project.properties'); + var parentProperties = this._getPropertiesFile(parentProjectFile); + addToPropertyList(parentProperties, 'cordova.system.library', value); + this._dirty = true; +}; + +AndroidProject.prototype.removeSystemLibrary = function (parentDir, value) { + var parentProjectFile = path.resolve(parentDir, 'project.properties'); + var parentProperties = this._getPropertiesFile(parentProjectFile); + removeFromPropertyList(parentProperties, 'cordova.system.library', value); + this._dirty = true; +}; + +AndroidProject.prototype.write = function () { + if (!this._dirty) { + return; + } + this._dirty = false; + + for (var filename in this._propertiesEditors) { + var editor = this._propertiesEditors[filename]; + if (editor.dirty) { + fs.writeFileSync(filename, editor.toString()); + editor.dirty = false; + } + } +}; + +AndroidProject.prototype._getPropertiesFile = function (filename) { + if (!this._propertiesEditors[filename]) { + if (fs.existsSync(filename)) { + this._propertiesEditors[filename] = properties_parser.createEditor(filename); + } else { + this._propertiesEditors[filename] = properties_parser.createEditor(); + } + } + + return this._propertiesEditors[filename]; +}; + +AndroidProject.prototype.getInstaller = function (type) { + return pluginHandlers.getInstaller(type); +}; + +AndroidProject.prototype.getUninstaller = function (type) { + return pluginHandlers.getUninstaller(type); +}; + +/* + * This checks if an Android project is clean or has old build artifacts + */ + +AndroidProject.prototype.isClean = function () { + var build_path = path.join(this.projectDir, 'build'); + // If the build directory doesn't exist, it's clean + return !(fs.existsSync(build_path)); +}; + +module.exports = AndroidProject; diff --git a/platforms/android/cordova/lib/AndroidStudio.js b/platforms/android/cordova/lib/AndroidStudio.js new file mode 100644 index 0000000..fbcb926 --- /dev/null +++ b/platforms/android/cordova/lib/AndroidStudio.js @@ -0,0 +1,42 @@ +/* + * This is a simple routine that checks if project is an Android Studio Project + * + * @param {String} root Root folder of the project + */ + +/* jshint esnext: false */ + +var path = require('path'); +var fs = require('fs'); +var CordovaError = require('cordova-common').CordovaError; + +module.exports.isAndroidStudioProject = function isAndroidStudioProject (root) { + var eclipseFiles = ['AndroidManifest.xml', 'libs', 'res']; + var androidStudioFiles = ['app', 'app/src/main']; + + // assume it is an AS project and not an Eclipse project + var isEclipse = false; + var isAS = true; + + if (!fs.existsSync(root)) { + throw new CordovaError('AndroidStudio.js:inAndroidStudioProject root does not exist: ' + root); + } + + // if any of the following exists, then we are not an ASProj + eclipseFiles.forEach(function (file) { + if (fs.existsSync(path.join(root, file))) { + isEclipse = true; + } + }); + + // if it is NOT an eclipse project, check that all required files exist + if (!isEclipse) { + androidStudioFiles.forEach(function (file) { + if (!fs.existsSync(path.join(root, file))) { + console.log('missing file :: ' + file); + isAS = false; + } + }); + } + return (!isEclipse && isAS); +}; diff --git a/platforms/android/cordova/lib/android_sdk.js b/platforms/android/cordova/lib/android_sdk.js new file mode 100755 index 0000000..148f9f3 --- /dev/null +++ b/platforms/android/cordova/lib/android_sdk.js @@ -0,0 +1,102 @@ +/* + 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. +*/ + +var Q = require('q'); +var superspawn = require('cordova-common').superspawn; + +var suffix_number_regex = /(\d+)$/; +// Used for sorting Android targets, example strings to sort: +// android-19 +// android-L +// Google Inc.:Google APIs:20 +// Google Inc.:Glass Development Kit Preview:20 +// The idea is to sort based on largest "suffix" number - meaning the bigger +// the number at the end, the more recent the target, the closer to the +// start of the array. +function sort_by_largest_numerical_suffix (a, b) { + var suffix_a = a.match(suffix_number_regex); + var suffix_b = b.match(suffix_number_regex); + if (suffix_a && suffix_b) { + // If the two targets being compared have suffixes, return less than + // zero, or greater than zero, based on which suffix is larger. + return (parseInt(suffix_a[1]) > parseInt(suffix_b[1]) ? -1 : 1); + } else { + // If no suffix numbers were detected, leave the order as-is between + // elements a and b. + return 0; + } +} + +module.exports.print_newest_available_sdk_target = function () { + return module.exports.list_targets().then(function (targets) { + targets.sort(sort_by_largest_numerical_suffix); + console.log(targets[0]); + }); +}; + +module.exports.version_string_to_api_level = { + '4.0': 14, + '4.0.3': 15, + '4.1': 16, + '4.2': 17, + '4.3': 18, + '4.4': 19, + '4.4W': 20, + '5.0': 21, + '5.1': 22, + '6.0': 23, + '7.0': 24, + '7.1.1': 25, + '8.0': 26 +}; + +function parse_targets (output) { + var target_out = output.split('\n'); + var targets = []; + for (var i = target_out.length - 1; i >= 0; i--) { + if (target_out[i].match(/id:/)) { // if "id:" is in the line... + targets.push(target_out[i].match(/"(.+)"/)[1]); // .. match whatever is in quotes. + } + } + return targets; +} + +module.exports.list_targets_with_android = function () { + return superspawn.spawn('android', ['list', 'target']).then(parse_targets); +}; + +module.exports.list_targets_with_avdmanager = function () { + return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets); +}; + +module.exports.list_targets = function () { + return module.exports.list_targets_with_avdmanager().catch(function (err) { + // If there's an error, like avdmanager could not be found, we can try + // as a last resort, to run `android`, in case this is a super old + // SDK installation. + if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) { + return module.exports.list_targets_with_android(); + } else throw err; + }).then(function (targets) { + if (targets.length === 0) { + return Q.reject(new Error('No android targets (SDKs) installed!')); + } + return targets; + }); +}; diff --git a/platforms/android/cordova/lib/build.js b/platforms/android/cordova/lib/build.js new file mode 100644 index 0000000..e33cfae --- /dev/null +++ b/platforms/android/cordova/lib/build.js @@ -0,0 +1,294 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var Q = require('q'); +var path = require('path'); +var fs = require('fs'); +var nopt = require('nopt'); + +var Adb = require('./Adb'); + +var builders = require('./builders/builders'); +var events = require('cordova-common').events; +var spawn = require('cordova-common').superspawn.spawn; +var CordovaError = require('cordova-common').CordovaError; + +function parseOpts (options, resolvedTarget, projectRoot) { + options = options || {}; + options.argv = nopt({ + gradle: Boolean, + studio: Boolean, + prepenv: Boolean, + versionCode: String, + minSdkVersion: String, + gradleArg: [String, Array], + keystore: path, + alias: String, + storePassword: String, + password: String, + keystoreType: String + }, {}, options.argv, 0); + + // Android Studio Build method is the default + var ret = { + buildType: options.release ? 'release' : 'debug', + buildMethod: process.env.ANDROID_BUILD || 'studio', + prepEnv: options.argv.prepenv, + arch: resolvedTarget && resolvedTarget.arch, + extraArgs: [] + }; + + if (options.argv.gradle || options.argv.studio) { + ret.buildMethod = options.argv.studio ? 'studio' : 'gradle'; + } + + // This comes from cordova/run + if (options.studio) ret.buildMethod = 'studio'; + if (options.gradle) ret.buildMethod = 'gradle'; + + if (options.nobuild) ret.buildMethod = 'none'; + + if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); } + + if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); } + + if (options.argv.gradleArg) { + ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg); + } + + var packageArgs = {}; + + if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); } + + ['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) { + if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; } + }); + + var buildConfig = options.buildConfig; + + // If some values are not specified as command line arguments - use build config to supplement them. + // Command line arguemnts have precedence over build config. + if (buildConfig) { + if (!fs.existsSync(buildConfig)) { + throw new Error('Specified build config file does not exist: ' + buildConfig); + } + events.emit('log', 'Reading build config file: ' + path.resolve(buildConfig)); + var buildjson = fs.readFileSync(buildConfig, 'utf8'); + var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM + if (config.android && config.android[ret.buildType]) { + var androidInfo = config.android[ret.buildType]; + if (androidInfo.keystore && !packageArgs.keystore) { + if (androidInfo.keystore.substr(0, 1) === '~') { + androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1); + } + packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore); + events.emit('log', 'Reading the keystore from: ' + packageArgs.keystore); + } + + ['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (key) { + packageArgs[key] = packageArgs[key] || androidInfo[key]; + }); + } + } + + if (packageArgs.keystore && packageArgs.alias) { + ret.packageInfo = new PackageInfo(packageArgs.keystore, packageArgs.alias, packageArgs.storePassword, + packageArgs.password, packageArgs.keystoreType); + } + + if (!ret.packageInfo) { + if (Object.keys(packageArgs).length > 0) { + events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.'); + } + } + + return ret; +} + +/* + * Builds the project with the specifed options + * Returns a promise. + */ +module.exports.runClean = function (options) { + var opts = parseOpts(options, null, this.root); + var builder = builders.getBuilder(opts.buildMethod); + return builder.prepEnv(opts).then(function () { + return builder.clean(opts); + }); +}; + +/** + * Builds the project with the specifed options. + * + * @param {BuildOptions} options A set of options. See PlatformApi.build + * method documentation for reference. + * @param {Object} optResolvedTarget A deployment target. Used to pass + * target architecture from upstream 'run' call. TODO: remove this option in + * favor of setting buildOptions.archs field. + * + * @return {Promise} Promise, resolved with built packages + * information. + */ +module.exports.run = function (options, optResolvedTarget) { + var opts = parseOpts(options, optResolvedTarget, this.root); + console.log(opts.buildMethod); + var builder = builders.getBuilder(opts.buildMethod); + return builder.prepEnv(opts).then(function () { + if (opts.prepEnv) { + events.emit('verbose', 'Build file successfully prepared.'); + return; + } + return builder.build(opts).then(function () { + var apkPaths = builder.findOutputApks(opts.buildType, opts.arch); + events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t')); + return { + apkPaths: apkPaths, + buildType: opts.buildType, + buildMethod: opts.buildMethod + }; + }); + }); +}; + +/* + * Detects the architecture of a device/emulator + * Returns "arm" or "x86". + */ +module.exports.detectArchitecture = function (target) { + function helper () { + return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) { + return /intel/i.exec(output) ? 'x86' : 'arm'; + }); + } + // It sometimes happens (at least on OS X), that this command will hang forever. + // To fix it, either unplug & replug device, or restart adb server. + return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) { + if (/timed out/.exec('' + err)) { + // adb kill-server doesn't seem to do the trick. + // Could probably find a x-platform version of killall, but I'm not actually + // sure that this scenario even happens on non-OSX machines. + events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.'); + return spawn('killall', ['adb']).then(function () { + return helper().then(null, function () { + // The double kill is sadly often necessary, at least on mac. + events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.'); + return spawn('killall', ['adb']).then(function () { + return helper().then(null, function () { + return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.')); + }); + }); + }); + }, function () { + // For non-killall OS's. + return Q.reject(err); + }); + } + throw err; + }); +}; + +module.exports.findBestApkForArchitecture = function (buildResults, arch) { + var paths = buildResults.apkPaths.filter(function (p) { + var apkName = path.basename(p); + if (buildResults.buildType === 'debug') { + return /-debug/.exec(apkName); + } + return !/-debug/.exec(apkName); + }); + var archPattern = new RegExp('-' + arch); + var hasArchPattern = /-x86|-arm/; + for (var i = 0; i < paths.length; ++i) { + var apkName = path.basename(paths[i]); + if (hasArchPattern.exec(apkName)) { + if (archPattern.exec(apkName)) { + return paths[i]; + } + } else { + return paths[i]; + } + } + throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType); +}; + +function PackageInfo (keystore, alias, storePassword, password, keystoreType) { + this.keystore = { + 'name': 'key.store', + 'value': keystore + }; + this.alias = { + 'name': 'key.alias', + 'value': alias + }; + if (storePassword) { + this.storePassword = { + 'name': 'key.store.password', + 'value': storePassword + }; + } + if (password) { + this.password = { + 'name': 'key.alias.password', + 'value': password + }; + } + if (keystoreType) { + this.keystoreType = { + 'name': 'key.store.type', + 'value': keystoreType + }; + } +} + +PackageInfo.prototype = { + toProperties: function () { + var self = this; + var result = ''; + Object.keys(self).forEach(function (key) { + result += self[key].name; + result += '='; + result += self[key].value.replace(/\\/g, '\\\\'); + result += '\n'; + }); + return result; + } +}; + +module.exports.help = function () { + console.log('Usage: ' + path.relative(process.cwd(), path.join('../build')) + ' [flags] [Signed APK flags]'); + console.log('Flags:'); + console.log(' \'--debug\': will build project in debug mode (default)'); + console.log(' \'--release\': will build project for release'); + console.log(' \'--ant\': will build project with ant'); + console.log(' \'--gradle\': will build project with gradle (default)'); + console.log(' \'--nobuild\': will skip build process (useful when using run command)'); + console.log(' \'--prepenv\': don\'t build, but copy in build scripts where necessary'); + console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs. Requires --gradle.'); + console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build. Useful for uploading multiple APKs. Requires --gradle.'); + console.log(' \'--gradleArg=\': Extra args to pass to the gradle command. Use one flag per arg. Ex. --gradleArg=-PcdvBuildMultipleApks=true'); + console.log(''); + console.log('Signed APK flags (overwrites debug/release-signing.proprties) :'); + console.log(' \'--keystore=\': Key store used to build a signed archive. (Required)'); + console.log(' \'--alias=\': Alias for the key store. (Required)'); + console.log(' \'--storePassword=\': Password for the key store. (Optional - prompted)'); + console.log(' \'--password=\': Password for the key. (Optional - prompted)'); + console.log(' \'--keystoreType\': Type of the keystore. (Optional)'); + process.exit(0); +}; diff --git a/platforms/android/cordova/lib/builders/GenericBuilder.js b/platforms/android/cordova/lib/builders/GenericBuilder.js new file mode 100644 index 0000000..892aa38 --- /dev/null +++ b/platforms/android/cordova/lib/builders/GenericBuilder.js @@ -0,0 +1,124 @@ +/* + 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. +*/ +/* eslint no-self-assign: 0 */ +/* eslint no-unused-vars: 0 */ + +var Q = require('q'); +var fs = require('fs'); +var path = require('path'); +var shell = require('shelljs'); +var events = require('cordova-common').events; + +function GenericBuilder (projectDir) { + this.root = projectDir || path.resolve(__dirname, '../../..'); + this.binDirs = { + studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'), + gradle: path.join(this.root, 'build', 'outputs', 'apk') + }; +} + +GenericBuilder.prototype.prepEnv = function () { + return Q(); +}; + +GenericBuilder.prototype.build = function () { + events.emit('log', 'Skipping build...'); + return Q(null); +}; + +GenericBuilder.prototype.clean = function () { + return Q(); +}; + +GenericBuilder.prototype.findOutputApks = function (build_type, arch) { + var self = this; + return Object.keys(this.binDirs).reduce(function (result, builderName) { + var binDir = self.binDirs[builderName]; + return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch)); + }, []).sort(apkSorter); +}; + +module.exports = GenericBuilder; + +function apkSorter (fileA, fileB) { + // De-prioritize arch specific builds + var archSpecificRE = /-x86|-arm/; + if (archSpecificRE.exec(fileA)) { + return 1; + } else if (archSpecificRE.exec(fileB)) { + return -1; + } + + // De-prioritize unsigned builds + var unsignedRE = /-unsigned/; + if (unsignedRE.exec(fileA)) { + return 1; + } else if (unsignedRE.exec(fileB)) { + return -1; + } + + var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime; + return timeDiff === 0 ? fileA.length - fileB.length : timeDiff; +} + +function findOutputApksHelper (dir, build_type, arch) { + var shellSilent = shell.config.silent; + shell.config.silent = true; + + // list directory recursively + var ret = shell.ls('-R', dir).map(function (file) { + // ls does not include base directory + return path.join(dir, file); + }).filter(function (file) { + // find all APKs + return file.match(/\.apk?$/i); + }).filter(function (candidate) { + var apkName = path.basename(candidate); + // Need to choose between release and debug .apk. + if (build_type === 'debug') { + return /-debug/.exec(apkName) && !/-unaligned|-unsigned/.exec(apkName); + } + if (build_type === 'release') { + return /-release/.exec(apkName) && !/-unaligned/.exec(apkName); + } + return true; + }).sort(apkSorter); + + shellSilent = shellSilent; + + if (ret.length === 0) { + return ret; + } + // Assume arch-specific build if newest apk has -x86 or -arm. + var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0])); + // And show only arch-specific ones (or non-arch-specific) + ret = ret.filter(function (p) { + /* jshint -W018 */ + return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific; + /* jshint +W018 */ + }); + + if (archSpecific && ret.length > 1 && arch) { + ret = ret.filter(function (p) { + return path.basename(p).indexOf('-' + arch) !== -1; + }); + } + + return ret; +} diff --git a/platforms/android/cordova/lib/builders/GradleBuilder.js b/platforms/android/cordova/lib/builders/GradleBuilder.js new file mode 100644 index 0000000..8237c73 --- /dev/null +++ b/platforms/android/cordova/lib/builders/GradleBuilder.js @@ -0,0 +1,330 @@ +/* + 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. +*/ + +var Q = require('q'); +var fs = require('fs'); +var util = require('util'); +var path = require('path'); +var shell = require('shelljs'); +var superspawn = require('cordova-common').superspawn; +var CordovaError = require('cordova-common').CordovaError; +var check_reqs = require('../check_reqs'); + +var GenericBuilder = require('./GenericBuilder'); + +var MARKER = 'YOUR CHANGES WILL BE ERASED!'; +var SIGNING_PROPERTIES = '-signing.properties'; +var TEMPLATE = + '# This file is automatically generated.\n' + + '# Do not modify this file -- ' + MARKER + '\n'; + +function GradleBuilder (projectRoot) { + GenericBuilder.call(this, projectRoot); + + this.binDirs = { gradle: this.binDirs.gradle }; +} + +util.inherits(GradleBuilder, GenericBuilder); + +GradleBuilder.prototype.getArgs = function (cmd, opts) { + if (cmd === 'release') { + cmd = 'cdvBuildRelease'; + } else if (cmd === 'debug') { + cmd = 'cdvBuildDebug'; + } + var args = [cmd, '-b', path.join(this.root, 'build.gradle')]; + if (opts.arch) { + args.push('-PcdvBuildArch=' + opts.arch); + } + + // 10 seconds -> 6 seconds + args.push('-Dorg.gradle.daemon=true'); + // to allow dex in process + args.push('-Dorg.gradle.jvmargs=-Xmx2048m'); + // allow NDK to be used - required by Gradle 1.5 plugin + args.push('-Pandroid.useDeprecatedNdk=true'); + args.push.apply(args, opts.extraArgs); + // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet): + // args.push('-Dorg.gradle.parallel=true'); + return args; +}; + +/* + * This returns a promise + */ + +GradleBuilder.prototype.runGradleWrapper = function (gradle_cmd, gradle_file) { + var gradlePath = path.join(this.root, 'gradlew'); + gradle_file = path.join(this.root, (gradle_file || 'wrapper.gradle')); + if (fs.existsSync(gradlePath)) { + // Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows + } else { + return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', gradle_file], { stdio: 'pipe' }) + .progress(function (stdio) { + suppressJavaOptionsInfo(stdio); + }); + } +}; + +/* + * We need to kill this in a fire. + */ + +GradleBuilder.prototype.readProjectProperties = function () { + function findAllUniq (data, r) { + var s = {}; + var m; + while ((m = r.exec(data))) { + s[m[1]] = 1; + } + return Object.keys(s); + } + + var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8'); + return { + libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg), + gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg), + systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg) + }; +}; + +GradleBuilder.prototype.extractRealProjectNameFromManifest = function () { + var manifestPath = path.join(this.root, 'AndroidManifest.xml'); + var manifestData = fs.readFileSync(manifestPath, 'utf8'); + var m = /= 0) { + return check_reqs.check_android_target(error).then(function () { + // If due to some odd reason - check_android_target succeeds + // we should still fail here. + return Q.reject(error); + }); + } + return Q.reject(error); + }); +}; + +GradleBuilder.prototype.clean = function (opts) { + var builder = this; + var wrapper = path.join(this.root, 'gradlew'); + var args = builder.getArgs('clean', opts); + return Q().then(function () { + return superspawn.spawn(wrapper, args, { stdio: 'inherit' }); + }).then(function () { + shell.rm('-rf', path.join(builder.root, 'out')); + + ['debug', 'release'].forEach(function (config) { + var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES); + if (isAutoGenerated(propertiesFilePath)) { + shell.rm('-f', propertiesFilePath); + } + }); + }); +}; + +module.exports = GradleBuilder; + +function suppressJavaOptionsInfo (stdio) { + if (stdio.stderr) { + /* + * Workaround for the issue with Java printing some unwanted information to + * stderr instead of stdout. + * This function suppresses 'Picked up _JAVA_OPTIONS' message from being + * printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for + * explanation. + */ + var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString()); + if (suppressThisLine) { + return; + } + process.stderr.write(stdio.stderr); + } else { + process.stdout.write(stdio.stdout); + } +} + +function isAutoGenerated (file) { + return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0; +} diff --git a/platforms/android/cordova/lib/builders/StudioBuilder.js b/platforms/android/cordova/lib/builders/StudioBuilder.js new file mode 100644 index 0000000..8322e15 --- /dev/null +++ b/platforms/android/cordova/lib/builders/StudioBuilder.js @@ -0,0 +1,302 @@ +/* + 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. +*/ + +var Q = require('q'); +var fs = require('fs'); +var util = require('util'); +var path = require('path'); +var shell = require('shelljs'); +var spawn = require('cordova-common').superspawn.spawn; +var CordovaError = require('cordova-common').CordovaError; +var check_reqs = require('../check_reqs'); + +var GenericBuilder = require('./GenericBuilder'); + +var MARKER = 'YOUR CHANGES WILL BE ERASED!'; +var SIGNING_PROPERTIES = '-signing.properties'; +var TEMPLATE = + '# This file is automatically generated.\n' + + '# Do not modify this file -- ' + MARKER + '\n'; + +function StudioBuilder (projectRoot) { + GenericBuilder.call(this, projectRoot); + + this.binDirs = {gradle: this.binDirs.studio}; +} + +util.inherits(StudioBuilder, GenericBuilder); + +StudioBuilder.prototype.getArgs = function (cmd, opts) { + if (cmd === 'release') { + cmd = 'cdvBuildRelease'; + } else if (cmd === 'debug') { + cmd = 'cdvBuildDebug'; + } + var args = [cmd, '-b', path.join(this.root, 'build.gradle')]; + if (opts.arch) { + args.push('-PcdvBuildArch=' + opts.arch); + } + + // 10 seconds -> 6 seconds + args.push('-Dorg.gradle.daemon=true'); + // to allow dex in process + args.push('-Dorg.gradle.jvmargs=-Xmx2048m'); + // allow NDK to be used - required by Gradle 1.5 plugin + // args.push('-Pandroid.useDeprecatedNdk=true'); + args.push.apply(args, opts.extraArgs); + // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet): + // args.push('-Dorg.gradle.parallel=true'); + return args; +}; + +/* + * This returns a promise + */ + +StudioBuilder.prototype.runGradleWrapper = function (gradle_cmd) { + var gradlePath = path.join(this.root, 'gradlew'); + var wrapperGradle = path.join(this.root, 'wrapper.gradle'); + if (fs.existsSync(gradlePath)) { + // Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows + } else { + return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'}); + } +}; + +StudioBuilder.prototype.readProjectProperties = function () { + + function findAllUniq (data, r) { + var s = {}; + var m; + while ((m = r.exec(data))) { + s[m[1]] = 1; + } + return Object.keys(s); + } + + var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8'); + return { + libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg), + gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg), + systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg) + }; +}; + +StudioBuilder.prototype.extractRealProjectNameFromManifest = function () { + var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml'); + var manifestData = fs.readFileSync(manifestPath, 'utf8'); + var m = /= 0) { + return check_reqs.check_android_target(error).then(function () { + // If due to some odd reason - check_android_target succeeds + // we should still fail here. + return Q.reject(error); + }); + } + return Q.reject(error); + }); +}; + +StudioBuilder.prototype.clean = function (opts) { + var builder = this; + var wrapper = path.join(this.root, 'gradlew'); + var args = builder.getArgs('clean', opts); + return Q().then(function () { + return spawn(wrapper, args, {stdio: 'inherit'}); + }) + .then(function () { + shell.rm('-rf', path.join(builder.root, 'out')); + + ['debug', 'release'].forEach(function (config) { + var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES); + if (isAutoGenerated(propertiesFilePath)) { + shell.rm('-f', propertiesFilePath); + } + }); + }); +}; + +module.exports = StudioBuilder; + +function isAutoGenerated (file) { + return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0; +} diff --git a/platforms/android/cordova/lib/builders/builders.js b/platforms/android/cordova/lib/builders/builders.js new file mode 100644 index 0000000..aedf9be --- /dev/null +++ b/platforms/android/cordova/lib/builders/builders.js @@ -0,0 +1,46 @@ +/* + 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. +*/ + +var CordovaError = require('cordova-common').CordovaError; + +var knownBuilders = { + gradle: 'GradleBuilder', + studio: 'StudioBuilder', + none: 'GenericBuilder' +}; + +/** + * Helper method that instantiates and returns a builder for specified build + * type. + * + * @param {String} builderType Builder name to construct and return. Must + * be one of 'ant', 'gradle' or 'none' + * + * @return {Builder} A builder instance for specified build type. + */ +module.exports.getBuilder = function (builderType, projectRoot) { + if (!knownBuilders[builderType]) { throw new CordovaError('Builder ' + builderType + ' is not supported.'); } + + try { + var Builder = require('./' + knownBuilders[builderType]); + return new Builder(projectRoot); + } catch (err) { + throw new CordovaError('Failed to instantiate ' + knownBuilders[builderType] + ' builder: ' + err); + } +}; diff --git a/platforms/android/cordova/lib/check_reqs.js b/platforms/android/cordova/lib/check_reqs.js new file mode 100644 index 0000000..4a22360 --- /dev/null +++ b/platforms/android/cordova/lib/check_reqs.js @@ -0,0 +1,432 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +/* jshint sub:true */ + +var shelljs = require('shelljs'); +var child_process = require('child_process'); +var Q = require('q'); +var path = require('path'); +var fs = require('fs'); +var os = require('os'); +var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'); +var PROJECT_ROOT = path.join(__dirname, '..', '..'); +var CordovaError = require('cordova-common').CordovaError; +var superspawn = require('cordova-common').superspawn; +var android_sdk = require('./android_sdk'); + +function forgivingWhichSync (cmd) { + try { + return fs.realpathSync(shelljs.which(cmd)); + } catch (e) { + return ''; + } +} + +function tryCommand (cmd, errMsg, catchStderr) { + var d = Q.defer(); + child_process.exec(cmd, function (err, stdout, stderr) { + if (err) d.reject(new CordovaError(errMsg)); + // Sometimes it is necessary to return an stderr instead of stdout in case of success, since + // some commands prints theirs output to stderr instead of stdout. 'javac' is the example + else d.resolve((catchStderr ? stderr : stdout).trim()); + }); + return d.promise; +} + +module.exports.isWindows = function () { + return (os.platform() === 'win32'); +}; + +module.exports.isDarwin = function () { + return (os.platform() === 'darwin'); +}; + +// Get valid target from framework/project.properties if run from this repo +// Otherwise get target from project.properties file within a generated cordova-android project +module.exports.get_target = function () { + function extractFromFile (filePath) { + var target = shelljs.grep(/\btarget=/, filePath); + if (!target) { + throw new Error('Could not find android target within: ' + filePath); + } + return target.split('=')[1].trim(); + } + var repo_file = path.join(REPO_ROOT, 'framework', 'project.properties'); + if (fs.existsSync(repo_file)) { + return extractFromFile(repo_file); + } + var project_file = path.join(PROJECT_ROOT, 'project.properties'); + if (fs.existsSync(project_file)) { + // if no target found, we're probably in a project and project.properties is in PROJECT_ROOT. + return extractFromFile(project_file); + } + throw new Error('Could not find android target in either ' + repo_file + ' nor ' + project_file); +}; + +// Returns a promise. Called only by build and clean commands. +module.exports.check_ant = function () { + return superspawn.spawn('ant', ['-version']).then(function (output) { + // Parse Ant version from command output + return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1]; + }).catch(function (err) { + if (err) { + throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.'); + } + }); +}; + +module.exports.get_gradle_wrapper = function () { + var androidStudioPath; + var i = 0; + var foundStudio = false; + var program_dir; + // OK, This hack only works on Windows, not on Mac OS or Linux. We will be deleting this eventually! + if (module.exports.isWindows()) { + + var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat')); + // console.log('result.stdout =' + result.stdout.toString()); + // console.log('result.stderr =' + result.stderr.toString()); + + if (result.stderr.toString().length > 0) { + var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/'; + if (fs.existsSync(androidPath)) { + program_dir = fs.readdirSync(androidPath); + while (i < program_dir.length && !foundStudio) { + if (program_dir[i].startsWith('Android Studio')) { + foundStudio = true; + androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle'); + } else { ++i; } + } + } + } else { + // console.log('got android studio path from registry'); + // remove the (os independent) new line char at the end of stdout + // add gradle to match the above. + androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle'); + } + } + + if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) { + var dirs = fs.readdirSync(androidStudioPath); + if (dirs[0].split('-')[0] === 'gradle') { + return path.join(androidStudioPath, dirs[0], 'bin', 'gradle'); + } + } else { + // OK, let's try to check for Gradle! + return forgivingWhichSync('gradle'); + } +}; + +// Returns a promise. Called only by build and clean commands. +module.exports.check_gradle = function () { + var sdkDir = process.env['ANDROID_HOME']; + var d = Q.defer(); + if (!sdkDir) { + return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' + + 'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.')); + } + + var gradlePath = module.exports.get_gradle_wrapper(); + if (gradlePath.length !== 0) { d.resolve(gradlePath); } else { + d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' + + 'or on your system to install the gradle wrapper. Please include gradle \n' + + 'in your path, or install Android Studio')); + } + return d.promise; +}; + +// Returns a promise. +module.exports.check_java = function () { + var javacPath = forgivingWhichSync('javac'); + var hasJavaHome = !!process.env['JAVA_HOME']; + return Q().then(function () { + if (hasJavaHome) { + // Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh). + if (!javacPath) { + process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin'); + } + } else { + if (javacPath) { + // OS X has a command for finding JAVA_HOME. + var find_java = '/usr/libexec/java_home'; + var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.'; + if (fs.existsSync(find_java)) { + return superspawn.spawn(find_java).then(function (stdout) { + process.env['JAVA_HOME'] = stdout.trim(); + }).catch(function (err) { + if (err) { + throw new CordovaError(default_java_error_msg); + } + }); + } else { + // See if we can derive it from javac's location. + // fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK + var maybeJavaHome = path.dirname(path.dirname(javacPath)); + if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) { + process.env['JAVA_HOME'] = maybeJavaHome; + } else { + throw new CordovaError(default_java_error_msg); + } + } + } else if (module.exports.isWindows()) { + // Try to auto-detect java in the default install paths. + var oldSilent = shelljs.config.silent; + shelljs.config.silent = true; + var firstJdkDir = + shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] || + shelljs.ls('C:\\Program Files\\java\\jdk*')[0] || + shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0]; + shelljs.config.silent = oldSilent; + if (firstJdkDir) { + // shelljs always uses / in paths. + firstJdkDir = firstJdkDir.replace(/\//g, path.sep); + if (!javacPath) { + process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin'); + } + process.env['JAVA_HOME'] = firstJdkDir; + } + } + } + }).then(function () { + var msg = + 'Failed to run "javac -version", make sure that you have a JDK installed.\n' + + 'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n'; + if (process.env['JAVA_HOME']) { + msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n'; + } + // We use tryCommand with catchStderr = true, because + // javac writes version info to stderr instead of stdout + return tryCommand('javac -version', msg, true).then(function (output) { + // Let's check for at least Java 8, and keep it future proof so we can support Java 10 + var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output); + return match && match[1]; + }); + }); +}; + +// Returns a promise. +module.exports.check_android = function () { + return Q().then(function () { + var androidCmdPath = forgivingWhichSync('android'); + var adbInPath = forgivingWhichSync('adb'); + var avdmanagerInPath = forgivingWhichSync('avdmanager'); + var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']); + function maybeSetAndroidHome (value) { + if (!hasAndroidHome && fs.existsSync(value)) { + hasAndroidHome = true; + process.env['ANDROID_HOME'] = value; + } + } + // First ensure ANDROID_HOME is set + // If we have no hints (nothing in PATH), try a few default locations + if (!hasAndroidHome && !androidCmdPath && !adbInPath && !avdmanagerInPath) { + if (module.exports.isWindows()) { + // Android Studio 1.0 installer + maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk')); + maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk')); + // Android Studio pre-1.0 installer + maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-studio', 'sdk')); + maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-studio', 'sdk')); + // Stand-alone installer + maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk')); + maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk')); + } else if (module.exports.isDarwin()) { + // Android Studio 1.0 installer + maybeSetAndroidHome(path.join(process.env['HOME'], 'Library', 'Android', 'sdk')); + // Android Studio pre-1.0 installer + maybeSetAndroidHome('/Applications/Android Studio.app/sdk'); + // Stand-alone zip file that user might think to put under /Applications + maybeSetAndroidHome('/Applications/android-sdk-macosx'); + maybeSetAndroidHome('/Applications/android-sdk'); + } + if (process.env['HOME']) { + // Stand-alone zip file that user might think to put under their home directory + maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk-macosx')); + maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk')); + } + } + if (!hasAndroidHome) { + // If we dont have ANDROID_HOME, but we do have some tools on the PATH, try to infer from the tooling PATH. + var parentDir, grandParentDir; + if (androidCmdPath) { + parentDir = path.dirname(androidCmdPath); + grandParentDir = path.dirname(parentDir); + if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) { + maybeSetAndroidHome(grandParentDir); + } else { + throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' + + 'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' + + 'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.'); + } + } + if (adbInPath) { + parentDir = path.dirname(adbInPath); + grandParentDir = path.dirname(parentDir); + if (path.basename(parentDir) === 'platform-tools') { + maybeSetAndroidHome(grandParentDir); + } else { + throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' + + 'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' + + 'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.'); + } + } + if (avdmanagerInPath) { + parentDir = path.dirname(avdmanagerInPath); + grandParentDir = path.dirname(parentDir); + if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') { + maybeSetAndroidHome(path.dirname(grandParentDir)); + } else { + throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' + + 'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' + + 'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.'); + } + } + } + if (!process.env['ANDROID_HOME']) { + throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' + + 'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.'); + } + if (!fs.existsSync(process.env['ANDROID_HOME'])) { + throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] + + '\nTry update it manually to point to valid SDK directory.'); + } + // Next let's make sure relevant parts of the SDK tooling is in our PATH + if (hasAndroidHome && !androidCmdPath) { + process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools'); + } + if (hasAndroidHome && !adbInPath) { + process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools'); + } + if (hasAndroidHome && !avdmanagerInPath) { + process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools', 'bin'); + } + return hasAndroidHome; + }); +}; + +// TODO: is this actually needed? +module.exports.getAbsoluteAndroidCmd = function () { + var cmd = forgivingWhichSync('android'); + if (cmd.length === 0) { + cmd = forgivingWhichSync('sdkmanager'); + } + if (module.exports.isWindows()) { + return '"' + cmd + '"'; + } + return cmd.replace(/(\s)/g, '\\$1'); +}; + +module.exports.check_android_target = function (originalError) { + // valid_target can look like: + // android-19 + // android-L + // Google Inc.:Google APIs:20 + // Google Inc.:Glass Development Kit Preview:20 + var desired_api_level = module.exports.get_target(); + return android_sdk.list_targets().then(function (targets) { + if (targets.indexOf(desired_api_level) >= 0) { + return targets; + } + var androidCmd = module.exports.getAbsoluteAndroidCmd(); + var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' + + 'Hint: Open the SDK manager by running: ' + androidCmd + '\n' + + 'You will require:\n' + + '1. "SDK Platform" for API level ' + desired_api_level + '\n' + + '2. "Android SDK Platform-tools (latest)\n' + + '3. "Android SDK Build-tools" (latest)'; + if (originalError) { + msg = originalError + '\n' + msg; + } + throw new CordovaError(msg); + }); +}; + +// Returns a promise. +module.exports.run = function () { + return Q.all([this.check_java(), this.check_android()]).then(function (values) { + console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']); + console.log('JAVA_HOME=' + process.env['JAVA_HOME']); + + if (!values[0]) { + throw new CordovaError('Requirements check failed for JDK 1.8 or greater'); + } + + if (!values[1]) { + throw new CordovaError('Requirements check failed for Android SDK'); + } + }); +}; + +/** + * Object thar represents one of requirements for current platform. + * @param {String} id The unique identifier for this requirements. + * @param {String} name The name of requirements. Human-readable field. + * @param {String} version The version of requirement installed. In some cases could be an array of strings + * (for example, check_android_target returns an array of android targets installed) + * @param {Boolean} installed Indicates whether the requirement is installed or not + */ +var Requirement = function (id, name, version, installed) { + this.id = id; + this.name = name; + this.installed = installed || false; + this.metadata = { + version: version + }; +}; + +/** + * Methods that runs all checks one by one and returns a result of checks + * as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method + * + * @return Promise Array of requirements. Due to implementation, promise is always fulfilled. + */ +module.exports.check_all = function () { + + var requirements = [ + new Requirement('java', 'Java JDK'), + new Requirement('androidSdk', 'Android SDK'), + new Requirement('androidTarget', 'Android target'), + new Requirement('gradle', 'Gradle') + ]; + + var checkFns = [ + this.check_java, + this.check_android, + this.check_android_target, + this.check_gradle + ]; + + // Then execute requirement checks one-by-one + return checkFns.reduce(function (promise, checkFn, idx) { + // Update each requirement with results + var requirement = requirements[idx]; + return promise.then(checkFn).then(function (version) { + requirement.installed = true; + requirement.metadata.version = version; + }, function (err) { + requirement.metadata.reason = err instanceof Error ? err.message : err; + }); + }, Q()).then(function () { + // When chain is completed, return requirements array to upstream API + return requirements; + }); +}; diff --git a/platforms/android/cordova/lib/device.js b/platforms/android/cordova/lib/device.js new file mode 100644 index 0000000..84b5094 --- /dev/null +++ b/platforms/android/cordova/lib/device.js @@ -0,0 +1,112 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var Q = require('q'); +var build = require('./build'); +var path = require('path'); +var Adb = require('./Adb'); +var AndroidManifest = require('./AndroidManifest'); +var spawn = require('cordova-common').superspawn.spawn; +var CordovaError = require('cordova-common').CordovaError; +var events = require('cordova-common').events; + +/** + * Returns a promise for the list of the device ID's found + * @param lookHarder When true, try restarting adb if no devices are found. + */ +module.exports.list = function (lookHarder) { + return Adb.devices().then(function (list) { + if (list.length === 0 && lookHarder) { + // adb kill-server doesn't seem to do the trick. + // Could probably find a x-platform version of killall, but I'm not actually + // sure that this scenario even happens on non-OSX machines. + return spawn('killall', ['adb']).then(function () { + events.emit('verbose', 'Restarting adb to see if more devices are detected.'); + return Adb.devices(); + }, function () { + // For non-killall OS's. + return list; + }); + } + return list; + }); +}; + +module.exports.resolveTarget = function (target) { + return this.list(true).then(function (device_list) { + if (!device_list || !device_list.length) { + return Q.reject(new CordovaError('Failed to deploy to device, no devices found.')); + } + // default device + target = target || device_list[0]; + + if (device_list.indexOf(target) < 0) { + return Q.reject('ERROR: Unable to find target \'' + target + '\'.'); + } + + return build.detectArchitecture(target).then(function (arch) { + return { target: target, arch: arch, isEmulator: false }; + }); + }); +}; + +/* + * Installs a previously built application on the device + * and launches it. + * Returns a promise. + */ +module.exports.install = function (target, buildResults) { + return Q().then(function () { + if (target && typeof target === 'object') { + return target; + } + return module.exports.resolveTarget(target); + }).then(function (resolvedTarget) { + var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch); + var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml')); + var pkgName = manifest.getPackageId(); + var launchName = pkgName + '/.' + manifest.getActivity().getName(); + events.emit('log', 'Using apk: ' + apk_path); + events.emit('log', 'Package name: ' + pkgName); + + return Adb.install(resolvedTarget.target, apk_path, {replace: true}).catch(function (error) { + // CB-9557 CB-10157 only uninstall and reinstall app if the one that + // is already installed on device was signed w/different certificate + if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; } + + events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' + + 'installed app already signed with different key'); + + // This promise is always resolved, even if 'adb uninstall' fails to uninstall app + // or the app doesn't installed at all, so no error catching needed. + return Adb.uninstall(resolvedTarget.target, pkgName).then(function () { + return Adb.install(resolvedTarget.target, apk_path, {replace: true}); + }); + }).then(function () { + // unlock screen + return Adb.shell(resolvedTarget.target, 'input keyevent 82'); + }).then(function () { + return Adb.start(resolvedTarget.target, launchName); + }).then(function () { + events.emit('log', 'LAUNCH SUCCESS'); + }); + }); +}; diff --git a/platforms/android/cordova/lib/emulator.js b/platforms/android/cordova/lib/emulator.js new file mode 100644 index 0000000..305e2e3 --- /dev/null +++ b/platforms/android/cordova/lib/emulator.js @@ -0,0 +1,533 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +/* jshint sub:true */ + +var android_versions = require('android-versions'); +var retry = require('./retry'); +var build = require('./build'); +var path = require('path'); +var Adb = require('./Adb'); +var AndroidManifest = require('./AndroidManifest'); +var events = require('cordova-common').events; +var superspawn = require('cordova-common').superspawn; +var CordovaError = require('cordova-common').CordovaError; +var shelljs = require('shelljs'); +var android_sdk = require('./android_sdk'); +var check_reqs = require('./check_reqs'); + +var Q = require('q'); +var os = require('os'); +var fs = require('fs'); +var child_process = require('child_process'); + +// constants +var ONE_SECOND = 1000; // in milliseconds +var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds +var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds +var NUM_INSTALL_RETRIES = 3; +var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds +var EXEC_KILL_SIGNAL = 'SIGKILL'; + +function forgivingWhichSync (cmd) { + try { + return fs.realpathSync(shelljs.which(cmd)); + } catch (e) { + return ''; + } +} + +module.exports.list_images_using_avdmanager = function () { + return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) { + var response = output.split('\n'); + var emulator_list = []; + for (var i = 1; i < response.length; i++) { + // To return more detailed information use img_obj + var img_obj = {}; + if (response[i].match(/Name:\s/)) { + img_obj['name'] = response[i].split('Name: ')[1].replace('\r', ''); + if (response[i + 1].match(/Device:\s/)) { + i++; + img_obj['device'] = response[i].split('Device: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/Path:\s/)) { + i++; + img_obj['path'] = response[i].split('Path: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/Target:\s/)) { + i++; + if (response[i + 1].match(/ABI:\s/)) { + img_obj['abi'] = response[i + 1].split('ABI: ')[1].replace('\r', ''); + } + // This next conditional just aims to match the old output of `android list avd` + // We do so so that we don't have to change the logic when parsing for the + // best emulator target to spawn (see below in `best_image`) + // This allows us to transitionally support both `android` and `avdmanager` binaries, + // depending on what SDK version the user has + if (response[i + 1].match(/Based\son:\s/)) { + img_obj['target'] = response[i + 1].split('Based on:')[1]; + if (img_obj['target'].match(/Tag\/ABI:\s/)) { + img_obj['target'] = img_obj['target'].split('Tag/ABI:')[0].replace('\r', '').trim(); + if (img_obj['target'].indexOf('(') > -1) { + img_obj['target'] = img_obj['target'].substr(0, img_obj['target'].indexOf('(') - 1).trim(); + } + } + var version_string = img_obj['target'].replace(/Android\s+/, ''); + + var api_level = android_sdk.version_string_to_api_level[version_string]; + if (api_level) { + img_obj['target'] += ' (API level ' + api_level + ')'; + } + } + } + if (response[i + 1].match(/Skin:\s/)) { + i++; + img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', ''); + } + + emulator_list.push(img_obj); + } + /* To just return a list of names use this + if (response[i].match(/Name:\s/)) { + emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); + } */ + + } + return emulator_list; + }); +}; + +module.exports.list_images_using_android = function () { + return superspawn.spawn('android', ['list', 'avd']).then(function (output) { + var response = output.split('\n'); + var emulator_list = []; + for (var i = 1; i < response.length; i++) { + // To return more detailed information use img_obj + var img_obj = {}; + if (response[i].match(/Name:\s/)) { + img_obj['name'] = response[i].split('Name: ')[1].replace('\r', ''); + if (response[i + 1].match(/Device:\s/)) { + i++; + img_obj['device'] = response[i].split('Device: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/Path:\s/)) { + i++; + img_obj['path'] = response[i].split('Path: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) { + i++; + var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : ''; + img_obj['target'] = (response[i] + secondLine).split('Target: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/ABI:\s/)) { + i++; + img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/Skin:\s/)) { + i++; + img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', ''); + } + + emulator_list.push(img_obj); + } + /* To just return a list of names use this + if (response[i].match(/Name:\s/)) { + emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); + } */ + + } + return emulator_list; + }); +}; + +/** + * Returns a Promise for a list of emulator images in the form of objects + * { + name : , + device : , + path : , + target : , + abi : , + skin : + } + */ +module.exports.list_images = function () { + return Q.fcall(function () { + if (forgivingWhichSync('avdmanager')) { + return module.exports.list_images_using_avdmanager(); + } else if (forgivingWhichSync('android')) { + return module.exports.list_images_using_android(); + } else { + return Q().then(function () { + throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?'); + }); + } + }).then(function (avds) { + // In case we're missing the Android OS version string from the target description, add it. + return avds.map(function (avd) { + if (avd.target && avd.target.indexOf('Android API') > -1 && avd.target.indexOf('API level') < 0) { + var api_level = avd.target.match(/\d+/); + if (api_level) { + var level = android_versions.get(api_level); + if (level) { + avd.target = 'Android ' + level.semver + ' (API level ' + api_level + ')'; + } + } + } + return avd; + }); + }); +}; + +/** + * Will return the closest avd to the projects target + * or undefined if no avds exist. + * Returns a promise. + */ +module.exports.best_image = function () { + return this.list_images().then(function (images) { + // Just return undefined if there is no images + if (images.length === 0) return; + + var closest = 9999; + var best = images[0]; + var project_target = parseInt(check_reqs.get_target().replace('android-', '')); + for (var i in images) { + var target = images[i].target; + if (target && target.indexOf('API level') > -1) { + var num = parseInt(target.split('(API level ')[1].replace(')', '')); + if (num === project_target) { + return images[i]; + } else if (project_target - num < closest && project_target > num) { + closest = project_target - num; + best = images[i]; + } + } + } + return best; + }); +}; + +// Returns a promise. +module.exports.list_started = function () { + return Adb.devices({emulators: true}); +}; + +// Returns a promise. +// TODO: we should remove this, there's a more robust method under android_sdk.js +module.exports.list_targets = function () { + return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}).then(function (output) { + var target_out = output.split('\n'); + var targets = []; + for (var i = target_out.length; i >= 0; i--) { + if (target_out[i].match(/id:/)) { + targets.push(targets[i].split(' ')[1]); + } + } + return targets; + }); +}; + +/* + * Gets unused port for android emulator, between 5554 and 5584 + * Returns a promise. + */ +module.exports.get_available_port = function () { + var self = this; + + return self.list_started().then(function (emulators) { + for (var p = 5584; p >= 5554; p -= 2) { + if (emulators.indexOf('emulator-' + p) === -1) { + events.emit('verbose', 'Found available port: ' + p); + return p; + } + } + throw new CordovaError('Could not find an available avd port'); + }); +}; + +/* + * Starts an emulator with the given ID, + * and returns the started ID of that emulator. + * If no ID is given it will use the first image available, + * if no image is available it will error out (maybe create one?). + * If no boot timeout is given or the value is negative it will wait forever for + * the emulator to boot + * + * Returns a promise. + */ +module.exports.start = function (emulator_ID, boot_timeout) { + var self = this; + + return Q().then(function () { + if (emulator_ID) return Q(emulator_ID); + + return self.best_image().then(function (best) { + if (best && best.name) { + events.emit('warn', 'No emulator specified, defaulting to ' + best.name); + return best.name; + } + + var androidCmd = check_reqs.getAbsoluteAndroidCmd(); + return Q.reject(new CordovaError('No emulator images (avds) found.\n' + + '1. Download desired System Image by running: ' + androidCmd + ' sdk\n' + + '2. Create an AVD by running: ' + androidCmd + ' avd\n' + + 'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n')); + }); + }).then(function (emulatorId) { + return self.get_available_port().then(function (port) { + // Figure out the directory the emulator binary runs in, and set the cwd to that directory. + // Workaround for https://code.google.com/p/android/issues/detail?id=235461 + var emulator_dir = path.dirname(shelljs.which('emulator')); + var args = ['-avd', emulatorId, '-port', port]; + // Don't wait for it to finish, since the emulator will probably keep running for a long time. + child_process + .spawn('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir }) + .unref(); + + // wait for emulator to start + events.emit('log', 'Waiting for emulator to start...'); + return self.wait_for_emulator(port); + }); + }).then(function (emulatorId) { + if (!emulatorId) { return Q.reject(new CordovaError('Failed to start emulator')); } + + // wait for emulator to boot up + process.stdout.write('Waiting for emulator to boot (this may take a while)...'); + return self.wait_for_boot(emulatorId, boot_timeout).then(function (success) { + if (success) { + events.emit('log', 'BOOT COMPLETE'); + // unlock screen + return Adb.shell(emulatorId, 'input keyevent 82').then(function () { + // return the new emulator id for the started emulators + return emulatorId; + }); + } else { + // We timed out waiting for the boot to happen + return null; + } + }); + }); +}; + +/* + * Waits for an emulator to boot on a given port. + * Returns this emulator's ID in a promise. + */ +module.exports.wait_for_emulator = function (port) { + var self = this; + return Q().then(function () { + var emulator_id = 'emulator-' + port; + return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) { + if (output.indexOf('1') >= 0) { + return emulator_id; + } + return self.wait_for_emulator(port); + }, function (error) { + if ((error && error.message && + (error.message.indexOf('not found') > -1)) || + (error.message.indexOf('device offline') > -1)) { + // emulator not yet started, continue waiting + return self.wait_for_emulator(port); + } else { + // something unexpected has happened + throw error; + } + }); + }); +}; + +/* + * Waits for the core android process of the emulator to start. Returns a + * promise that resolves to a boolean indicating success. Not specifying a + * time_remaining or passing a negative value will cause it to wait forever + */ +module.exports.wait_for_boot = function (emulator_id, time_remaining) { + var self = this; + return Adb.shell(emulator_id, 'ps').then(function (output) { + if (output.match(/android\.process\.acore/)) { + return true; + } else if (time_remaining === 0) { + return false; + } else { + process.stdout.write('.'); + + // Check at regular intervals + return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function () { + var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining; + return self.wait_for_boot(emulator_id, updated_time); + }); + } + }); +}; + +/* + * Create avd + * TODO : Enter the stdin input required to complete the creation of an avd. + * Returns a promise. + */ +module.exports.create_image = function (name, target) { + console.log('Creating new avd named ' + name); + if (target) { + return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) { + console.error('ERROR : Failed to create emulator image : '); + console.error(' Do you have the latest android targets including ' + target + '?'); + console.error(error); + }); + } else { + console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.'); + // TODO: there's a more robust method for finding targets in android_sdk.js + return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () { + // TODO: This seems like another error case, even though it always happens. + console.error('ERROR : Unable to create an avd emulator, no targets found.'); + console.error('Ensure you have targets available by running the "android" command'); + return Q.reject(); + }, function (error) { + console.error('ERROR : Failed to create emulator image : '); + console.error(error); + }); + } +}; + +module.exports.resolveTarget = function (target) { + return this.list_started().then(function (emulator_list) { + if (emulator_list.length < 1) { + return Q.reject('No running Android emulators found, please start an emulator before deploying your project.'); + } + + // default emulator + target = target || emulator_list[0]; + if (emulator_list.indexOf(target) < 0) { + return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'); + } + + return build.detectArchitecture(target).then(function (arch) { + return {target: target, arch: arch, isEmulator: true}; + }); + }); +}; + +/* + * Installs a previously built application on the emulator and launches it. + * If no target is specified, then it picks one. + * If no started emulators are found, error out. + * Returns a promise. + */ +module.exports.install = function (givenTarget, buildResults) { + + var target; + // We need to find the proper path to the Android Manifest + var manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml'); + if (buildResults.buildMethod === 'gradle') { + manifestPath = path.join(__dirname, '../../AndroidManifest.xml'); + } + var manifest = new AndroidManifest(manifestPath); + var pkgName = manifest.getPackageId(); + + // resolve the target emulator + return Q().then(function () { + if (givenTarget && typeof givenTarget === 'object') { + return givenTarget; + } else { + return module.exports.resolveTarget(givenTarget); + } + + // set the resolved target + }).then(function (resolvedTarget) { + target = resolvedTarget; + + // install the app + }).then(function () { + // This promise is always resolved, even if 'adb uninstall' fails to uninstall app + // or the app doesn't installed at all, so no error catching needed. + return Q.when().then(function () { + + var apk_path = build.findBestApkForArchitecture(buildResults, target.arch); + var execOptions = { + cwd: os.tmpdir(), + timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds + killSignal: EXEC_KILL_SIGNAL + }; + + events.emit('log', 'Using apk: ' + apk_path); + events.emit('log', 'Package name: ' + pkgName); + events.emit('verbose', 'Installing app on emulator...'); + + // A special function to call adb install in specific environment w/ specific options. + // Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119 + // to workaround sporadic emulator hangs + function adbInstallWithOptions (target, apk, opts) { + events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...'); + + var command = 'adb -s ' + target + ' install -r "' + apk + '"'; + return Q.promise(function (resolve, reject) { + child_process.exec(command, opts, function (err, stdout, stderr) { + if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr)); + // adb does not return an error code even if installation fails. Instead it puts a specific + // message to stdout, so we have to use RegExp matching to detect installation failure. + else if (/Failure/.test(stdout)) { + if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) { + stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' + + ' or sign and deploy the unsigned apk manually using Android tools.'; + } else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) { + stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' + + '\nEither uninstall an app or increment the versionCode.'; + } + + reject(new CordovaError('Failed to install apk to emulator: ' + stdout)); + } else resolve(stdout); + }); + }); + } + + function installPromise () { + return adbInstallWithOptions(target.target, apk_path, execOptions).catch(function (error) { + // CB-9557 CB-10157 only uninstall and reinstall app if the one that + // is already installed on device was signed w/different certificate + if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; } + + events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' + + 'currently installed app was signed with different key'); + + // This promise is always resolved, even if 'adb uninstall' fails to uninstall app + // or the app doesn't installed at all, so no error catching needed. + return Adb.uninstall(target.target, pkgName).then(function () { + return adbInstallWithOptions(target.target, apk_path, execOptions); + }); + }); + } + + return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) { + events.emit('log', 'INSTALL SUCCESS'); + }); + }); + // unlock screen + }).then(function () { + + events.emit('verbose', 'Unlocking screen...'); + return Adb.shell(target.target, 'input keyevent 82'); + }).then(function () { + Adb.start(target.target, pkgName + '/.' + manifest.getActivity().getName()); + // report success or failure + }).then(function (output) { + events.emit('log', 'LAUNCH SUCCESS'); + }); +}; diff --git a/platforms/android/cordova/lib/getASPath.bat b/platforms/android/cordova/lib/getASPath.bat new file mode 100644 index 0000000..14dad43 --- /dev/null +++ b/platforms/android/cordova/lib/getASPath.bat @@ -0,0 +1,3 @@ +@ECHO OFF +for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b" +ECHO %ASPath% \ No newline at end of file diff --git a/platforms/android/cordova/lib/install-device b/platforms/android/cordova/lib/install-device new file mode 100755 index 0000000..fc4b784 --- /dev/null +++ b/platforms/android/cordova/lib/install-device @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var device = require('./device'), + args = process.argv; + +if(args.length > 2) { + var install_target; + if (args[2].substring(0, 9) == '--target=') { + install_target = args[2].substring(9, args[2].length); + device.install(install_target).done(null, function(err) { + console.error('ERROR: ' + err); + process.exit(2); + }); + } else { + console.error('ERROR : argument \'' + args[2] + '\' not recognized.'); + process.exit(2); + } +} else { + device.install().done(null, function(err) { + console.error('ERROR: ' + err); + process.exit(2); + }); +} diff --git a/platforms/android/cordova/lib/install-device.bat b/platforms/android/cordova/lib/install-device.bat new file mode 100644 index 0000000..109b470 --- /dev/null +++ b/platforms/android/cordova/lib/install-device.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0install-device" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'install-device' script in 'cordova\lib' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/lib/install-emulator b/platforms/android/cordova/lib/install-emulator new file mode 100755 index 0000000..aa2a34f --- /dev/null +++ b/platforms/android/cordova/lib/install-emulator @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var emulator = require('./emulator'), + args = process.argv; + +var install_target; +if(args.length > 2) { + if (args[2].substring(0, 9) == '--target=') { + install_target = args[2].substring(9, args[2].length); + } else { + console.error('ERROR : argument \'' + args[2] + '\' not recognized.'); + process.exit(2); + } +} + +emulator.install(install_target).done(null, function(err) { + console.error('ERROR: ' + err); + process.exit(2); +}); diff --git a/platforms/android/cordova/lib/install-emulator.bat b/platforms/android/cordova/lib/install-emulator.bat new file mode 100644 index 0000000..a28c23a --- /dev/null +++ b/platforms/android/cordova/lib/install-emulator.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0install-emulator" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'install-emulator' script in 'cordova\lib' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/lib/list-devices b/platforms/android/cordova/lib/list-devices new file mode 100755 index 0000000..8e22c7f --- /dev/null +++ b/platforms/android/cordova/lib/list-devices @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var devices = require('./device'); + +// Usage support for when args are given +require('./check_reqs').check_android().then(function() { + devices.list().done(function(device_list) { + device_list && device_list.forEach(function(dev) { + console.log(dev); + }); + }, function(err) { + console.error('ERROR: ' + err); + process.exit(2); + }); +}); diff --git a/platforms/android/cordova/lib/list-devices.bat b/platforms/android/cordova/lib/list-devices.bat new file mode 100644 index 0000000..ad5f03e --- /dev/null +++ b/platforms/android/cordova/lib/list-devices.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0list-devices" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'list-devices' script in 'cordova\lib' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/lib/list-emulator-images b/platforms/android/cordova/lib/list-emulator-images new file mode 100755 index 0000000..25e5c81 --- /dev/null +++ b/platforms/android/cordova/lib/list-emulator-images @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var emulators = require('./emulator'); + +// Usage support for when args are given +require('./check_reqs').check_android().then(function() { + emulators.list_images().done(function(emulator_list) { + emulator_list && emulator_list.forEach(function(emu) { + console.log(emu.name); + }); + }, function(err) { + console.error('ERROR: ' + err); + process.exit(2); + }); +}); diff --git a/platforms/android/cordova/lib/list-emulator-images.bat b/platforms/android/cordova/lib/list-emulator-images.bat new file mode 100644 index 0000000..616ffb7 --- /dev/null +++ b/platforms/android/cordova/lib/list-emulator-images.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0list-emulator-images" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2 + EXIT /B 1 +) diff --git a/platforms/android/cordova/lib/list-started-emulators b/platforms/android/cordova/lib/list-started-emulators new file mode 100755 index 0000000..43ebda2 --- /dev/null +++ b/platforms/android/cordova/lib/list-started-emulators @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var emulators = require('./emulator'); + +// Usage support for when args are given +require('./check_reqs').check_android().then(function() { + emulators.list_started().done(function(emulator_list) { + emulator_list && emulator_list.forEach(function(emu) { + console.log(emu); + }); + }, function(err) { + console.error('ERROR: ' + err); + process.exit(2); + }); +}); diff --git a/platforms/android/cordova/lib/list-started-emulators.bat b/platforms/android/cordova/lib/list-started-emulators.bat new file mode 100644 index 0000000..eed02a5 --- /dev/null +++ b/platforms/android/cordova/lib/list-started-emulators.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0list-started-emulators" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'list-started-emulators' script in 'cordova\lib' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/lib/log.js b/platforms/android/cordova/lib/log.js new file mode 100644 index 0000000..ef2dd5c --- /dev/null +++ b/platforms/android/cordova/lib/log.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var path = require('path'); +var os = require('os'); +var Q = require('q'); +var child_process = require('child_process'); +var ROOT = path.join(__dirname, '..', '..'); + +/* + * Starts running logcat in the shell. + * Returns a promise. + */ +module.exports.run = function () { + var d = Q.defer(); + var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()}); + + adb.stdout.on('data', function (data) { + var lines = data ? data.toString().split('\n') : []; + var out = lines.filter(function (x) { return x.indexOf('nativeGetEnabledTags') < 0; }); + console.log(out.join('\n')); + }); + + adb.stderr.on('data', console.error); + adb.on('close', function (code) { + if (code > 0) { + d.reject('Failed to run logcat command.'); + } else d.resolve(); + }); + + return d.promise; +}; + +module.exports.help = function () { + console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log'))); + console.log('Gives the logcat output on the command line.'); + process.exit(0); +}; diff --git a/platforms/android/cordova/lib/plugin-build.gradle b/platforms/android/cordova/lib/plugin-build.gradle new file mode 100644 index 0000000..bf8c59a --- /dev/null +++ b/platforms/android/cordova/lib/plugin-build.gradle @@ -0,0 +1,72 @@ +/* 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. +*/ + +// GENERATED FILE! DO NOT EDIT! + +buildscript { + repositories { + jcenter() + maven { + url "https://maven.google.com" + } + } + + // Switch the Android Gradle plugin version requirement depending on the + // installed version of Gradle. This dependency is documented at + // http://tools.android.com/tech-docs/new-build-system/version-compatibility + // and https://issues.apache.org/jira/browse/CB-8143 + dependencies { + classpath 'com.android.tools.build:gradle:1.0.0+' + } +} + +apply plugin: 'com.android.library' + +dependencies { + compile fileTree(dir: 'libs', include: '*.jar') + debugCompile project(path: ":CordovaLib", configuration: "debug") + releaseCompile project(path: ":CordovaLib", configuration: "release") +} + +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'] + jniLibs.srcDirs = ['libs'] + } + } +} + +if (file('build-extras.gradle').exists()) { + apply from: 'build-extras.gradle' +} diff --git a/platforms/android/cordova/lib/pluginHandlers.js b/platforms/android/cordova/lib/pluginHandlers.js new file mode 100644 index 0000000..842489a --- /dev/null +++ b/platforms/android/cordova/lib/pluginHandlers.js @@ -0,0 +1,320 @@ +/* + * 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. + * +*/ + +/* jshint unused: vars */ + +var fs = require('fs'); +var path = require('path'); +var shell = require('shelljs'); +var events = require('cordova-common').events; +var CordovaError = require('cordova-common').CordovaError; + +var handlers = { + 'source-file': { + install: function (obj, plugin, project, options) { + if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id)); + if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id)); + + var dest = path.join(obj.targetDir, path.basename(obj.src)); + + // TODO: This code needs to be replaced, since the core plugins need to be re-mapped to a different location in + // a later plugins release. This is for legacy plugins to work with Cordova. + + if (options && options.android_studio === true) { + // If a Java file is using the new directory structure, don't penalize it + if (!obj.targetDir.includes('app/src/main')) { + if (obj.src.endsWith('.java')) { + dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); + } else if (obj.src.endsWith('.xml')) { + // We are making a huge assumption here that XML files will be going to res/xml or values/xml + dest = path.join('app/src/main', obj.targetDir, path.basename(obj.src)); + } + } + } + + if (options && options.force) { + copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); + } else { + copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); + } + }, + uninstall: function (obj, plugin, project, options) { + var dest = path.join(obj.targetDir, path.basename(obj.src)); + + if (options && options.android_studio === true) { + dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); + } + + deleteJava(project.projectDir, dest); + } + }, + 'lib-file': { + install: function (obj, plugin, project, options) { + var dest = path.join('libs', path.basename(obj.src)); + if (options && options.android_studio === true) { + dest = path.join('app/libs', path.basename(obj.src)); + } + copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); + }, + uninstall: function (obj, plugin, project, options) { + var dest = path.join('libs', path.basename(obj.src)); + if (options && options.android_studio === true) { + dest = path.join('app/libs', path.basename(obj.src)); + } + removeFile(project.projectDir, dest); + } + }, + 'resource-file': { + install: function (obj, plugin, project, options) { + var dest = path.normalize(obj.target); + if (options && options.android_studio === true) { + dest = path.join('app/src/main', dest); + } + copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); + }, + uninstall: function (obj, plugin, project, options) { + var dest = path.normalize(obj.target); + if (options && options.android_studio === true) { + dest = path.join('app/src/main', dest); + } + removeFile(project.projectDir, dest); + } + }, + 'framework': { + install: function (obj, plugin, project, options) { + var src = obj.src; + if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id)); + + events.emit('verbose', 'Installing Android library: ' + src); + var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir; + var subDir; + + if (obj.custom) { + var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src); + copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, !!(options && options.link)); + subDir = path.resolve(project.projectDir, subRelativeDir); + } else { + obj.type = 'sys'; + subDir = src; + } + + if (obj.type === 'gradleReference') { + project.addGradleReference(parentDir, subDir); + } else if (obj.type === 'sys') { + project.addSystemLibrary(parentDir, subDir); + } else { + project.addSubProject(parentDir, subDir); + } + }, + uninstall: function (obj, plugin, project, options) { + var src = obj.src; + if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id)); + + events.emit('verbose', 'Uninstalling Android library: ' + src); + var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir; + var subDir; + + if (obj.custom) { + var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src); + removeFile(project.projectDir, subRelativeDir); + subDir = path.resolve(project.projectDir, subRelativeDir); + // If it's the last framework in the plugin, remove the parent directory. + var parDir = path.dirname(subDir); + if (fs.existsSync(parDir) && fs.readdirSync(parDir).length === 0) { + fs.rmdirSync(parDir); + } + } else { + obj.type = 'sys'; + subDir = src; + } + + if (obj.type === 'gradleReference') { + project.removeGradleReference(parentDir, subDir); + } else if (obj.type === 'sys') { + project.removeSystemLibrary(parentDir, subDir); + } else { + project.removeSubProject(parentDir, subDir); + } + } + }, + asset: { + install: function (obj, plugin, project, options) { + if (!obj.src) { + throw new CordovaError(generateAttributeError('src', 'asset', plugin.id)); + } + if (!obj.target) { + throw new CordovaError(generateAttributeError('target', 'asset', plugin.id)); + } + + copyFile(plugin.dir, obj.src, project.www, obj.target); + if (options && options.usePlatformWww) { + // CB-11022 copy file to both directories if usePlatformWww is specified + copyFile(plugin.dir, obj.src, project.platformWww, obj.target); + } + }, + uninstall: function (obj, plugin, project, options) { + var target = obj.target || obj.src; + + if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id)); + + removeFileF(path.resolve(project.www, target)); + removeFileF(path.resolve(project.www, 'plugins', plugin.id)); + if (options && options.usePlatformWww) { + // CB-11022 remove file from both directories if usePlatformWww is specified + removeFileF(path.resolve(project.platformWww, target)); + removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id)); + } + } + }, + 'js-module': { + install: function (obj, plugin, project, options) { + // Copy the plugin's files into the www directory. + var moduleSource = path.resolve(plugin.dir, obj.src); + var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src))); + + // Read in the file, prepend the cordova.define, and write it back out. + var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM + if (moduleSource.match(/.*\.json$/)) { + scriptContent = 'module.exports = ' + scriptContent; + } + scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n'; + + var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src); + shell.mkdir('-p', path.dirname(wwwDest)); + fs.writeFileSync(wwwDest, scriptContent, 'utf-8'); + + if (options && options.usePlatformWww) { + // CB-11022 copy file to both directories if usePlatformWww is specified + var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src); + shell.mkdir('-p', path.dirname(platformWwwDest)); + fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8'); + } + }, + uninstall: function (obj, plugin, project, options) { + var pluginRelativePath = path.join('plugins', plugin.id, obj.src); + removeFileAndParents(project.www, pluginRelativePath); + if (options && options.usePlatformWww) { + // CB-11022 remove file from both directories if usePlatformWww is specified + removeFileAndParents(project.platformWww, pluginRelativePath); + } + } + } +}; + +module.exports.getInstaller = function (type) { + if (handlers[type] && handlers[type].install) { + return handlers[type].install; + } + + events.emit('verbose', '<' + type + '> is not supported for android plugins'); +}; + +module.exports.getUninstaller = function (type) { + if (handlers[type] && handlers[type].uninstall) { + return handlers[type].uninstall; + } + + events.emit('verbose', '<' + type + '> is not supported for android plugins'); +}; + +function copyFile (plugin_dir, src, project_dir, dest, link) { + src = path.resolve(plugin_dir, src); + if (!fs.existsSync(src)) throw new CordovaError('"' + src + '" not found!'); + + // check that src path is inside plugin directory + var real_path = fs.realpathSync(src); + var real_plugin_path = fs.realpathSync(plugin_dir); + if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); } + + dest = path.resolve(project_dir, dest); + + // check that dest path is located in project directory + if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); } + + shell.mkdir('-p', path.dirname(dest)); + if (link) { + symlinkFileOrDirTree(src, dest); + } else if (fs.statSync(src).isDirectory()) { + // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq + shell.cp('-Rf', src + '/*', dest); + } else { + shell.cp('-f', src, dest); + } +} + +// Same as copy file but throws error if target exists +function copyNewFile (plugin_dir, src, project_dir, dest, link) { + var target_path = path.resolve(project_dir, dest); + if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); } + + copyFile(plugin_dir, src, project_dir, dest, !!link); +} + +function symlinkFileOrDirTree (src, dest) { + if (fs.existsSync(dest)) { + shell.rm('-Rf', dest); + } + + if (fs.statSync(src).isDirectory()) { + shell.mkdir('-p', dest); + fs.readdirSync(src).forEach(function (entry) { + symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry)); + }); + } else { + fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest); + } +} + +// checks if file exists and then deletes. Error if doesn't exist +function removeFile (project_dir, src) { + var file = path.resolve(project_dir, src); + shell.rm('-Rf', file); +} + +// deletes file/directory without checking +function removeFileF (file) { + shell.rm('-Rf', file); +} + +// Sometimes we want to remove some java, and prune any unnecessary empty directories +function deleteJava (project_dir, destFile) { + removeFileAndParents(project_dir, destFile, 'src'); +} + +function removeFileAndParents (baseDir, destFile, stopper) { + stopper = stopper || '.'; + var file = path.resolve(baseDir, destFile); + if (!fs.existsSync(file)) return; + + removeFileF(file); + + // check if directory is empty + var curDir = path.dirname(file); + + while (curDir !== path.resolve(baseDir, stopper)) { + if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) { + fs.rmdirSync(curDir); + curDir = path.resolve(curDir, '..'); + } else { + // directory not empty...do nothing + break; + } + } +} + +function generateAttributeError (attribute, element, id) { + return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id; +} diff --git a/platforms/android/cordova/lib/prepare.js b/platforms/android/cordova/lib/prepare.js new file mode 100644 index 0000000..ac63f8a --- /dev/null +++ b/platforms/android/cordova/lib/prepare.js @@ -0,0 +1,480 @@ +/** + 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. +*/ +/* eslint no-useless-escape: 0 */ + +var Q = require('q'); +var fs = require('fs'); +var path = require('path'); +var shell = require('shelljs'); +var events = require('cordova-common').events; +var AndroidManifest = require('./AndroidManifest'); +var checkReqs = require('./check_reqs'); +var xmlHelpers = require('cordova-common').xmlHelpers; +var CordovaError = require('cordova-common').CordovaError; +var ConfigParser = require('cordova-common').ConfigParser; +var FileUpdater = require('cordova-common').FileUpdater; +var PlatformJson = require('cordova-common').PlatformJson; +var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger; +var PluginInfoProvider = require('cordova-common').PluginInfoProvider; + +module.exports.prepare = function (cordovaProject, options) { + var self = this; + + var platformJson = PlatformJson.load(this.locations.root, this.platform); + var munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider()); + + this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations); + + // Update own www dir with project's www assets and plugins' assets and js-files + return Q.when(updateWww(cordovaProject, this.locations)).then(function () { + // update project according to config.xml changes. + return updateProjectAccordingTo(self._config, self.locations); + }).then(function () { + updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res)); + updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res)); + updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root)); + }).then(function () { + events.emit('verbose', 'Prepared android project successfully'); + }); +}; + +module.exports.clean = function (options) { + // A cordovaProject isn't passed into the clean() function, because it might have + // been called from the platform shell script rather than the CLI. Check for the + // noPrepare option passed in by the non-CLI clean script. If that's present, or if + // there's no config.xml found at the project root, then don't clean prepared files. + var projectRoot = path.resolve(this.root, '../..'); + if ((options && options.noPrepare) || !fs.existsSync(this.locations.configXml) || + !fs.existsSync(this.locations.configXml)) { + return Q(); + } + + var projectConfig = new ConfigParser(this.locations.configXml); + + var self = this; + return Q().then(function () { + cleanWww(projectRoot, self.locations); + cleanIcons(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res)); + cleanSplashes(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res)); + cleanFileResources(projectRoot, projectConfig, path.relative(projectRoot, self.locations.root)); + }); +}; + +/** + * Updates config files in project based on app's config.xml and config munge, + * generated by plugins. + * + * @param {ConfigParser} sourceConfig A project's configuration that will + * be merged into platform's config.xml + * @param {ConfigChanges} configMunger An initialized ConfigChanges instance + * for this platform. + * @param {Object} locations A map of locations for this platform + * + * @return {ConfigParser} An instance of ConfigParser, that + * represents current project's configuration. When returned, the + * configuration is already dumped to appropriate config.xml file. + */ +function updateConfigFilesFrom (sourceConfig, configMunger, locations) { + events.emit('verbose', 'Generating platform-specific config.xml from defaults for android at ' + locations.configXml); + + // First cleanup current config and merge project's one into own + // Overwrite platform config.xml with defaults.xml. + shell.cp('-f', locations.defaultConfigXml, locations.configXml); + + // Then apply config changes from global munge to all config files + // in project (including project's config) + configMunger.reapply_global_munge().save_all(); + + events.emit('verbose', 'Merging project\'s config.xml into platform-specific android config.xml'); + // Merge changes from app's config.xml into platform's one + var config = new ConfigParser(locations.configXml); + xmlHelpers.mergeXml(sourceConfig.doc.getroot(), + config.doc.getroot(), 'android', /* clobber= */true); + + config.write(); + return config; +} + +/** + * Logs all file operations via the verbose event stream, indented. + */ +function logFileOp (message) { + events.emit('verbose', ' ' + message); +} + +/** + * Updates platform 'www' directory by replacing it with contents of + * 'platform_www' and app www. Also copies project's overrides' folder into + * the platform 'www' folder + * + * @param {Object} cordovaProject An object which describes cordova project. + * @param {Object} destinations An object that contains destination + * paths for www files. + */ +function updateWww (cordovaProject, destinations) { + var sourceDirs = [ + path.relative(cordovaProject.root, cordovaProject.locations.www), + path.relative(cordovaProject.root, destinations.platformWww) + ]; + + // If project contains 'merges' for our platform, use them as another overrides + var merges_path = path.join(cordovaProject.root, 'merges', 'android'); + if (fs.existsSync(merges_path)) { + events.emit('verbose', 'Found "merges/android" folder. Copying its contents into the android project.'); + sourceDirs.push(path.join('merges', 'android')); + } + + var targetDir = path.relative(cordovaProject.root, destinations.www); + events.emit( + 'verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir); + FileUpdater.mergeAndUpdateDir( + sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp); +} + +/** + * Cleans all files from the platform 'www' directory. + */ +function cleanWww (projectRoot, locations) { + var targetDir = path.relative(projectRoot, locations.www); + events.emit('verbose', 'Cleaning ' + targetDir); + + // No source paths are specified, so mergeAndUpdateDir() will clear the target directory. + FileUpdater.mergeAndUpdateDir( + [], targetDir, { rootDir: projectRoot, all: true }, logFileOp); +} + +/** + * Updates project structure and AndroidManifest according to project's configuration. + * + * @param {ConfigParser} platformConfig A project's configuration that will + * be used to update project + * @param {Object} locations A map of locations for this platform + */ +function updateProjectAccordingTo (platformConfig, locations) { + // Update app name by editing res/values/strings.xml + var strings = xmlHelpers.parseElementtreeSync(locations.strings); + + var name = platformConfig.name(); + strings.find('string[@name="app_name"]').text = name.replace(/\'/g, '\\\''); + + var shortName = platformConfig.shortName && platformConfig.shortName(); + if (shortName && shortName !== name) { + strings.find('string[@name="launcher_name"]').text = shortName.replace(/\'/g, '\\\''); + } + + fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8'); + events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings); + + // Java packages cannot support dashes + var androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_'); + + var manifest = new AndroidManifest(locations.manifest); + var manifestId = manifest.getPackageId(); + + manifest.getActivity() + .setOrientation(platformConfig.getPreference('orientation')) + .setLaunchMode(findAndroidLaunchModePreference(platformConfig)); + + manifest.setVersionName(platformConfig.version()) + .setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version())) + .setPackageId(androidPkgName) + .setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android')) + .setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android')) + .setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android')) + .write(); + + // Java file paths shouldn't be hard coded + var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java'); + var java_files = shell.ls(javaPattern).filter(function (f) { + return shell.grep(/extends\s+CordovaActivity/g, f); + }); + + if (java_files.length === 0) { + throw new CordovaError('No Java files found that extend CordovaActivity.'); + } else if (java_files.length > 1) { + events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]); + } + + var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0])); + shell.mkdir('-p', path.dirname(destFile)); + shell.sed(/package [\w\.]*;/, 'package ' + androidPkgName + ';', java_files[0]).to(destFile); + events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile); + + var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin() ? + manifestId.toUpperCase() !== androidPkgName.toUpperCase() : + manifestId !== androidPkgName; + + if (removeOrigPkg) { + // If package was name changed we need to remove old java with main activity + shell.rm('-Rf', java_files[0]); + // remove any empty directories + var currentDir = path.dirname(java_files[0]); + var sourcesRoot = path.resolve(locations.root, 'src'); + while (currentDir !== sourcesRoot) { + if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) { + fs.rmdirSync(currentDir); + currentDir = path.resolve(currentDir, '..'); + } else { + break; + } + } + } +} + +// Consturct the default value for versionCode as +// PATCH + MINOR * 100 + MAJOR * 10000 +// see http://developer.android.com/tools/publishing/versioning.html +function default_versionCode (version) { + var nums = version.split('-')[0].split('.'); + var versionCode = 0; + if (+nums[0]) { + versionCode += +nums[0] * 10000; + } + if (+nums[1]) { + versionCode += +nums[1] * 100; + } + if (+nums[2]) { + versionCode += +nums[2]; + } + + events.emit('verbose', 'android-versionCode not found in config.xml. Generating a code based on version in config.xml (' + version + '): ' + versionCode); + return versionCode; +} + +function getImageResourcePath (resourcesDir, type, density, name, sourceName) { + if (/\.9\.png$/.test(sourceName)) { + name = name.replace(/\.png$/, '.9.png'); + } + var resourcePath = path.join(resourcesDir, (density ? type + '-' + density : type), name); + return resourcePath; +} + +function updateSplashes (cordovaProject, platformResourcesDir) { + var resources = cordovaProject.projectConfig.getSplashScreens('android'); + + // if there are "splash" elements in config.xml + if (resources.length === 0) { + events.emit('verbose', 'This app does not have splash screens defined'); + return; + } + + var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'drawable', 'screen.png'); + + var hadMdpi = false; + resources.forEach(function (resource) { + if (!resource.density) { + return; + } + if (resource.density === 'mdpi') { + hadMdpi = true; + } + var targetPath = getImageResourcePath( + platformResourcesDir, 'drawable', resource.density, 'screen.png', path.basename(resource.src)); + resourceMap[targetPath] = resource.src; + }); + + // There's no "default" drawable, so assume default == mdpi. + if (!hadMdpi && resources.defaultResource) { + var targetPath = getImageResourcePath( + platformResourcesDir, 'drawable', 'mdpi', 'screen.png', path.basename(resources.defaultResource.src)); + resourceMap[targetPath] = resources.defaultResource.src; + } + + events.emit('verbose', 'Updating splash screens at ' + platformResourcesDir); + FileUpdater.updatePaths( + resourceMap, { rootDir: cordovaProject.root }, logFileOp); +} + +function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) { + var resources = projectConfig.getSplashScreens('android'); + if (resources.length > 0) { + var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png'); + events.emit('verbose', 'Cleaning splash screens at ' + platformResourcesDir); + + // No source paths are specified in the map, so updatePaths() will delete the target files. + FileUpdater.updatePaths( + resourceMap, { rootDir: projectRoot, all: true }, logFileOp); + } +} + +function updateIcons (cordovaProject, platformResourcesDir) { + var icons = cordovaProject.projectConfig.getIcons('android'); + + // if there are icon elements in config.xml + if (icons.length === 0) { + events.emit('verbose', 'This app does not have launcher icons defined'); + return; + } + + var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'icon.png'); + + var android_icons = {}; + var default_icon; + // http://developer.android.com/design/style/iconography.html + var sizeToDensityMap = { + 36: 'ldpi', + 48: 'mdpi', + 72: 'hdpi', + 96: 'xhdpi', + 144: 'xxhdpi', + 192: 'xxxhdpi' + }; + // find the best matching icon for a given density or size + // @output android_icons + var parseIcon = function (icon, icon_size) { + // do I have a platform icon for that density already + var density = icon.density || sizeToDensityMap[icon_size]; + if (!density) { + // invalid icon defition ( or unsupported size) + return; + } + var previous = android_icons[density]; + if (previous && previous.platform) { + return; + } + android_icons[density] = icon; + }; + + // iterate over all icon elements to find the default icon and call parseIcon + for (var i = 0; i < icons.length; i++) { + var icon = icons[i]; + var size = icon.width; + if (!size) { + size = icon.height; + } + if (!size && !icon.density) { + if (default_icon) { + events.emit('verbose', 'Found extra default icon: ' + icon.src + ' (ignoring in favor of ' + default_icon.src + ')'); + } else { + default_icon = icon; + } + } else { + parseIcon(icon, size); + } + } + + // The source paths for icons and splashes are relative to + // project's config.xml location, so we use it as base path. + for (var density in android_icons) { + var targetPath = getImageResourcePath( + platformResourcesDir, 'mipmap', density, 'icon.png', path.basename(android_icons[density].src)); + resourceMap[targetPath] = android_icons[density].src; + } + + // There's no "default" drawable, so assume default == mdpi. + if (default_icon && !android_icons.mdpi) { + var defaultTargetPath = getImageResourcePath( + platformResourcesDir, 'mipmap', 'mdpi', 'icon.png', path.basename(default_icon.src)); + resourceMap[defaultTargetPath] = default_icon.src; + } + + events.emit('verbose', 'Updating icons at ' + platformResourcesDir); + FileUpdater.updatePaths( + resourceMap, { rootDir: cordovaProject.root }, logFileOp); +} + +function cleanIcons (projectRoot, projectConfig, platformResourcesDir) { + var icons = projectConfig.getIcons('android'); + if (icons.length > 0) { + var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png'); + events.emit('verbose', 'Cleaning icons at ' + platformResourcesDir); + + // No source paths are specified in the map, so updatePaths() will delete the target files. + FileUpdater.updatePaths( + resourceMap, { rootDir: projectRoot, all: true }, logFileOp); + } +} + +/** + * Gets a map containing resources of a specified name from all drawable folders in a directory. + */ +function mapImageResources (rootDir, subDir, type, resourceName) { + var pathMap = {}; + shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) { + var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName); + pathMap[imagePath] = null; + }); + return pathMap; +} + +function updateFileResources (cordovaProject, platformDir) { + var files = cordovaProject.projectConfig.getFileResources('android'); + + // if there are resource-file elements in config.xml + if (files.length === 0) { + events.emit('verbose', 'This app does not have additional resource files defined'); + return; + } + + var resourceMap = {}; + files.forEach(function (res) { + var targetPath = path.join(platformDir, res.target); + resourceMap[targetPath] = res.src; + }); + + events.emit('verbose', 'Updating resource files at ' + platformDir); + FileUpdater.updatePaths( + resourceMap, { rootDir: cordovaProject.root }, logFileOp); +} + +function cleanFileResources (projectRoot, projectConfig, platformDir) { + var files = projectConfig.getFileResources('android', true); + if (files.length > 0) { + events.emit('verbose', 'Cleaning resource files at ' + platformDir); + + var resourceMap = {}; + files.forEach(function (res) { + var filePath = path.join(platformDir, res.target); + resourceMap[filePath] = null; + }); + + FileUpdater.updatePaths( + resourceMap, { + rootDir: projectRoot, all: true}, logFileOp); + } +} + +/** + * Gets and validates 'AndroidLaunchMode' prepference from config.xml. Returns + * preference value and warns if it doesn't seems to be valid + * + * @param {ConfigParser} platformConfig A configParser instance for + * platform. + * + * @return {String} Preference's value from config.xml or + * default value, if there is no such preference. The default value is + * 'singleTop' + */ +function findAndroidLaunchModePreference (platformConfig) { + var launchMode = platformConfig.getPreference('AndroidLaunchMode'); + if (!launchMode) { + // Return a default value + return 'singleTop'; + } + + var expectedValues = ['standard', 'singleTop', 'singleTask', 'singleInstance']; + var valid = expectedValues.indexOf(launchMode) >= 0; + if (!valid) { + // Note: warn, but leave the launch mode as developer wanted, in case the list of options changes in the future + events.emit('warn', 'Unrecognized value for AndroidLaunchMode preference: ' + + launchMode + '. Expected values are: ' + expectedValues.join(', ')); + } + + return launchMode; +} diff --git a/platforms/android/cordova/lib/retry.js b/platforms/android/cordova/lib/retry.js new file mode 100644 index 0000000..c464b9d --- /dev/null +++ b/platforms/android/cordova/lib/retry.js @@ -0,0 +1,68 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +/* jshint node: true */ + +'use strict'; + +var events = require('cordova-common').events; + +/* + * Retry a promise-returning function a number of times, propagating its + * results on success or throwing its error on a failed final attempt. + * + * @arg {Number} attemts_left - The number of times to retry the passed call. + * @arg {Function} promiseFunction - A function that returns a promise. + * @arg {...} - Arguments to pass to promiseFunction. + * + * @returns {Promise} + */ +module.exports.retryPromise = function (attemts_left, promiseFunction) { + + // NOTE: + // get all trailing arguments, by skipping the first two (attemts_left and + // promiseFunction) because they shouldn't get passed to promiseFunction + var promiseFunctionArguments = Array.prototype.slice.call(arguments, 2); + + return promiseFunction.apply(undefined, promiseFunctionArguments).then( + + // on success pass results through + function onFulfilled (value) { + return value; + }, + + // on rejection either retry, or throw the error + function onRejected (error) { + + attemts_left -= 1; + + if (attemts_left < 1) { + throw error; + } + + events.emit('verbose', 'A retried call failed. Retrying ' + attemts_left + ' more time(s).'); + + // retry call self again with the same arguments, except attemts_left is now lower + var fullArguments = [attemts_left, promiseFunction].concat(promiseFunctionArguments); + return module.exports.retryPromise.apply(undefined, fullArguments); + } + ); +}; diff --git a/platforms/android/cordova/lib/run.js b/platforms/android/cordova/lib/run.js new file mode 100644 index 0000000..b97fce2 --- /dev/null +++ b/platforms/android/cordova/lib/run.js @@ -0,0 +1,132 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +/* jshint loopfunc:true */ + +var path = require('path'); +var build = require('./build'); +var emulator = require('./emulator'); +var device = require('./device'); +var Q = require('q'); +var events = require('cordova-common').events; + +function getInstallTarget (runOptions) { + var install_target; + if (runOptions.target) { + install_target = runOptions.target; + } else if (runOptions.device) { + install_target = '--device'; + } else if (runOptions.emulator) { + install_target = '--emulator'; + } + + return install_target; +} + +/** + * Runs the application on a device if available. If no device is found, it will + * use a started emulator. If no started emulators are found it will attempt + * to start an avd. If no avds are found it will error out. + * + * @param {Object} runOptions various run/build options. See Api.js build/run + * methods for reference. + * + * @return {Promise} + */ +module.exports.run = function (runOptions) { + + var self = this; + var install_target = getInstallTarget(runOptions); + + return Q().then(function () { + if (!install_target) { + // no target given, deploy to device if available, otherwise use the emulator. + return device.list().then(function (device_list) { + if (device_list.length > 0) { + events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.'); + install_target = device_list[0]; + } else { + events.emit('warn', 'No target specified and no devices found, deploying to emulator'); + install_target = '--emulator'; + } + }); + } + }).then(function () { + if (install_target === '--device') { + return device.resolveTarget(null); + } else if (install_target === '--emulator') { + // Give preference to any already started emulators. Else, start one. + return emulator.list_started().then(function (started) { + return started && started.length > 0 ? started[0] : emulator.start(); + }).then(function (emulatorId) { + return emulator.resolveTarget(emulatorId); + }); + } + // They specified a specific device/emulator ID. + return device.list().then(function (devices) { + if (devices.indexOf(install_target) > -1) { + return device.resolveTarget(install_target); + } + return emulator.list_started().then(function (started_emulators) { + if (started_emulators.indexOf(install_target) > -1) { + return emulator.resolveTarget(install_target); + } + return emulator.list_images().then(function (avds) { + // if target emulator isn't started, then start it. + for (var avd in avds) { + if (avds[avd].name === install_target) { + return emulator.start(install_target).then(function (emulatorId) { + return emulator.resolveTarget(emulatorId); + }); + } + } + return Q.reject('Target \'' + install_target + '\' not found, unable to run project'); + }); + }); + }); + }).then(function (resolvedTarget) { + // Better just call self.build, but we're doing some processing of + // build results (according to platformApi spec) so they are in different + // format than emulator.install expects. + // TODO: Update emulator/device.install to handle this change + return build.run.call(self, runOptions, resolvedTarget).then(function (buildResults) { + if (resolvedTarget.isEmulator) { + return emulator.wait_for_boot(resolvedTarget.target).then(function () { + return emulator.install(resolvedTarget, buildResults); + }); + } + return device.install(resolvedTarget, buildResults); + }); + }); +}; + +module.exports.help = function () { + console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]'); + console.log('Build options :'); + console.log(' --debug : Builds project in debug mode'); + console.log(' --release : Builds project in release mode'); + console.log(' --nobuild : Runs the currently built project without recompiling'); + console.log('Deploy options :'); + console.log(' --device : Will deploy the built project to a device'); + console.log(' --emulator : Will deploy the built project to an emulator if one exists'); + console.log(' --target= : Installs to the target with the specified id.'); + process.exit(0); +}; diff --git a/platforms/android/cordova/lib/start-emulator b/platforms/android/cordova/lib/start-emulator new file mode 100755 index 0000000..f96bdc3 --- /dev/null +++ b/platforms/android/cordova/lib/start-emulator @@ -0,0 +1,39 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var emulator = require('./emulator'), + args = process.argv; + +var install_target; +if(args.length > 2) { + if (args[2].substring(0, 9) == '--target=') { + install_target = args[2].substring(9, args[2].length); + } else { + console.error('ERROR : argument \'' + args[2] + '\' not recognized.'); + process.exit(2); + } +} + +emulator.start(install_target).done(null, function(err) { + console.error('ERROR: ' + err); + process.exit(2); +}); + diff --git a/platforms/android/cordova/lib/start-emulator.bat b/platforms/android/cordova/lib/start-emulator.bat new file mode 100644 index 0000000..6c237ea --- /dev/null +++ b/platforms/android/cordova/lib/start-emulator.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0start-emulator" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'start-emulator' script in 'cordova\lib' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/log b/platforms/android/cordova/log new file mode 100755 index 0000000..47f0605 --- /dev/null +++ b/platforms/android/cordova/log @@ -0,0 +1,36 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var log = require('./lib/log'), + reqs = require('./lib/check_reqs'), + args = process.argv; + +// Usage support for when args are given +if(args.length > 2) { + log.help(); +} else { + reqs.run().done(function() { + return log.run(); + }, function(err) { + console.error('ERROR: ' + err); + process.exit(2); + }); +} diff --git a/platforms/android/cordova/log.bat b/platforms/android/cordova/log.bat new file mode 100644 index 0000000..4b2b434 --- /dev/null +++ b/platforms/android/cordova/log.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0log" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'log' script in 'cordova' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/loggingHelper.js b/platforms/android/cordova/loggingHelper.js new file mode 100644 index 0000000..32b2ee0 --- /dev/null +++ b/platforms/android/cordova/loggingHelper.js @@ -0,0 +1,18 @@ +var CordovaLogger = require('cordova-common').CordovaLogger; + +module.exports = { + adjustLoggerLevel: function (opts) { + if (opts instanceof Array) { + opts.silent = opts.indexOf('--silent') !== -1; + opts.verbose = opts.indexOf('--verbose') !== -1; + } + + if (opts.silent) { + CordovaLogger.get().setLevel('error'); + } + + if (opts.verbose) { + CordovaLogger.get().setLevel('verbose'); + } + } +}; diff --git a/platforms/android/cordova/run b/platforms/android/cordova/run new file mode 100755 index 0000000..9544c1d --- /dev/null +++ b/platforms/android/cordova/run @@ -0,0 +1,53 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var Api = require('./Api'); +var nopt = require('nopt'); +var path = require('path'); + +// Support basic help commands +if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) + require('./lib/run').help(); + +// Do some basic argument parsing +var runOpts = nopt({ + 'verbose' : Boolean, + 'silent' : Boolean, + 'debug' : Boolean, + 'release' : Boolean, + 'nobuild': Boolean, + 'buildConfig' : path, + 'archs' : String, + 'device' : Boolean, + 'emulator': Boolean, + 'target' : String +}, { 'd' : '--verbose' }); + +// Make runOptions compatible with PlatformApi run method spec +runOpts.argv = runOpts.argv.remain; + +require('./loggingHelper').adjustLoggerLevel(runOpts); + +new Api().run(runOpts) +.catch(function(err) { + console.error(err, err.stack); + process.exit(2); +}); diff --git a/platforms/android/cordova/run.bat b/platforms/android/cordova/run.bat new file mode 100644 index 0000000..b0bc28b --- /dev/null +++ b/platforms/android/cordova/run.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0run" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'run' script in 'cordova' folder, aborting...>&2 + EXIT /B 1 +) \ No newline at end of file diff --git a/platforms/android/cordova/version b/platforms/android/cordova/version new file mode 100755 index 0000000..5d6e2b6 --- /dev/null +++ b/platforms/android/cordova/version @@ -0,0 +1,29 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +// Coho updates this line: +var VERSION = "7.1.0"; + +module.exports.version = VERSION; + +if (!module.parent) { + console.log(VERSION); +} diff --git a/platforms/android/cordova/version.bat b/platforms/android/cordova/version.bat new file mode 100644 index 0000000..3610c17 --- /dev/null +++ b/platforms/android/cordova/version.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script_path="%~dp0version" +IF EXIST %script_path% ( + node %script_path% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'version' script in 'cordova' folder, aborting...>&2 + EXIT /B 1 +) diff --git a/platforms/android/platform_www/cordova-js-src/android/nativeapiprovider.js b/platforms/android/platform_www/cordova-js-src/android/nativeapiprovider.js new file mode 100644 index 0000000..2e9aa67 --- /dev/null +++ b/platforms/android/platform_www/cordova-js-src/android/nativeapiprovider.js @@ -0,0 +1,36 @@ +/* + * 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. +*/ + +/** + * Exports the ExposedJsApi.java object if available, otherwise exports the PromptBasedNativeApi. + */ + +var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi'); +var currentApi = nativeApi; + +module.exports = { + get: function() { return currentApi; }, + setPreferPrompt: function(value) { + currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi; + }, + // Used only by tests. + set: function(value) { + currentApi = value; + } +}; diff --git a/platforms/android/platform_www/cordova-js-src/android/promptbasednativeapi.js b/platforms/android/platform_www/cordova-js-src/android/promptbasednativeapi.js new file mode 100644 index 0000000..f7fb6bc --- /dev/null +++ b/platforms/android/platform_www/cordova-js-src/android/promptbasednativeapi.js @@ -0,0 +1,35 @@ +/* + * 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. +*/ + +/** + * Implements the API of ExposedJsApi.java, but uses prompt() to communicate. + * This is used pre-JellyBean, where addJavascriptInterface() is disabled. + */ + +module.exports = { + exec: function(bridgeSecret, service, action, callbackId, argsJson) { + return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId])); + }, + setNativeToJsBridgeMode: function(bridgeSecret, value) { + prompt(value, 'gap_bridge_mode:' + bridgeSecret); + }, + retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) { + return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret); + } +}; diff --git a/platforms/android/platform_www/cordova-js-src/exec.js b/platforms/android/platform_www/cordova-js-src/exec.js new file mode 100644 index 0000000..f73d87a --- /dev/null +++ b/platforms/android/platform_www/cordova-js-src/exec.js @@ -0,0 +1,297 @@ +/* + * + * 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. + * +*/ + +/** + * Execute a cordova command. It is up to the native side whether this action + * is synchronous or asynchronous. The native side can return: + * Synchronous: PluginResult object as a JSON string + * Asynchronous: Empty string "" + * If async, the native side will cordova.callbackSuccess or cordova.callbackError, + * depending upon the result of the action. + * + * @param {Function} success The success callback + * @param {Function} fail The fail callback + * @param {String} service The name of the service to use + * @param {String} action Action to be run in cordova + * @param {String[]} [args] Zero or more arguments to pass to the method + */ +var cordova = require('cordova'), + nativeApiProvider = require('cordova/android/nativeapiprovider'), + utils = require('cordova/utils'), + base64 = require('cordova/base64'), + channel = require('cordova/channel'), + jsToNativeModes = { + PROMPT: 0, + JS_OBJECT: 1 + }, + nativeToJsModes = { + // Polls for messages using the JS->Native bridge. + POLLING: 0, + // For LOAD_URL to be viable, it would need to have a work-around for + // the bug where the soft-keyboard gets dismissed when a message is sent. + LOAD_URL: 1, + // For the ONLINE_EVENT to be viable, it would need to intercept all event + // listeners (both through addEventListener and window.ononline) as well + // as set the navigator property itself. + ONLINE_EVENT: 2, + EVAL_BRIDGE: 3 + }, + jsToNativeBridgeMode, // Set lazily. + nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE, + pollEnabled = false, + bridgeSecret = -1; + +var messagesFromNative = []; +var isProcessing = false; +var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve(); +var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); }; + +function androidExec(success, fail, service, action, args) { + if (bridgeSecret < 0) { + // If we ever catch this firing, we'll need to queue up exec()s + // and fire them once we get a secret. For now, I don't think + // it's possible for exec() to be called since plugins are parsed but + // not run until until after onNativeReady. + throw new Error('exec() called without bridgeSecret'); + } + // Set default bridge modes if they have not already been set. + // By default, we use the failsafe, since addJavascriptInterface breaks too often + if (jsToNativeBridgeMode === undefined) { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); + } + + // If args is not provided, default to an empty array + args = args || []; + + // Process any ArrayBuffers in the args into a string. + for (var i = 0; i < args.length; i++) { + if (utils.typeName(args[i]) == 'ArrayBuffer') { + args[i] = base64.fromArrayBuffer(args[i]); + } + } + + var callbackId = service + cordova.callbackId++, + argsJson = JSON.stringify(args); + if (success || fail) { + cordova.callbacks[callbackId] = {success:success, fail:fail}; + } + + var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson); + // If argsJson was received by Java as null, try again with the PROMPT bridge mode. + // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666. + if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); + androidExec(success, fail, service, action, args); + androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); + } else if (msgs) { + messagesFromNative.push(msgs); + // Always process async to avoid exceptions messing up stack. + nextTick(processMessages); + } +} + +androidExec.init = function() { + //CB-11828 + //This failsafe checks the version of Android and if it's Jellybean, it switches it to + //using the Online Event bridge for communicating from Native to JS + // + //It's ugly, but it's necessary. + var check = navigator.userAgent.toLowerCase().match(/android\s[0-9].[0-9]/); + var version_code = check && check[0].match(/4.[0-3].*/); + if (version_code != null && nativeToJsBridgeMode == nativeToJsModes.EVAL_BRIDGE) { + nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT; + } + + bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode); + channel.onNativeReady.fire(); +}; + +function pollOnceFromOnlineEvent() { + pollOnce(true); +} + +function pollOnce(opt_fromOnlineEvent) { + if (bridgeSecret < 0) { + // This can happen when the NativeToJsMessageQueue resets the online state on page transitions. + // We know there's nothing to retrieve, so no need to poll. + return; + } + var msgs = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent); + if (msgs) { + messagesFromNative.push(msgs); + // Process sync since we know we're already top-of-stack. + processMessages(); + } +} + +function pollingTimerFunc() { + if (pollEnabled) { + pollOnce(); + setTimeout(pollingTimerFunc, 50); + } +} + +function hookOnlineApis() { + function proxyEvent(e) { + cordova.fireWindowEvent(e.type); + } + // The network module takes care of firing online and offline events. + // It currently fires them only on document though, so we bridge them + // to window here (while first listening for exec()-releated online/offline + // events). + window.addEventListener('online', pollOnceFromOnlineEvent, false); + window.addEventListener('offline', pollOnceFromOnlineEvent, false); + cordova.addWindowEventHandler('online'); + cordova.addWindowEventHandler('offline'); + document.addEventListener('online', proxyEvent, false); + document.addEventListener('offline', proxyEvent, false); +} + +hookOnlineApis(); + +androidExec.jsToNativeModes = jsToNativeModes; +androidExec.nativeToJsModes = nativeToJsModes; + +androidExec.setJsToNativeBridgeMode = function(mode) { + if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) { + mode = jsToNativeModes.PROMPT; + } + nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT); + jsToNativeBridgeMode = mode; +}; + +androidExec.setNativeToJsBridgeMode = function(mode) { + if (mode == nativeToJsBridgeMode) { + return; + } + if (nativeToJsBridgeMode == nativeToJsModes.POLLING) { + pollEnabled = false; + } + + nativeToJsBridgeMode = mode; + // Tell the native side to switch modes. + // Otherwise, it will be set by androidExec.init() + if (bridgeSecret >= 0) { + nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode); + } + + if (mode == nativeToJsModes.POLLING) { + pollEnabled = true; + setTimeout(pollingTimerFunc, 1); + } +}; + +function buildPayload(payload, message) { + var payloadKind = message.charAt(0); + if (payloadKind == 's') { + payload.push(message.slice(1)); + } else if (payloadKind == 't') { + payload.push(true); + } else if (payloadKind == 'f') { + payload.push(false); + } else if (payloadKind == 'N') { + payload.push(null); + } else if (payloadKind == 'n') { + payload.push(+message.slice(1)); + } else if (payloadKind == 'A') { + var data = message.slice(1); + payload.push(base64.toArrayBuffer(data)); + } else if (payloadKind == 'S') { + payload.push(window.atob(message.slice(1))); + } else if (payloadKind == 'M') { + var multipartMessages = message.slice(1); + while (multipartMessages !== "") { + var spaceIdx = multipartMessages.indexOf(' '); + var msgLen = +multipartMessages.slice(0, spaceIdx); + var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen); + multipartMessages = multipartMessages.slice(spaceIdx + msgLen + 1); + buildPayload(payload, multipartMessage); + } + } else { + payload.push(JSON.parse(message)); + } +} + +// Processes a single message, as encoded by NativeToJsMessageQueue.java. +function processMessage(message) { + var firstChar = message.charAt(0); + if (firstChar == 'J') { + // This is deprecated on the .java side. It doesn't work with CSP enabled. + eval(message.slice(1)); + } else if (firstChar == 'S' || firstChar == 'F') { + var success = firstChar == 'S'; + var keepCallback = message.charAt(1) == '1'; + var spaceIdx = message.indexOf(' ', 2); + var status = +message.slice(2, spaceIdx); + var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1); + var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx); + var payloadMessage = message.slice(nextSpaceIdx + 1); + var payload = []; + buildPayload(payload, payloadMessage); + cordova.callbackFromNative(callbackId, success, status, payload, keepCallback); + } else { + console.log("processMessage failed: invalid message: " + JSON.stringify(message)); + } +} + +function processMessages() { + // Check for the reentrant case. + if (isProcessing) { + return; + } + if (messagesFromNative.length === 0) { + return; + } + isProcessing = true; + try { + var msg = popMessageFromQueue(); + // The Java side can send a * message to indicate that it + // still has messages waiting to be retrieved. + if (msg == '*' && messagesFromNative.length === 0) { + nextTick(pollOnce); + return; + } + processMessage(msg); + } finally { + isProcessing = false; + if (messagesFromNative.length > 0) { + nextTick(processMessages); + } + } +} + +function popMessageFromQueue() { + var messageBatch = messagesFromNative.shift(); + if (messageBatch == '*') { + return '*'; + } + + var spaceIdx = messageBatch.indexOf(' '); + var msgLen = +messageBatch.slice(0, spaceIdx); + var message = messageBatch.substr(spaceIdx + 1, msgLen); + messageBatch = messageBatch.slice(spaceIdx + msgLen + 1); + if (messageBatch) { + messagesFromNative.unshift(messageBatch); + } + return message; +} + +module.exports = androidExec; diff --git a/platforms/android/platform_www/cordova-js-src/platform.js b/platforms/android/platform_www/cordova-js-src/platform.js new file mode 100644 index 0000000..2bfd024 --- /dev/null +++ b/platforms/android/platform_www/cordova-js-src/platform.js @@ -0,0 +1,125 @@ +/* + * + * 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. + * +*/ + +// The last resume event that was received that had the result of a plugin call. +var lastResumeEvent = null; + +module.exports = { + id: 'android', + bootstrap: function() { + var channel = require('cordova/channel'), + cordova = require('cordova'), + exec = require('cordova/exec'), + modulemapper = require('cordova/modulemapper'); + + // Get the shared secret needed to use the bridge. + exec.init(); + + // TODO: Extract this as a proper plugin. + modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app'); + + var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App'; + + // Inject a listener for the backbutton on the document. + var backButtonChannel = cordova.addDocumentEventHandler('backbutton'); + backButtonChannel.onHasSubscribersChange = function() { + // If we just attached the first handler or detached the last handler, + // let native know we need to override the back button. + exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]); + }; + + // Add hardware MENU and SEARCH button handlers + cordova.addDocumentEventHandler('menubutton'); + cordova.addDocumentEventHandler('searchbutton'); + + function bindButtonChannel(buttonName) { + // generic button bind used for volumeup/volumedown buttons + var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button'); + volumeButtonChannel.onHasSubscribersChange = function() { + exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]); + }; + } + // Inject a listener for the volume buttons on the document. + bindButtonChannel('volumeup'); + bindButtonChannel('volumedown'); + + // The resume event is not "sticky", but it is possible that the event + // will contain the result of a plugin call. We need to ensure that the + // plugin result is delivered even after the event is fired (CB-10498) + var cordovaAddEventListener = document.addEventListener; + + document.addEventListener = function(evt, handler, capture) { + cordovaAddEventListener(evt, handler, capture); + + if (evt === 'resume' && lastResumeEvent) { + handler(lastResumeEvent); + } + }; + + // Let native code know we are all done on the JS side. + // Native code will then un-hide the WebView. + channel.onCordovaReady.subscribe(function() { + exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []); + exec(null, null, APP_PLUGIN_NAME, "show", []); + }); + } +}; + +function onMessageFromNative(msg) { + var cordova = require('cordova'); + var action = msg.action; + + switch (action) + { + // Button events + case 'backbutton': + case 'menubutton': + case 'searchbutton': + // App life cycle events + case 'pause': + // Volume events + case 'volumedownbutton': + case 'volumeupbutton': + cordova.fireDocumentEvent(action); + break; + case 'resume': + if(arguments.length > 1 && msg.pendingResult) { + if(arguments.length === 2) { + msg.pendingResult.result = arguments[1]; + } else { + // The plugin returned a multipart message + var res = []; + for(var i = 1; i < arguments.length; i++) { + res.push(arguments[i]); + } + msg.pendingResult.result = res; + } + + // Save the plugin result so that it can be delivered to the js + // even if they miss the initial firing of the event + lastResumeEvent = msg; + } + cordova.fireDocumentEvent(action, msg); + break; + default: + throw new Error('Unknown event action ' + action); + } +} diff --git a/platforms/android/platform_www/cordova-js-src/plugin/android/app.js b/platforms/android/platform_www/cordova-js-src/plugin/android/app.js new file mode 100644 index 0000000..22cf96e --- /dev/null +++ b/platforms/android/platform_www/cordova-js-src/plugin/android/app.js @@ -0,0 +1,108 @@ +/* + * + * 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. + * +*/ + +var exec = require('cordova/exec'); +var APP_PLUGIN_NAME = Number(require('cordova').platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App'; + +module.exports = { + /** + * Clear the resource cache. + */ + clearCache:function() { + exec(null, null, APP_PLUGIN_NAME, "clearCache", []); + }, + + /** + * Load the url into the webview or into new browser instance. + * + * @param url The URL to load + * @param props Properties that can be passed in to the activity: + * wait: int => wait msec before loading URL + * loadingDialog: "Title,Message" => display a native loading dialog + * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error + * clearHistory: boolean => clear webview history (default=false) + * openExternal: boolean => open in a new browser (default=false) + * + * Example: + * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); + */ + loadUrl:function(url, props) { + exec(null, null, APP_PLUGIN_NAME, "loadUrl", [url, props]); + }, + + /** + * Cancel loadUrl that is waiting to be loaded. + */ + cancelLoadUrl:function() { + exec(null, null, APP_PLUGIN_NAME, "cancelLoadUrl", []); + }, + + /** + * Clear web history in this web view. + * Instead of BACK button loading the previous web page, it will exit the app. + */ + clearHistory:function() { + exec(null, null, APP_PLUGIN_NAME, "clearHistory", []); + }, + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + backHistory:function() { + exec(null, null, APP_PLUGIN_NAME, "backHistory", []); + }, + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ + overrideBackbutton:function(override) { + exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [override]); + }, + + /** + * Override the default behavior of the Android volume button. + * If overridden, when the volume button is pressed, the "volume[up|down]button" + * JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "volume[up|down]button" event, this is automatically done. + * + * @param button volumeup, volumedown + * @param override T=override, F=cancel override + */ + overrideButton:function(button, override) { + exec(null, null, APP_PLUGIN_NAME, "overrideButton", [button, override]); + }, + + /** + * Exit and terminate the application. + */ + exitApp:function() { + return exec(null, null, APP_PLUGIN_NAME, "exitApp", []); + } +}; diff --git a/platforms/android/platform_www/cordova.js b/platforms/android/platform_www/cordova.js new file mode 100644 index 0000000..1f2cdee --- /dev/null +++ b/platforms/android/platform_www/cordova.js @@ -0,0 +1,2188 @@ +// Platform: android +// 4450a4cea50616e080a82e8ede9e3d6a1fe3c3ec +/* + 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. +*/ +;(function() { +var PLATFORM_VERSION_BUILD_LABEL = '7.1.0'; +// file: src/scripts/require.js + +/* jshint -W079 */ +/* jshint -W020 */ + +var require; +var define; + +(function () { + var modules = {}; + // Stack of moduleIds currently being built. + var requireStack = []; + // Map of module ID -> index into requireStack of modules currently being built. + var inProgressModules = {}; + var SEPARATOR = '.'; + + function build (module) { + var factory = module.factory; + var localRequire = function (id) { + var resultantId = id; + // Its a relative path, so lop off the last portion and add the id (minus "./") + if (id.charAt(0) === '.') { + resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2); + } + return require(resultantId); + }; + module.exports = {}; + delete module.factory; + factory(localRequire, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw 'module ' + id + ' not found'; + } else if (id in inProgressModules) { + var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id; + throw 'Cycle in require graph: ' + cycle; + } + if (modules[id].factory) { + try { + inProgressModules[id] = requireStack.length; + requireStack.push(id); + return build(modules[id]); + } finally { + delete inProgressModules[id]; + requireStack.pop(); + } + } + return modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw 'module ' + id + ' already defined'; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + + define.moduleMap = modules; +})(); + +// Export for use in node +if (typeof module === 'object' && typeof require === 'function') { + module.exports.require = require; + module.exports.define = define; +} + +// file: src/cordova.js +define("cordova", function(require, exports, module) { + +// Workaround for Windows 10 in hosted environment case +// http://www.w3.org/html/wg/drafts/html/master/browsers.html#named-access-on-the-window-object +if (window.cordova && !(window.cordova instanceof HTMLElement)) { // eslint-disable-line no-undef + throw new Error('cordova already defined'); +} + +var channel = require('cordova/channel'); +var platform = require('cordova/platform'); + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}; +var windowEventHandlers = {}; + +document.addEventListener = function (evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] !== 'undefined') { + documentEventHandlers[e].subscribe(handler); + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function (evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] !== 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function (evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubscribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] !== 'undefined') { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function (evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubscribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] !== 'undefined') { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent (type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +/* eslint-disable no-undef */ +var cordova = { + define: define, + require: require, + version: PLATFORM_VERSION_BUILD_LABEL, + platformVersion: PLATFORM_VERSION_BUILD_LABEL, + platformId: platform.id, + + /* eslint-enable no-undef */ + + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler: function (event) { + return (windowEventHandlers[event] = channel.create(event)); + }, + addStickyDocumentEventHandler: function (event) { + return (documentEventHandlers[event] = channel.createSticky(event)); + }, + addDocumentEventHandler: function (event) { + return (documentEventHandlers[event] = channel.create(event)); + }, + removeWindowEventHandler: function (event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler: function (event) { + delete documentEventHandlers[event]; + }, + /** + * Retrieve original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function () { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + * bNoDetach is required for events which cause an exception which needs to be caught in native code + */ + fireDocumentEvent: function (type, data, bNoDetach) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] !== 'undefined') { + if (bNoDetach) { + documentEventHandlers[type].fire(evt); + } else { + setTimeout(function () { + // Fire deviceready on listeners that were registered before cordova.js was loaded. + if (type === 'deviceready') { + document.dispatchEvent(evt); + } + documentEventHandlers[type].fire(evt); + }, 0); + } + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function (type, data) { + var evt = createEvent(type, data); + if (typeof windowEventHandlers[type] !== 'undefined') { + setTimeout(function () { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + + /** + * Plugin callback mechanism. + */ + // Randomize the starting callbackId to avoid collisions after refreshing or navigating. + // This way, it's very unlikely that any new callback would get the same callbackId as an old callback. + callbackId: Math.floor(Math.random() * 2000000000), + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + */ + callbackSuccess: function (callbackId, args) { + cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback); + }, + + /** + * Called by native code when returning error result from an action. + */ + callbackError: function (callbackId, args) { + // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative. + // Derive success from status. + cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback); + }, + + /** + * Called by native code when returning the result from an action. + */ + callbackFromNative: function (callbackId, isSuccess, status, args, keepCallback) { + try { + var callback = cordova.callbacks[callbackId]; + if (callback) { + if (isSuccess && status === cordova.callbackStatus.OK) { + callback.success && callback.success.apply(null, args); + } else if (!isSuccess) { + callback.fail && callback.fail.apply(null, args); + } + /* + else + Note, this case is intentionally not caught. + this can happen if isSuccess is true, but callbackStatus is NO_RESULT + which is used to remove a callback from the list without calling the callbacks + typically keepCallback is false in this case + */ + // Clear callback if not expecting any more results + if (!keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + } catch (err) { + var msg = 'Error in ' + (isSuccess ? 'Success' : 'Error') + ' callbackId: ' + callbackId + ' : ' + err; + console && console.log && console.log(msg); + cordova.fireWindowEvent('cordovacallbackerror', { 'message': msg }); + throw err; + } + }, + addConstructor: function (func) { + channel.onCordovaReady.subscribe(function () { + try { + func(); + } catch (e) { + console.log('Failed to run constructor: ' + e); + } + }); + } +}; + +module.exports = cordova; + +}); + +// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js +define("cordova/android/nativeapiprovider", function(require, exports, module) { + +/** + * Exports the ExposedJsApi.java object if available, otherwise exports the PromptBasedNativeApi. + */ + +var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi'); +var currentApi = nativeApi; + +module.exports = { + get: function() { return currentApi; }, + setPreferPrompt: function(value) { + currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi; + }, + // Used only by tests. + set: function(value) { + currentApi = value; + } +}; + +}); + +// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js +define("cordova/android/promptbasednativeapi", function(require, exports, module) { + +/** + * Implements the API of ExposedJsApi.java, but uses prompt() to communicate. + * This is used pre-JellyBean, where addJavascriptInterface() is disabled. + */ + +module.exports = { + exec: function(bridgeSecret, service, action, callbackId, argsJson) { + return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId])); + }, + setNativeToJsBridgeMode: function(bridgeSecret, value) { + prompt(value, 'gap_bridge_mode:' + bridgeSecret); + }, + retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) { + return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret); + } +}; + +}); + +// file: src/common/argscheck.js +define("cordova/argscheck", function(require, exports, module) { + +var utils = require('cordova/utils'); + +var moduleExports = module.exports; + +var typeMap = { + 'A': 'Array', + 'D': 'Date', + 'N': 'Number', + 'S': 'String', + 'F': 'Function', + 'O': 'Object' +}; + +function extractParamName (callee, argIndex) { + return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex]; +} + +function checkArgs (spec, functionName, args, opt_callee) { + if (!moduleExports.enableChecks) { + return; + } + var errMsg = null; + var typeName; + for (var i = 0; i < spec.length; ++i) { + var c = spec.charAt(i); + var cUpper = c.toUpperCase(); + var arg = args[i]; + // Asterix means allow anything. + if (c === '*') { + continue; + } + typeName = utils.typeName(arg); + if ((arg === null || arg === undefined) && c === cUpper) { + continue; + } + if (typeName !== typeMap[cUpper]) { + errMsg = 'Expected ' + typeMap[cUpper]; + break; + } + } + if (errMsg) { + errMsg += ', but got ' + typeName + '.'; + errMsg = 'Wrong type for parameter "' + extractParamName(opt_callee || args.callee, i) + '" of ' + functionName + ': ' + errMsg; + // Don't log when running unit tests. + if (typeof jasmine === 'undefined') { + console.error(errMsg); + } + throw TypeError(errMsg); + } +} + +function getValue (value, defaultValue) { + return value === undefined ? defaultValue : value; +} + +moduleExports.checkArgs = checkArgs; +moduleExports.getValue = getValue; +moduleExports.enableChecks = true; + +}); + +// file: src/common/base64.js +define("cordova/base64", function(require, exports, module) { + +var base64 = exports; + +base64.fromArrayBuffer = function (arrayBuffer) { + var array = new Uint8Array(arrayBuffer); + return uint8ToBase64(array); +}; + +base64.toArrayBuffer = function (str) { + var decodedStr = typeof atob !== 'undefined' ? atob(str) : Buffer.from(str, 'base64').toString('binary'); // eslint-disable-line no-undef + var arrayBuffer = new ArrayBuffer(decodedStr.length); + var array = new Uint8Array(arrayBuffer); + for (var i = 0, len = decodedStr.length; i < len; i++) { + array[i] = decodedStr.charCodeAt(i); + } + return arrayBuffer; +}; + +// ------------------------------------------------------------------------------ + +/* This code is based on the performance tests at http://jsperf.com/b64tests + * This 12-bit-at-a-time algorithm was the best performing version on all + * platforms tested. + */ + +var b64_6bit = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +var b64_12bit; + +var b64_12bitTable = function () { + b64_12bit = []; + for (var i = 0; i < 64; i++) { + for (var j = 0; j < 64; j++) { + b64_12bit[i * 64 + j] = b64_6bit[i] + b64_6bit[j]; + } + } + b64_12bitTable = function () { return b64_12bit; }; + return b64_12bit; +}; + +function uint8ToBase64 (rawData) { + var numBytes = rawData.byteLength; + var output = ''; + var segment; + var table = b64_12bitTable(); + for (var i = 0; i < numBytes - 2; i += 3) { + segment = (rawData[i] << 16) + (rawData[i + 1] << 8) + rawData[i + 2]; + output += table[segment >> 12]; + output += table[segment & 0xfff]; + } + if (numBytes - i === 2) { + segment = (rawData[i] << 16) + (rawData[i + 1] << 8); + output += table[segment >> 12]; + output += b64_6bit[(segment & 0xfff) >> 6]; + output += '='; + } else if (numBytes - i === 1) { + segment = (rawData[i] << 16); + output += table[segment >> 12]; + output += '=='; + } + return output; +} + +}); + +// file: src/common/builder.js +define("cordova/builder", function(require, exports, module) { + +var utils = require('cordova/utils'); + +function each (objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function clobber (obj, key, value) { + exports.replaceHookForTesting(obj, key); + var needsProperty = false; + try { + obj[key] = value; + } catch (e) { + needsProperty = true; + } + // Getters can only be overridden by getters. + if (needsProperty || obj[key] !== value) { + utils.defineGetter(obj, key, function () { + return value; + }); + } +} + +function assignOrWrapInDeprecateGetter (obj, key, value, message) { + if (message) { + utils.defineGetter(obj, key, function () { + console.log(message); + delete obj[key]; + clobber(obj, key, value); + return value; + }); + } else { + clobber(obj, key, value); + } +} + +function include (parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated); + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated); + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] === 'undefined') { + assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated); + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch (e) { + utils.alert('Exception building Cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge (target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (target.prototype && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + clobber(target.prototype, prop, src[prop]); + } else { + if (typeof src[prop] === 'object' && typeof target[prop] === 'object') { + recursiveMerge(target[prop], src[prop]); + } else { + clobber(target, prop, src[prop]); + } + } + } + } +} + +exports.buildIntoButDoNotClobber = function (objects, target) { + include(target, objects, false, false); +}; +exports.buildIntoAndClobber = function (objects, target) { + include(target, objects, true, false); +}; +exports.buildIntoAndMerge = function (objects, target) { + include(target, objects, true, true); +}; +exports.recursiveMerge = recursiveMerge; +exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter; +exports.replaceHookForTesting = function () {}; + +}); + +// file: src/common/channel.js +define("cordova/channel", function(require, exports, module) { + +var utils = require('cordova/utils'); +var nextGuid = 1; + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization, as well as for custom events thereafter. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded* Internal event that is received when the web page is loaded and parsed. + * onNativeReady* Internal event that indicates the Cordova native side is ready. + * onCordovaReady* Internal event fired when all Cordova JavaScript objects have been created. + * onDeviceReady* User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * + * The events marked with an * are sticky. Once they have fired, they will stay in the fired state. + * All listeners that subscribe after the event is fired will be executed right away. + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + */ +var Channel = function (type, sticky) { + this.type = type; + // Map of guid -> function. + this.handlers = {}; + // 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired. + this.state = sticky ? 1 : 0; + // Used in sticky mode to remember args passed to fire(). + this.fireArgs = null; + // Used by onHasSubscribersChange to know if there are any listeners. + this.numHandlers = 0; + // Function that is called when the first listener is subscribed, or when + // the last listener is unsubscribed. + this.onHasSubscribersChange = null; +}; +var channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. All channels must be sticky channels. + */ + join: function (h, c) { + var len = c.length; + var i = len; + var f = function () { + if (!(--i)) h(); + }; + for (var j = 0; j < len; j++) { + if (c[j].state === 0) { + throw Error('Can only use join with sticky channels.'); + } + c[j].subscribe(f); + } + if (!len) h(); + }, + /* eslint-disable no-return-assign */ + create: function (type) { + return channel[type] = new Channel(type, false); + }, + createSticky: function (type) { + return channel[type] = new Channel(type, true); + }, + /* eslint-enable no-return-assign */ + /** + * cordova Channels that must fire before "deviceready" is fired. + */ + deviceReadyChannelsArray: [], + deviceReadyChannelsMap: {}, + + /** + * Indicate that a feature needs to be initialized before it is ready to be used. + * This holds up Cordova's "deviceready" event until the feature has been initialized + * and Cordova.initComplete(feature) is called. + * + * @param feature {String} The unique feature name + */ + waitForInitialization: function (feature) { + if (feature) { + var c = channel[feature] || this.createSticky(feature); + this.deviceReadyChannelsMap[feature] = c; + this.deviceReadyChannelsArray.push(c); + } + }, + + /** + * Indicate that initialization code has completed and the feature is ready to be used. + * + * @param feature {String} The unique feature name + */ + initializationComplete: function (feature) { + var c = this.deviceReadyChannelsMap[feature]; + if (c) { + c.fire(); + } + } +}; + +function checkSubscriptionArgument (argument) { + if (typeof argument !== 'function' && typeof argument.handleEvent !== 'function') { + throw new Error( + 'Must provide a function or an EventListener object ' + + 'implementing the handleEvent interface.' + ); + } +} + +/** + * Subscribes the given function to the channel. Any time that + * Channel.fire is called so too will the function. + * Optionally specify an execution context for the function + * and a guid that can be used to stop subscribing to the channel. + * Returns the guid. + */ +Channel.prototype.subscribe = function (eventListenerOrFunction, eventListener) { + checkSubscriptionArgument(eventListenerOrFunction); + var handleEvent, guid; + + if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') { + // Received an EventListener object implementing the handleEvent interface + handleEvent = eventListenerOrFunction.handleEvent; + eventListener = eventListenerOrFunction; + } else { + // Received a function to handle event + handleEvent = eventListenerOrFunction; + } + + if (this.state === 2) { + handleEvent.apply(eventListener || this, this.fireArgs); + return; + } + + guid = eventListenerOrFunction.observer_guid; + if (typeof eventListener === 'object') { + handleEvent = utils.close(eventListener, handleEvent); + } + + if (!guid) { + // First time any channel has seen this subscriber + guid = '' + nextGuid++; + } + handleEvent.observer_guid = guid; + eventListenerOrFunction.observer_guid = guid; + + // Don't add the same handler more than once. + if (!this.handlers[guid]) { + this.handlers[guid] = handleEvent; + this.numHandlers++; + if (this.numHandlers === 1) { + this.onHasSubscribersChange && this.onHasSubscribersChange(); + } + } +}; + +/** + * Unsubscribes the function with the given guid from the channel. + */ +Channel.prototype.unsubscribe = function (eventListenerOrFunction) { + checkSubscriptionArgument(eventListenerOrFunction); + var handleEvent, guid, handler; + + if (eventListenerOrFunction && typeof eventListenerOrFunction === 'object') { + // Received an EventListener object implementing the handleEvent interface + handleEvent = eventListenerOrFunction.handleEvent; + } else { + // Received a function to handle event + handleEvent = eventListenerOrFunction; + } + + guid = handleEvent.observer_guid; + handler = this.handlers[guid]; + if (handler) { + delete this.handlers[guid]; + this.numHandlers--; + if (this.numHandlers === 0) { + this.onHasSubscribersChange && this.onHasSubscribersChange(); + } + } +}; + +/** + * Calls all functions subscribed to this channel. + */ +Channel.prototype.fire = function (e) { + var fail = false; // eslint-disable-line no-unused-vars + var fireArgs = Array.prototype.slice.call(arguments); + // Apply stickiness. + if (this.state === 1) { + this.state = 2; + this.fireArgs = fireArgs; + } + if (this.numHandlers) { + // Copy the values first so that it is safe to modify it from within + // callbacks. + var toCall = []; + for (var item in this.handlers) { + toCall.push(this.handlers[item]); + } + for (var i = 0; i < toCall.length; ++i) { + toCall[i].apply(this, fireArgs); + } + if (this.state === 2 && this.numHandlers) { + this.numHandlers = 0; + this.handlers = {}; + this.onHasSubscribersChange && this.onHasSubscribersChange(); + } + } +}; + +// defining them here so they are ready super fast! +// DOM event that is received when the web page is loaded and parsed. +channel.createSticky('onDOMContentLoaded'); + +// Event to indicate the Cordova native side is ready. +channel.createSticky('onNativeReady'); + +// Event to indicate that all Cordova JavaScript objects have been created +// and it's time to run plugin constructors. +channel.createSticky('onCordovaReady'); + +// Event to indicate that all automatically loaded JS plugins are loaded and ready. +// FIXME remove this +channel.createSticky('onPluginsReady'); + +// Event to indicate that Cordova is ready +channel.createSticky('onDeviceReady'); + +// Event to indicate a resume lifecycle event +channel.create('onResume'); + +// Event to indicate a pause lifecycle event +channel.create('onPause'); + +// Channels that must fire before "deviceready" is fired. +channel.waitForInitialization('onCordovaReady'); +channel.waitForInitialization('onDOMContentLoaded'); + +module.exports = channel; + +}); + +// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/exec.js +define("cordova/exec", function(require, exports, module) { + +/** + * Execute a cordova command. It is up to the native side whether this action + * is synchronous or asynchronous. The native side can return: + * Synchronous: PluginResult object as a JSON string + * Asynchronous: Empty string "" + * If async, the native side will cordova.callbackSuccess or cordova.callbackError, + * depending upon the result of the action. + * + * @param {Function} success The success callback + * @param {Function} fail The fail callback + * @param {String} service The name of the service to use + * @param {String} action Action to be run in cordova + * @param {String[]} [args] Zero or more arguments to pass to the method + */ +var cordova = require('cordova'), + nativeApiProvider = require('cordova/android/nativeapiprovider'), + utils = require('cordova/utils'), + base64 = require('cordova/base64'), + channel = require('cordova/channel'), + jsToNativeModes = { + PROMPT: 0, + JS_OBJECT: 1 + }, + nativeToJsModes = { + // Polls for messages using the JS->Native bridge. + POLLING: 0, + // For LOAD_URL to be viable, it would need to have a work-around for + // the bug where the soft-keyboard gets dismissed when a message is sent. + LOAD_URL: 1, + // For the ONLINE_EVENT to be viable, it would need to intercept all event + // listeners (both through addEventListener and window.ononline) as well + // as set the navigator property itself. + ONLINE_EVENT: 2, + EVAL_BRIDGE: 3 + }, + jsToNativeBridgeMode, // Set lazily. + nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE, + pollEnabled = false, + bridgeSecret = -1; + +var messagesFromNative = []; +var isProcessing = false; +var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve(); +var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); }; + +function androidExec(success, fail, service, action, args) { + if (bridgeSecret < 0) { + // If we ever catch this firing, we'll need to queue up exec()s + // and fire them once we get a secret. For now, I don't think + // it's possible for exec() to be called since plugins are parsed but + // not run until until after onNativeReady. + throw new Error('exec() called without bridgeSecret'); + } + // Set default bridge modes if they have not already been set. + // By default, we use the failsafe, since addJavascriptInterface breaks too often + if (jsToNativeBridgeMode === undefined) { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); + } + + // If args is not provided, default to an empty array + args = args || []; + + // Process any ArrayBuffers in the args into a string. + for (var i = 0; i < args.length; i++) { + if (utils.typeName(args[i]) == 'ArrayBuffer') { + args[i] = base64.fromArrayBuffer(args[i]); + } + } + + var callbackId = service + cordova.callbackId++, + argsJson = JSON.stringify(args); + if (success || fail) { + cordova.callbacks[callbackId] = {success:success, fail:fail}; + } + + var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson); + // If argsJson was received by Java as null, try again with the PROMPT bridge mode. + // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666. + if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); + androidExec(success, fail, service, action, args); + androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); + } else if (msgs) { + messagesFromNative.push(msgs); + // Always process async to avoid exceptions messing up stack. + nextTick(processMessages); + } +} + +androidExec.init = function() { + //CB-11828 + //This failsafe checks the version of Android and if it's Jellybean, it switches it to + //using the Online Event bridge for communicating from Native to JS + // + //It's ugly, but it's necessary. + var check = navigator.userAgent.toLowerCase().match(/android\s[0-9].[0-9]/); + var version_code = check && check[0].match(/4.[0-3].*/); + if (version_code != null && nativeToJsBridgeMode == nativeToJsModes.EVAL_BRIDGE) { + nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT; + } + + bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode); + channel.onNativeReady.fire(); +}; + +function pollOnceFromOnlineEvent() { + pollOnce(true); +} + +function pollOnce(opt_fromOnlineEvent) { + if (bridgeSecret < 0) { + // This can happen when the NativeToJsMessageQueue resets the online state on page transitions. + // We know there's nothing to retrieve, so no need to poll. + return; + } + var msgs = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent); + if (msgs) { + messagesFromNative.push(msgs); + // Process sync since we know we're already top-of-stack. + processMessages(); + } +} + +function pollingTimerFunc() { + if (pollEnabled) { + pollOnce(); + setTimeout(pollingTimerFunc, 50); + } +} + +function hookOnlineApis() { + function proxyEvent(e) { + cordova.fireWindowEvent(e.type); + } + // The network module takes care of firing online and offline events. + // It currently fires them only on document though, so we bridge them + // to window here (while first listening for exec()-releated online/offline + // events). + window.addEventListener('online', pollOnceFromOnlineEvent, false); + window.addEventListener('offline', pollOnceFromOnlineEvent, false); + cordova.addWindowEventHandler('online'); + cordova.addWindowEventHandler('offline'); + document.addEventListener('online', proxyEvent, false); + document.addEventListener('offline', proxyEvent, false); +} + +hookOnlineApis(); + +androidExec.jsToNativeModes = jsToNativeModes; +androidExec.nativeToJsModes = nativeToJsModes; + +androidExec.setJsToNativeBridgeMode = function(mode) { + if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) { + mode = jsToNativeModes.PROMPT; + } + nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT); + jsToNativeBridgeMode = mode; +}; + +androidExec.setNativeToJsBridgeMode = function(mode) { + if (mode == nativeToJsBridgeMode) { + return; + } + if (nativeToJsBridgeMode == nativeToJsModes.POLLING) { + pollEnabled = false; + } + + nativeToJsBridgeMode = mode; + // Tell the native side to switch modes. + // Otherwise, it will be set by androidExec.init() + if (bridgeSecret >= 0) { + nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode); + } + + if (mode == nativeToJsModes.POLLING) { + pollEnabled = true; + setTimeout(pollingTimerFunc, 1); + } +}; + +function buildPayload(payload, message) { + var payloadKind = message.charAt(0); + if (payloadKind == 's') { + payload.push(message.slice(1)); + } else if (payloadKind == 't') { + payload.push(true); + } else if (payloadKind == 'f') { + payload.push(false); + } else if (payloadKind == 'N') { + payload.push(null); + } else if (payloadKind == 'n') { + payload.push(+message.slice(1)); + } else if (payloadKind == 'A') { + var data = message.slice(1); + payload.push(base64.toArrayBuffer(data)); + } else if (payloadKind == 'S') { + payload.push(window.atob(message.slice(1))); + } else if (payloadKind == 'M') { + var multipartMessages = message.slice(1); + while (multipartMessages !== "") { + var spaceIdx = multipartMessages.indexOf(' '); + var msgLen = +multipartMessages.slice(0, spaceIdx); + var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen); + multipartMessages = multipartMessages.slice(spaceIdx + msgLen + 1); + buildPayload(payload, multipartMessage); + } + } else { + payload.push(JSON.parse(message)); + } +} + +// Processes a single message, as encoded by NativeToJsMessageQueue.java. +function processMessage(message) { + var firstChar = message.charAt(0); + if (firstChar == 'J') { + // This is deprecated on the .java side. It doesn't work with CSP enabled. + eval(message.slice(1)); + } else if (firstChar == 'S' || firstChar == 'F') { + var success = firstChar == 'S'; + var keepCallback = message.charAt(1) == '1'; + var spaceIdx = message.indexOf(' ', 2); + var status = +message.slice(2, spaceIdx); + var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1); + var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx); + var payloadMessage = message.slice(nextSpaceIdx + 1); + var payload = []; + buildPayload(payload, payloadMessage); + cordova.callbackFromNative(callbackId, success, status, payload, keepCallback); + } else { + console.log("processMessage failed: invalid message: " + JSON.stringify(message)); + } +} + +function processMessages() { + // Check for the reentrant case. + if (isProcessing) { + return; + } + if (messagesFromNative.length === 0) { + return; + } + isProcessing = true; + try { + var msg = popMessageFromQueue(); + // The Java side can send a * message to indicate that it + // still has messages waiting to be retrieved. + if (msg == '*' && messagesFromNative.length === 0) { + nextTick(pollOnce); + return; + } + processMessage(msg); + } finally { + isProcessing = false; + if (messagesFromNative.length > 0) { + nextTick(processMessages); + } + } +} + +function popMessageFromQueue() { + var messageBatch = messagesFromNative.shift(); + if (messageBatch == '*') { + return '*'; + } + + var spaceIdx = messageBatch.indexOf(' '); + var msgLen = +messageBatch.slice(0, spaceIdx); + var message = messageBatch.substr(spaceIdx + 1, msgLen); + messageBatch = messageBatch.slice(spaceIdx + msgLen + 1); + if (messageBatch) { + messagesFromNative.unshift(messageBatch); + } + return message; +} + +module.exports = androidExec; + +}); + +// file: src/common/exec/proxy.js +define("cordova/exec/proxy", function(require, exports, module) { + +// internal map of proxy function +var CommandProxyMap = {}; + +module.exports = { + + // example: cordova.commandProxy.add("Accelerometer",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...); + add: function (id, proxyObj) { + console.log('adding proxy for ' + id); + CommandProxyMap[id] = proxyObj; + return proxyObj; + }, + + // cordova.commandProxy.remove("Accelerometer"); + remove: function (id) { + var proxy = CommandProxyMap[id]; + delete CommandProxyMap[id]; + CommandProxyMap[id] = null; + return proxy; + }, + + get: function (service, action) { + return (CommandProxyMap[service] ? CommandProxyMap[service][action] : null); + } +}; + +}); + +// file: src/common/init.js +define("cordova/init", function(require, exports, module) { + +var channel = require('cordova/channel'); +var cordova = require('cordova'); +var modulemapper = require('cordova/modulemapper'); +var platform = require('cordova/platform'); +var pluginloader = require('cordova/pluginloader'); +var utils = require('cordova/utils'); + +var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady]; + +function logUnfiredChannels (arr) { + for (var i = 0; i < arr.length; ++i) { + if (arr[i].state !== 2) { + console.log('Channel not fired: ' + arr[i].type); + } + } +} + +window.setTimeout(function () { + if (channel.onDeviceReady.state !== 2) { + console.log('deviceready has not fired after 5 seconds.'); + logUnfiredChannels(platformInitChannelsArray); + logUnfiredChannels(channel.deviceReadyChannelsArray); + } +}, 5000); + +// Replace navigator before any modules are required(), to ensure it happens as soon as possible. +// We replace it so that properties that can't be clobbered can instead be overridden. +function replaceNavigator (origNavigator) { + var CordovaNavigator = function () {}; + CordovaNavigator.prototype = origNavigator; + var newNavigator = new CordovaNavigator(); + // This work-around really only applies to new APIs that are newer than Function.bind. + // Without it, APIs such as getGamepads() break. + if (CordovaNavigator.bind) { + for (var key in origNavigator) { + if (typeof origNavigator[key] === 'function') { + newNavigator[key] = origNavigator[key].bind(origNavigator); + } else { + (function (k) { + utils.defineGetterSetter(newNavigator, key, function () { + return origNavigator[k]; + }); + })(key); + } + } + } + return newNavigator; +} + +if (window.navigator) { + window.navigator = replaceNavigator(window.navigator); +} + +if (!window.console) { + window.console = { + log: function () {} + }; +} +if (!window.console.warn) { + window.console.warn = function (msg) { + this.log('warn: ' + msg); + }; +} + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onActivated = cordova.addDocumentEventHandler('activated'); +channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready'); + +// Listen for DOMContentLoaded and notify our channel subscribers. +if (document.readyState === 'complete' || document.readyState === 'interactive') { + channel.onDOMContentLoaded.fire(); +} else { + document.addEventListener('DOMContentLoaded', function () { + channel.onDOMContentLoaded.fire(); + }, false); +} + +// _nativeReady is global variable that the native side can set +// to signify that the native code is ready. It is a global since +// it may be called before any cordova JS is ready. +if (window._nativeReady) { + channel.onNativeReady.fire(); +} + +modulemapper.clobbers('cordova', 'cordova'); +modulemapper.clobbers('cordova/exec', 'cordova.exec'); +modulemapper.clobbers('cordova/exec', 'Cordova.exec'); + +// Call the platform-specific initialization. +platform.bootstrap && platform.bootstrap(); + +// Wrap in a setTimeout to support the use-case of having plugin JS appended to cordova.js. +// The delay allows the attached modules to be defined before the plugin loader looks for them. +setTimeout(function () { + pluginloader.load(function () { + channel.onPluginsReady.fire(); + }); +}, 0); + +/** + * Create all cordova objects once native side is ready. + */ +channel.join(function () { + modulemapper.mapModules(window); + + platform.initialize && platform.initialize(); + + // Fire event to notify that all objects are created + channel.onCordovaReady.fire(); + + // Fire onDeviceReady event once page has fully loaded, all + // constructors have run and cordova info has been received from native + // side. + channel.join(function () { + require('cordova').fireDocumentEvent('deviceready'); + }, channel.deviceReadyChannelsArray); + +}, platformInitChannelsArray); + +}); + +// file: src/common/init_b.js +define("cordova/init_b", function(require, exports, module) { + +var channel = require('cordova/channel'); +var cordova = require('cordova'); +var modulemapper = require('cordova/modulemapper'); +var platform = require('cordova/platform'); +var pluginloader = require('cordova/pluginloader'); +var utils = require('cordova/utils'); + +var platformInitChannelsArray = [channel.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady]; + +// setting exec +cordova.exec = require('cordova/exec'); + +function logUnfiredChannels (arr) { + for (var i = 0; i < arr.length; ++i) { + if (arr[i].state !== 2) { + console.log('Channel not fired: ' + arr[i].type); + } + } +} + +window.setTimeout(function () { + if (channel.onDeviceReady.state !== 2) { + console.log('deviceready has not fired after 5 seconds.'); + logUnfiredChannels(platformInitChannelsArray); + logUnfiredChannels(channel.deviceReadyChannelsArray); + } +}, 5000); + +// Replace navigator before any modules are required(), to ensure it happens as soon as possible. +// We replace it so that properties that can't be clobbered can instead be overridden. +function replaceNavigator (origNavigator) { + var CordovaNavigator = function () {}; + CordovaNavigator.prototype = origNavigator; + var newNavigator = new CordovaNavigator(); + // This work-around really only applies to new APIs that are newer than Function.bind. + // Without it, APIs such as getGamepads() break. + if (CordovaNavigator.bind) { + for (var key in origNavigator) { + if (typeof origNavigator[key] === 'function') { + newNavigator[key] = origNavigator[key].bind(origNavigator); + } else { + (function (k) { + utils.defineGetterSetter(newNavigator, key, function () { + return origNavigator[k]; + }); + })(key); + } + } + } + return newNavigator; +} +if (window.navigator) { + window.navigator = replaceNavigator(window.navigator); +} + +if (!window.console) { + window.console = { + log: function () {} + }; +} +if (!window.console.warn) { + window.console.warn = function (msg) { + this.log('warn: ' + msg); + }; +} + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onActivated = cordova.addDocumentEventHandler('activated'); +channel.onDeviceReady = cordova.addStickyDocumentEventHandler('deviceready'); + +// Listen for DOMContentLoaded and notify our channel subscribers. +if (document.readyState === 'complete' || document.readyState === 'interactive') { + channel.onDOMContentLoaded.fire(); +} else { + document.addEventListener('DOMContentLoaded', function () { + channel.onDOMContentLoaded.fire(); + }, false); +} + +// _nativeReady is global variable that the native side can set +// to signify that the native code is ready. It is a global since +// it may be called before any cordova JS is ready. +if (window._nativeReady) { + channel.onNativeReady.fire(); +} + +// Call the platform-specific initialization. +platform.bootstrap && platform.bootstrap(); + +// Wrap in a setTimeout to support the use-case of having plugin JS appended to cordova.js. +// The delay allows the attached modules to be defined before the plugin loader looks for them. +setTimeout(function () { + pluginloader.load(function () { + channel.onPluginsReady.fire(); + }); +}, 0); + +/** + * Create all cordova objects once native side is ready. + */ +channel.join(function () { + modulemapper.mapModules(window); + + platform.initialize && platform.initialize(); + + // Fire event to notify that all objects are created + channel.onCordovaReady.fire(); + + // Fire onDeviceReady event once page has fully loaded, all + // constructors have run and cordova info has been received from native + // side. + channel.join(function () { + require('cordova').fireDocumentEvent('deviceready'); + }, channel.deviceReadyChannelsArray); + +}, platformInitChannelsArray); + +}); + +// file: src/common/modulemapper.js +define("cordova/modulemapper", function(require, exports, module) { + +var builder = require('cordova/builder'); +var moduleMap = define.moduleMap; // eslint-disable-line no-undef +var symbolList; +var deprecationMap; + +exports.reset = function () { + symbolList = []; + deprecationMap = {}; +}; + +function addEntry (strategy, moduleName, symbolPath, opt_deprecationMessage) { + if (!(moduleName in moduleMap)) { + throw new Error('Module ' + moduleName + ' does not exist.'); + } + symbolList.push(strategy, moduleName, symbolPath); + if (opt_deprecationMessage) { + deprecationMap[symbolPath] = opt_deprecationMessage; + } +} + +// Note: Android 2.3 does have Function.bind(). +exports.clobbers = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('c', moduleName, symbolPath, opt_deprecationMessage); +}; + +exports.merges = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('m', moduleName, symbolPath, opt_deprecationMessage); +}; + +exports.defaults = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('d', moduleName, symbolPath, opt_deprecationMessage); +}; + +exports.runs = function (moduleName) { + addEntry('r', moduleName, null); +}; + +function prepareNamespace (symbolPath, context) { + if (!symbolPath) { + return context; + } + var parts = symbolPath.split('.'); + var cur = context; + for (var i = 0, part; part = parts[i]; ++i) { // eslint-disable-line no-cond-assign + cur = cur[part] = cur[part] || {}; + } + return cur; +} + +exports.mapModules = function (context) { + var origSymbols = {}; + context.CDV_origSymbols = origSymbols; + for (var i = 0, len = symbolList.length; i < len; i += 3) { + var strategy = symbolList[i]; + var moduleName = symbolList[i + 1]; + var module = require(moduleName); + // + if (strategy === 'r') { + continue; + } + var symbolPath = symbolList[i + 2]; + var lastDot = symbolPath.lastIndexOf('.'); + var namespace = symbolPath.substr(0, lastDot); + var lastName = symbolPath.substr(lastDot + 1); + + var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null; + var parentObj = prepareNamespace(namespace, context); + var target = parentObj[lastName]; + + if (strategy === 'm' && target) { + builder.recursiveMerge(target, module); + } else if ((strategy === 'd' && !target) || (strategy !== 'd')) { + if (!(symbolPath in origSymbols)) { + origSymbols[symbolPath] = target; + } + builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg); + } + } +}; + +exports.getOriginalSymbol = function (context, symbolPath) { + var origSymbols = context.CDV_origSymbols; + if (origSymbols && (symbolPath in origSymbols)) { + return origSymbols[symbolPath]; + } + var parts = symbolPath.split('.'); + var obj = context; + for (var i = 0; i < parts.length; ++i) { + obj = obj && obj[parts[i]]; + } + return obj; +}; + +exports.reset(); + +}); + +// file: src/common/modulemapper_b.js +define("cordova/modulemapper_b", function(require, exports, module) { + +var builder = require('cordova/builder'); +var symbolList = []; +var deprecationMap; + +exports.reset = function () { + symbolList = []; + deprecationMap = {}; +}; + +function addEntry (strategy, moduleName, symbolPath, opt_deprecationMessage) { + symbolList.push(strategy, moduleName, symbolPath); + if (opt_deprecationMessage) { + deprecationMap[symbolPath] = opt_deprecationMessage; + } +} + +// Note: Android 2.3 does have Function.bind(). +exports.clobbers = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('c', moduleName, symbolPath, opt_deprecationMessage); +}; + +exports.merges = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('m', moduleName, symbolPath, opt_deprecationMessage); +}; + +exports.defaults = function (moduleName, symbolPath, opt_deprecationMessage) { + addEntry('d', moduleName, symbolPath, opt_deprecationMessage); +}; + +exports.runs = function (moduleName) { + addEntry('r', moduleName, null); +}; + +function prepareNamespace (symbolPath, context) { + if (!symbolPath) { + return context; + } + var parts = symbolPath.split('.'); + var cur = context; + for (var i = 0, part; part = parts[i]; ++i) { // eslint-disable-line no-cond-assign + cur = cur[part] = cur[part] || {}; + } + return cur; +} + +exports.mapModules = function (context) { + var origSymbols = {}; + context.CDV_origSymbols = origSymbols; + for (var i = 0, len = symbolList.length; i < len; i += 3) { + var strategy = symbolList[i]; + var moduleName = symbolList[i + 1]; + var module = require(moduleName); + // + if (strategy === 'r') { + continue; + } + var symbolPath = symbolList[i + 2]; + var lastDot = symbolPath.lastIndexOf('.'); + var namespace = symbolPath.substr(0, lastDot); + var lastName = symbolPath.substr(lastDot + 1); + + var deprecationMsg = symbolPath in deprecationMap ? 'Access made to deprecated symbol: ' + symbolPath + '. ' + deprecationMsg : null; + var parentObj = prepareNamespace(namespace, context); + var target = parentObj[lastName]; + + if (strategy === 'm' && target) { + builder.recursiveMerge(target, module); + } else if ((strategy === 'd' && !target) || (strategy !== 'd')) { + if (!(symbolPath in origSymbols)) { + origSymbols[symbolPath] = target; + } + builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg); + } + } +}; + +exports.getOriginalSymbol = function (context, symbolPath) { + var origSymbols = context.CDV_origSymbols; + if (origSymbols && (symbolPath in origSymbols)) { + return origSymbols[symbolPath]; + } + var parts = symbolPath.split('.'); + var obj = context; + for (var i = 0; i < parts.length; ++i) { + obj = obj && obj[parts[i]]; + } + return obj; +}; + +exports.reset(); + +}); + +// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/platform.js +define("cordova/platform", function(require, exports, module) { + +// The last resume event that was received that had the result of a plugin call. +var lastResumeEvent = null; + +module.exports = { + id: 'android', + bootstrap: function() { + var channel = require('cordova/channel'), + cordova = require('cordova'), + exec = require('cordova/exec'), + modulemapper = require('cordova/modulemapper'); + + // Get the shared secret needed to use the bridge. + exec.init(); + + // TODO: Extract this as a proper plugin. + modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app'); + + var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App'; + + // Inject a listener for the backbutton on the document. + var backButtonChannel = cordova.addDocumentEventHandler('backbutton'); + backButtonChannel.onHasSubscribersChange = function() { + // If we just attached the first handler or detached the last handler, + // let native know we need to override the back button. + exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]); + }; + + // Add hardware MENU and SEARCH button handlers + cordova.addDocumentEventHandler('menubutton'); + cordova.addDocumentEventHandler('searchbutton'); + + function bindButtonChannel(buttonName) { + // generic button bind used for volumeup/volumedown buttons + var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button'); + volumeButtonChannel.onHasSubscribersChange = function() { + exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]); + }; + } + // Inject a listener for the volume buttons on the document. + bindButtonChannel('volumeup'); + bindButtonChannel('volumedown'); + + // The resume event is not "sticky", but it is possible that the event + // will contain the result of a plugin call. We need to ensure that the + // plugin result is delivered even after the event is fired (CB-10498) + var cordovaAddEventListener = document.addEventListener; + + document.addEventListener = function(evt, handler, capture) { + cordovaAddEventListener(evt, handler, capture); + + if (evt === 'resume' && lastResumeEvent) { + handler(lastResumeEvent); + } + }; + + // Let native code know we are all done on the JS side. + // Native code will then un-hide the WebView. + channel.onCordovaReady.subscribe(function() { + exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []); + exec(null, null, APP_PLUGIN_NAME, "show", []); + }); + } +}; + +function onMessageFromNative(msg) { + var cordova = require('cordova'); + var action = msg.action; + + switch (action) + { + // Button events + case 'backbutton': + case 'menubutton': + case 'searchbutton': + // App life cycle events + case 'pause': + // Volume events + case 'volumedownbutton': + case 'volumeupbutton': + cordova.fireDocumentEvent(action); + break; + case 'resume': + if(arguments.length > 1 && msg.pendingResult) { + if(arguments.length === 2) { + msg.pendingResult.result = arguments[1]; + } else { + // The plugin returned a multipart message + var res = []; + for(var i = 1; i < arguments.length; i++) { + res.push(arguments[i]); + } + msg.pendingResult.result = res; + } + + // Save the plugin result so that it can be delivered to the js + // even if they miss the initial firing of the event + lastResumeEvent = msg; + } + cordova.fireDocumentEvent(action, msg); + break; + default: + throw new Error('Unknown event action ' + action); + } +} + +}); + +// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/plugin/android/app.js +define("cordova/plugin/android/app", function(require, exports, module) { + +var exec = require('cordova/exec'); +var APP_PLUGIN_NAME = Number(require('cordova').platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App'; + +module.exports = { + /** + * Clear the resource cache. + */ + clearCache:function() { + exec(null, null, APP_PLUGIN_NAME, "clearCache", []); + }, + + /** + * Load the url into the webview or into new browser instance. + * + * @param url The URL to load + * @param props Properties that can be passed in to the activity: + * wait: int => wait msec before loading URL + * loadingDialog: "Title,Message" => display a native loading dialog + * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error + * clearHistory: boolean => clear webview history (default=false) + * openExternal: boolean => open in a new browser (default=false) + * + * Example: + * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); + */ + loadUrl:function(url, props) { + exec(null, null, APP_PLUGIN_NAME, "loadUrl", [url, props]); + }, + + /** + * Cancel loadUrl that is waiting to be loaded. + */ + cancelLoadUrl:function() { + exec(null, null, APP_PLUGIN_NAME, "cancelLoadUrl", []); + }, + + /** + * Clear web history in this web view. + * Instead of BACK button loading the previous web page, it will exit the app. + */ + clearHistory:function() { + exec(null, null, APP_PLUGIN_NAME, "clearHistory", []); + }, + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + backHistory:function() { + exec(null, null, APP_PLUGIN_NAME, "backHistory", []); + }, + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ + overrideBackbutton:function(override) { + exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [override]); + }, + + /** + * Override the default behavior of the Android volume button. + * If overridden, when the volume button is pressed, the "volume[up|down]button" + * JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "volume[up|down]button" event, this is automatically done. + * + * @param button volumeup, volumedown + * @param override T=override, F=cancel override + */ + overrideButton:function(button, override) { + exec(null, null, APP_PLUGIN_NAME, "overrideButton", [button, override]); + }, + + /** + * Exit and terminate the application. + */ + exitApp:function() { + return exec(null, null, APP_PLUGIN_NAME, "exitApp", []); + } +}; + +}); + +// file: src/common/pluginloader.js +define("cordova/pluginloader", function(require, exports, module) { + +var modulemapper = require('cordova/modulemapper'); + +// Helper function to inject a + diff --git a/plugins/cordova-plugin-inappbrowser/tests/resources/inject.js b/plugins/cordova-plugin-inappbrowser/tests/resources/inject.js new file mode 100644 index 0000000..d704ab3 --- /dev/null +++ b/plugins/cordova-plugin-inappbrowser/tests/resources/inject.js @@ -0,0 +1,20 @@ +/* + * 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. +*/ +var d = document.getElementById('header'); +d.innerHTML = 'Script file successfully injected'; diff --git a/plugins/cordova-plugin-inappbrowser/tests/resources/local.html b/plugins/cordova-plugin-inappbrowser/tests/resources/local.html new file mode 100644 index 0000000..d23a714 --- /dev/null +++ b/plugins/cordova-plugin-inappbrowser/tests/resources/local.html @@ -0,0 +1,67 @@ + + + + + + + + + IAB test page + + + + + +

Local URL

+
+ You have successfully loaded a local URL: + +
+
+
User-Agent =
+
+
Likely running inAppBrowser: Device version from Cordova=not found, Back link should not work, toolbar may be present, logcat should show failed 'gap:' calls.
+
+
Visit Google (whitelisted)
+
Visit Yahoo (not whitelisted)
+ + +

Back +

+ +

tall div with border
+ + + diff --git a/plugins/cordova-plugin-inappbrowser/tests/resources/local.pdf b/plugins/cordova-plugin-inappbrowser/tests/resources/local.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b54f1b759a9ab7c60b05b209a84e1eb2cabe455a GIT binary patch literal 8568 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 + + + + + + + + Cordova Mobile Spec + + + + +
+ + +
+ + + diff --git a/plugins/cordova-plugin-inappbrowser/tests/tests.js b/plugins/cordova-plugin-inappbrowser/tests/tests.js new file mode 100644 index 0000000..abb2a6d --- /dev/null +++ b/plugins/cordova-plugin-inappbrowser/tests/tests.js @@ -0,0 +1,696 @@ +/* + * + * 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. + * +*/ + +/* global MSApp */ + +var cordova = require('cordova'); +var isWindows = cordova.platformId === 'windows'; +var isBrowser = cordova.platformId === 'browser'; + +window.alert = window.alert || navigator.notification.alert; +if (isWindows && navigator && navigator.notification && navigator.notification.alert) { + // window.alert is defined but not functional on UWP + window.alert = navigator.notification.alert; +} + +exports.defineAutoTests = function () { + + describe('cordova.InAppBrowser', function () { + + it('inappbrowser.spec.1 should exist', function () { + expect(cordova.InAppBrowser).toBeDefined(); + }); + + it('inappbrowser.spec.2 should contain open function', function () { + expect(cordova.InAppBrowser.open).toBeDefined(); + expect(cordova.InAppBrowser.open).toEqual(jasmine.any(Function)); + }); + }); + + describe('open method', function () { + + if (cordova.platformId === 'osx') { + pending('Open method not fully supported on OSX.'); + return; + } + + var iabInstance; + var originalTimeout; + var url = 'https://dist.apache.org/repos/dist/dev/cordova/'; + var badUrl = 'http://bad-uri/'; + + beforeEach(function () { + // increase timeout to ensure test url could be loaded within test time + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; + + iabInstance = null; + }); + + afterEach(function (done) { + // restore original timeout + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + + if (iabInstance !== null && iabInstance.close) { + iabInstance.close(); + } + iabInstance = null; + // add some extra time so that iab dialog is closed + setTimeout(done, 2000); + }); + + function verifyEvent (evt, type) { + expect(evt).toBeDefined(); + expect(evt.type).toEqual(type); + // `exit` event does not have url field, browser returns null url for CORS requests + if (type !== 'exit' && !isBrowser) { + expect(evt.url).toEqual(url); + } + } + + function verifyLoadErrorEvent (evt) { + expect(evt).toBeDefined(); + expect(evt.type).toEqual('loaderror'); + expect(evt.url).toEqual(badUrl); + expect(evt.code).toEqual(jasmine.any(Number)); + expect(evt.message).toEqual(jasmine.any(String)); + } + + it('inappbrowser.spec.3 should return InAppBrowser instance with required methods', function () { + iabInstance = cordova.InAppBrowser.open(url, '_blank'); + + expect(iabInstance).toBeDefined(); + + expect(iabInstance.addEventListener).toEqual(jasmine.any(Function)); + expect(iabInstance.removeEventListener).toEqual(jasmine.any(Function)); + expect(iabInstance.close).toEqual(jasmine.any(Function)); + expect(iabInstance.show).toEqual(jasmine.any(Function)); + expect(iabInstance.hide).toEqual(jasmine.any(Function)); + expect(iabInstance.executeScript).toEqual(jasmine.any(Function)); + expect(iabInstance.insertCSS).toEqual(jasmine.any(Function)); + }); + + it('inappbrowser.spec.4 should support loadstart and loadstop events', function (done) { + var onLoadStart = jasmine.createSpy('loadstart event callback').and.callFake(function (evt) { + verifyEvent(evt, 'loadstart'); + }); + + iabInstance = cordova.InAppBrowser.open(url, '_blank'); + iabInstance.addEventListener('loadstart', onLoadStart); + iabInstance.addEventListener('loadstop', function (evt) { + verifyEvent(evt, 'loadstop'); + if (!isBrowser) { + // according to documentation, "loadstart" event is not supported on browser + // https://github.com/apache/cordova-plugin-inappbrowser#browser-quirks-1 + expect(onLoadStart).toHaveBeenCalled(); + } + done(); + }); + }); + + it('inappbrowser.spec.5 should support exit event', function (done) { + iabInstance = cordova.InAppBrowser.open(url, '_blank'); + iabInstance.addEventListener('exit', function (evt) { + verifyEvent(evt, 'exit'); + done(); + }); + iabInstance.close(); + iabInstance = null; + }); + + it('inappbrowser.spec.6 should support loaderror event', function (done) { + if (isBrowser) { + // according to documentation, "loaderror" event is not supported on browser + // https://github.com/apache/cordova-plugin-inappbrowser#browser-quirks-1 + pending('Browser platform doesn\'t support loaderror event'); + } + iabInstance = cordova.InAppBrowser.open(badUrl, '_blank'); + iabInstance.addEventListener('loaderror', function (evt) { + verifyLoadErrorEvent(evt); + done(); + }); + }); + }); +}; + +exports.defineManualTests = function (contentEl, createActionButton) { + + function doOpen (url, target, params, numExpectedRedirects, useWindowOpen) { + numExpectedRedirects = numExpectedRedirects || 0; + useWindowOpen = useWindowOpen || false; + console.log('Opening ' + url); + + var counts; + var lastLoadStartURL; + var wasReset = false; + function reset () { + counts = { + 'loaderror': 0, + 'loadstart': 0, + 'loadstop': 0, + 'exit': 0 + }; + lastLoadStartURL = ''; + } + reset(); + + var iab; + var callbacks = { + loaderror: logEvent, + loadstart: logEvent, + loadstop: logEvent, + exit: logEvent + }; + if (useWindowOpen) { + console.log('Use window.open() for url'); + iab = window.open(url, target, params, callbacks); + } else { + iab = cordova.InAppBrowser.open(url, target, params, callbacks); + } + if (!iab) { + alert('open returned ' + iab); // eslint-disable-line no-undef + return; + } + + function logEvent (e) { + console.log('IAB event=' + JSON.stringify(e)); + counts[e.type]++; + // Verify that event.url gets updated on redirects. + if (e.type === 'loadstart') { + if (e.url === lastLoadStartURL) { + alert('Unexpected: loadstart fired multiple times for the same URL.'); // eslint-disable-line no-undef + } + lastLoadStartURL = e.url; + } + // Verify the right number of loadstart events were fired. + if (e.type === 'loadstop' || e.type === 'loaderror') { + if (e.url !== lastLoadStartURL) { + alert('Unexpected: ' + e.type + ' event.url != loadstart\'s event.url'); // eslint-disable-line no-undef + } + if (numExpectedRedirects === 0 && counts.loadstart !== 1) { + // Do allow a loaderror without a loadstart (e.g. in the case of an invalid URL). + if (!(e.type === 'loaderror' && counts.loadstart === 0)) { + alert('Unexpected: got multiple loadstart events. (' + counts.loadstart + ')'); // eslint-disable-line no-undef + } + } else if (numExpectedRedirects > 0 && counts.loadstart < (numExpectedRedirects + 1)) { + alert('Unexpected: should have got at least ' + (numExpectedRedirects + 1) + ' loadstart events, but got ' + counts.loadstart); // eslint-disable-line no-undef + } + wasReset = true; + numExpectedRedirects = 0; + reset(); + } + // Verify that loadend / loaderror was called. + if (e.type === 'exit') { + var numStopEvents = counts.loadstop + counts.loaderror; + if (numStopEvents === 0 && !wasReset) { + alert('Unexpected: browser closed without a loadstop or loaderror.'); // eslint-disable-line no-undef + } else if (numStopEvents > 1) { + alert('Unexpected: got multiple loadstop/loaderror events.'); // eslint-disable-line no-undef + } + } + } + + return iab; + } + + function doHookOpen (url, target, params, numExpectedRedirects) { + var originalFunc = window.open; + var wasClobbered = window.hasOwnProperty('open'); + window.open = cordova.InAppBrowser.open; + + try { + doOpen(url, target, params, numExpectedRedirects, true); + } finally { + if (wasClobbered) { + window.open = originalFunc; + } else { + console.log('just delete, to restore open from prototype'); + delete window.open; + } + } + } + + function openWithStyle (url, cssUrl, useCallback) { + var iab = doOpen(url, '_blank', 'location=yes'); + var callback = function (results) { + if (results && results.length === 0) { + alert('Results verified'); // eslint-disable-line no-undef + } else { + console.log(results); + alert('Got: ' + typeof (results) + '\n' + JSON.stringify(results)); // eslint-disable-line no-undef + } + }; + if (cssUrl) { + iab.addEventListener('loadstop', function (event) { + iab.insertCSS({ file: cssUrl }, useCallback && callback); + }); + } else { + iab.addEventListener('loadstop', function (event) { + iab.insertCSS({ code: '#style-update-literal { \ndisplay: block !important; \n}' }, + useCallback && callback); + }); + } + } + + function openWithScript (url, jsUrl, useCallback) { + var iab = doOpen(url, '_blank', 'location=yes'); + if (jsUrl) { + iab.addEventListener('loadstop', function (event) { + iab.executeScript({ file: jsUrl }, useCallback && function (results) { + if (results && results.length === 0) { + alert('Results verified'); // eslint-disable-line no-undef + } else { + console.log(results); + alert('Got: ' + typeof (results) + '\n' + JSON.stringify(results)); // eslint-disable-line no-undef + } + }); + }); + } else { + iab.addEventListener('loadstop', function (event) { + var code = '(function(){\n' + + ' var header = document.getElementById("header");\n' + + ' header.innerHTML = "Script literal successfully injected";\n' + + ' return "abc";\n' + + '})()'; + iab.executeScript({ code: code }, useCallback && function (results) { + if (results && results.length === 1 && results[0] === 'abc') { + alert('Results verified'); // eslint-disable-line no-undef + } else { + console.log(results); + alert('Got: ' + typeof (results) + '\n' + JSON.stringify(results)); // eslint-disable-line no-undef + } + }); + }); + } + } + var hiddenwnd = null; + var loadlistener = function (event) { alert('background window loaded '); }; // eslint-disable-line no-undef + function openHidden (url, startHidden) { + var shopt = (startHidden) ? 'hidden=yes' : ''; + hiddenwnd = cordova.InAppBrowser.open(url, 'random_string', shopt); + if (!hiddenwnd) { + alert('cordova.InAppBrowser.open returned ' + hiddenwnd); // eslint-disable-line no-undef + return; + } + if (startHidden) hiddenwnd.addEventListener('loadstop', loadlistener); + } + function showHidden () { + if (hiddenwnd) { + hiddenwnd.show(); + } + } + function closeHidden () { + if (hiddenwnd) { + hiddenwnd.removeEventListener('loadstop', loadlistener); + hiddenwnd.close(); + hiddenwnd = null; + } + } + + var info_div = '

InAppBrowser

' + + '
' + + 'Make sure http://cordova.apache.org and http://google.co.uk and https://www.google.co.uk are white listed.
' + + 'Make sure http://www.apple.com is not in the white list.
' + + 'In iOS, starred * tests will put the app in a state with no way to return.
' + + '

User-Agent: ' + + '

'; + + var local_tests = '

Local URL

' + + '
' + + 'Expected result: opens successfully in CordovaWebView.' + + '

' + + 'Expected result: opens successfully in CordovaWebView (using hook of window.open()).' + + '

' + + 'Expected result: opens successfully in CordovaWebView.' + + '

' + + 'Expected result: fails to open' + + '

' + + 'Expected result: opens successfully in InAppBrowser with locationBar at top.' + + '

' + + 'Expected result: opens successfully in InAppBrowser without locationBar.' + + '

' + + 'Expected result: opens successfully in InAppBrowser with locationBar. On iOS the toolbar is at the bottom.' + + '

' + + 'Expected result: opens successfully in InAppBrowser with locationBar. On iOS the toolbar is at the top.' + + '

' + + 'Expected result: open successfully in InAppBrowser with no locationBar. On iOS the toolbar is at the top.'; + + var white_listed_tests = '

White Listed URL

' + + '
' + + 'Expected result: open successfully in CordovaWebView to cordova.apache.org' + + '

' + + 'Expected result: open successfully in CordovaWebView to cordova.apache.org (using hook of window.open())' + + '

' + + 'Expected result: open successfully in CordovaWebView to cordova.apache.org' + + '

' + + 'Expected result: open successfully in system browser to cordova.apache.org' + + '

' + + 'Expected result: open successfully in InAppBrowser to cordova.apache.org' + + '

' + + 'Expected result: open successfully in InAppBrowser to cordova.apache.org' + + '

' + + 'Expected result: open successfully in InAppBrowser to cordova.apache.org with no location bar.'; + + var non_white_listed_tests = '

Non White Listed URL

' + + '
' + + 'Expected result: open successfully in InAppBrowser to apple.com.' + + '

' + + 'Expected result: open successfully in InAppBrowser to apple.com (using hook of window.open()).' + + '

' + + 'Expected result: open successfully in InAppBrowser to apple.com (_self enforces whitelist).' + + '

' + + 'Expected result: open successfully in system browser to apple.com.' + + '

' + + 'Expected result: open successfully in InAppBrowser to apple.com.' + + '

' + + 'Expected result: open successfully in InAppBrowser to apple.com.' + + '

' + + 'Expected result: open successfully in InAppBrowser to apple.com without locationBar.'; + + var page_with_redirects_tests = '

Page with redirect

' + + '
' + + 'Expected result: should 301 and open successfully in InAppBrowser to https://www.google.co.uk.' + + '

' + + 'Expected result: should 302 and open successfully in InAppBrowser to www.zhihu.com/answer/16714076.'; + + var pdf_url_tests = '

PDF URL

' + + '
' + + 'Expected result: InAppBrowser opens. PDF should render on iOS.' + + '

' + + 'Expected result: InAppBrowser opens. PDF should render on iOS.'; + + var invalid_url_tests = '

Invalid URL

' + + '
' + + 'Expected result: fail to load in InAppBrowser.' + + '

' + + 'Expected result: fail to load in InAppBrowser.' + + '

' + + 'Expected result: fail to load in InAppBrowser (404).'; + + var css_js_injection_tests = '

CSS / JS Injection

' + + '
' + + 'Expected result: open successfully in InAppBrowser without text "Style updated from..."' + + '

' + + 'Expected result: open successfully in InAppBrowser with "Style updated from file".' + + '

' + + 'Expected result: open successfully in InAppBrowser with "Style updated from file", and alert dialog with text "Results verified".' + + '

' + + 'Expected result: open successfully in InAppBrowser with "Style updated from literal".' + + '

' + + 'Expected result: open successfully in InAppBrowser with "Style updated from literal", and alert dialog with text "Results verified".' + + '

' + + 'Expected result: open successfully in InAppBrowser with text "Script file successfully injected".' + + '

' + + 'Expected result: open successfully in InAppBrowser with text "Script file successfully injected" and alert dialog with the text "Results verified".' + + '

' + + 'Expected result: open successfully in InAppBrowser with the text "Script literal successfully injected" .' + + '

' + + 'Expected result: open successfully in InAppBrowser with the text "Script literal successfully injected" and alert dialog with the text "Results verified".'; + + var open_hidden_tests = '

Open Hidden

' + + '
' + + 'Expected result: no additional browser window. Alert appears with the text "background window loaded".' + + '

' + + 'Expected result: after first clicking on previous test "create hidden", open successfully in InAppBrowser to https://www.google.co.uk.' + + '

' + + 'Expected result: no output. But click on "show hidden" again and nothing should be shown.' + + '

' + + 'Expected result: open successfully in InAppBrowser to https://www.google.co.uk' + + '

' + + 'Expected result: open successfully in InAppBrowser to https://www.google.co.uk. Hide after 2 seconds'; + + var clearing_cache_tests = '

Clearing Cache

' + + '
' + + 'Expected result: ?' + + '

' + + 'Expected result: ?'; + + var video_tag_tests = '

Video tag

' + + '
' + + 'Expected result: open successfully in InAppBrowser with an embedded video plays automatically on iOS and Android.' + + '
' + + 'Expected result: open successfully in InAppBrowser with an embedded video plays automatically on iOS and Android.' + + '
' + + 'Expected result: open successfully in InAppBrowser with an embedded video does not play automatically on iOS and Android but rather works after clicking the "play" button.'; + + var local_with_anchor_tag_tests = '

Local with anchor tag

' + + '
' + + 'Expected result: open successfully in InAppBrowser to the local page, scrolled to the top as normal.' + + '

' + + 'Expected result: open successfully in InAppBrowser to the local page, scrolled to the beginning of the tall div with border.'; + + var hardwareback_tests = '

HardwareBack

' + + '

' + + 'Expected result: By default hardwareback is yes so pressing back button should navigate backwards in history then close InAppBrowser' + + '

' + + 'Expected result: hardwareback=yes pressing back button should navigate backwards in history then close InAppBrowser' + + '

' + + 'Expected result: hardwareback=no pressing back button should close InAppBrowser regardless history' + + '

' + + 'Expected result: consistently open browsers with with the appropriate option: hardwareback=defaults to yes then hardwareback=no then hardwareback=defaults to yes. By default hardwareback is yes so pressing back button should navigate backwards in history then close InAppBrowser'; + + // CB-7490 We need to wrap this code due to Windows security restrictions + // see http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx#differences for details + if (window.MSApp && window.MSApp.execUnsafeLocalFunction) { + MSApp.execUnsafeLocalFunction(function () { + contentEl.innerHTML = info_div + local_tests + white_listed_tests + non_white_listed_tests + page_with_redirects_tests + pdf_url_tests + invalid_url_tests + + css_js_injection_tests + open_hidden_tests + clearing_cache_tests + video_tag_tests + local_with_anchor_tag_tests + hardwareback_tests; + }); + } else { + contentEl.innerHTML = info_div + local_tests + white_listed_tests + non_white_listed_tests + page_with_redirects_tests + pdf_url_tests + invalid_url_tests + + css_js_injection_tests + open_hidden_tests + clearing_cache_tests + video_tag_tests + local_with_anchor_tag_tests + hardwareback_tests; + } + + document.getElementById('user-agent').textContent = navigator.userAgent; + + // we are already in cdvtests directory + var basePath = 'iab-resources/'; + var localhtml = basePath + 'local.html'; + var localpdf = basePath + 'local.pdf'; + var injecthtml = basePath + 'inject.html'; + var injectjs = isWindows ? basePath + 'inject.js' : 'inject.js'; + var injectcss = isWindows ? basePath + 'inject.css' : 'inject.css'; + var videohtml = basePath + 'video.html'; + + // Local + createActionButton('target=Default', function () { + doOpen(localhtml); + }, 'openLocal'); + createActionButton('target=Default (window.open)', function () { + doHookOpen(localhtml); + }, 'openLocalHook'); + createActionButton('target=_self', function () { + doOpen(localhtml, '_self'); + }, 'openLocalSelf'); + createActionButton('target=_system', function () { + doOpen(localhtml, '_system'); + }, 'openLocalSystem'); + createActionButton('target=_blank', function () { + doOpen(localhtml, '_blank'); + }, 'openLocalBlank'); + createActionButton('target=Random, location=no, disallowoverscroll=yes', function () { + doOpen(localhtml, 'random_string', 'location=no, disallowoverscroll=yes'); + }, 'openLocalRandomNoLocation'); + createActionButton('target=Random, toolbarposition=bottom', function () { + doOpen(localhtml, 'random_string', 'toolbarposition=bottom'); + }, 'openLocalRandomToolBarBottom'); + createActionButton('target=Random, toolbarposition=top', function () { + doOpen(localhtml, 'random_string', 'toolbarposition=top'); + }, 'openLocalRandomToolBarTop'); + createActionButton('target=Random, toolbarposition=top, location=no', function () { + doOpen(localhtml, 'random_string', 'toolbarposition=top,location=no'); + }, 'openLocalRandomToolBarTopNoLocation'); + + // White Listed + createActionButton('* target=Default', function () { + doOpen('http://cordova.apache.org'); + }, 'openWhiteListed'); + createActionButton('* target=Default (window.open)', function () { + doHookOpen('http://cordova.apache.org'); + }, 'openWhiteListedHook'); + createActionButton('* target=_self', function () { + doOpen('http://cordova.apache.org', '_self'); + }, 'openWhiteListedSelf'); + createActionButton('target=_system', function () { + doOpen('http://cordova.apache.org', '_system'); + }, 'openWhiteListedSystem'); + createActionButton('target=_blank', function () { + doOpen('http://cordova.apache.org', '_blank'); + }, 'openWhiteListedBlank'); + createActionButton('target=Random', function () { + doOpen('http://cordova.apache.org', 'random_string'); + }, 'openWhiteListedRandom'); + createActionButton('* target=Random, no location bar', function () { + doOpen('http://cordova.apache.org', 'random_string', 'location=no'); + }, 'openWhiteListedRandomNoLocation'); + + // Non White Listed + createActionButton('target=Default', function () { + doOpen('http://www.apple.com'); + }, 'openNonWhiteListed'); + createActionButton('target=Default (window.open)', function () { + doHookOpen('http://www.apple.com'); + }, 'openNonWhiteListedHook'); + createActionButton('target=_self', function () { + doOpen('http://www.apple.com', '_self'); + }, 'openNonWhiteListedSelf'); + createActionButton('target=_system', function () { + doOpen('http://www.apple.com', '_system'); + }, 'openNonWhiteListedSystem'); + createActionButton('target=_blank', function () { + doOpen('http://www.apple.com', '_blank'); + }, 'openNonWhiteListedBlank'); + createActionButton('target=Random', function () { + doOpen('http://www.apple.com', 'random_string'); + }, 'openNonWhiteListedRandom'); + createActionButton('* target=Random, no location bar', function () { + doOpen('http://www.apple.com', 'random_string', 'location=no'); + }, 'openNonWhiteListedRandomNoLocation'); + + // Page with redirect + createActionButton('http://google.co.uk', function () { + doOpen('http://google.co.uk', 'random_string', '', 1); + }, 'openRedirect301'); + createActionButton('http://goo.gl/pUFqg', function () { + doOpen('http://goo.gl/pUFqg', 'random_string', '', 2); + }, 'openRedirect302'); + + // PDF URL + createActionButton('Remote URL', function () { + doOpen('http://www.stluciadance.com/prospectus_file/sample.pdf'); + }, 'openPDF'); + createActionButton('Local URL', function () { + doOpen(localpdf, '_blank'); + }, 'openPDFBlank'); + + // Invalid URL + createActionButton('Invalid Scheme', function () { + doOpen('x-ttp://www.invalid.com/', '_blank'); + }, 'openInvalidScheme'); + createActionButton('Invalid Host', function () { + doOpen('http://www.inv;alid.com/', '_blank'); + }, 'openInvalidHost'); + createActionButton('Missing Local File', function () { + doOpen('nonexistent.html', '_blank'); + }, 'openInvalidMissing'); + + // CSS / JS injection + createActionButton('Original Document', function () { + doOpen(injecthtml, '_blank'); + }, 'openOriginalDocument'); + createActionButton('CSS File Injection', function () { + openWithStyle(injecthtml, injectcss); + }, 'openCSSInjection'); + createActionButton('CSS File Injection (callback)', function () { + openWithStyle(injecthtml, injectcss, true); + }, 'openCSSInjectionCallback'); + createActionButton('CSS Literal Injection', function () { + openWithStyle(injecthtml); + }, 'openCSSLiteralInjection'); + createActionButton('CSS Literal Injection (callback)', function () { + openWithStyle(injecthtml, null, true); + }, 'openCSSLiteralInjectionCallback'); + createActionButton('Script File Injection', function () { + openWithScript(injecthtml, injectjs); + }, 'openScriptInjection'); + createActionButton('Script File Injection (callback)', function () { + openWithScript(injecthtml, injectjs, true); + }, 'openScriptInjectionCallback'); + createActionButton('Script Literal Injection', function () { + openWithScript(injecthtml); + }, 'openScriptLiteralInjection'); + createActionButton('Script Literal Injection (callback)', function () { + openWithScript(injecthtml, null, true); + }, 'openScriptLiteralInjectionCallback'); + + // Open hidden + createActionButton('Create Hidden', function () { + openHidden('https://www.google.co.uk', true); + }, 'openHidden'); + createActionButton('Show Hidden', function () { + showHidden(); + }, 'showHidden'); + createActionButton('Close Hidden', function () { + closeHidden(); + }, 'closeHidden'); + createActionButton('google.co.uk Not Hidden', function () { + openHidden('https://www.google.co.uk', false); + }, 'openHiddenShow'); + createActionButton('google.co.uk shown for 2 seconds than hidden', function () { + var iab = doOpen('https://www.google.co.uk/', 'random_sting'); + setTimeout(function () { + iab.hide(); + }, 2000); + }, 'openVisibleAndHide'); + + // Clearing cache + createActionButton('Clear Browser Cache', function () { + doOpen('https://www.google.co.uk', '_blank', 'clearcache=yes'); + }, 'openClearCache'); + createActionButton('Clear Session Cache', function () { + doOpen('https://www.google.co.uk', '_blank', 'clearsessioncache=yes'); + }, 'openClearSessionCache'); + + // Video tag + createActionButton('Remote Video', function () { + doOpen(videohtml, '_blank'); + }, 'openRemoteVideo'); + createActionButton('Remote Need User No Video', function () { + doOpen(videohtml, '_blank', 'mediaPlaybackRequiresUserAction=no'); + }, 'openRemoteNeedUserNoVideo'); + createActionButton('Remote Need User Yes Video', function () { + doOpen(videohtml, '_blank', 'mediaPlaybackRequiresUserAction=yes'); + }, 'openRemoteNeedUserYesVideo'); + + // Local With Anchor Tag + createActionButton('Anchor1', function () { + doOpen(localhtml + '#bogusanchor', '_blank'); + }, 'openAnchor1'); + createActionButton('Anchor2', function () { + doOpen(localhtml + '#anchor2', '_blank'); + }, 'openAnchor2'); + + // Hardwareback + createActionButton('no hardwareback (defaults to yes)', function () { + doOpen('http://cordova.apache.org', '_blank'); + }, 'openHardwareBackDefault'); + createActionButton('hardwareback=yes', function () { + doOpen('http://cordova.apache.org', '_blank', 'hardwareback=yes'); + }, 'openHardwareBackYes'); + createActionButton('hardwareback=no', function () { + doOpen('http://cordova.apache.org', '_blank', 'hardwareback=no'); + }, 'openHardwareBackNo'); + createActionButton('no hardwareback -> hardwareback=no -> no hardwareback', function () { + var ref = cordova.InAppBrowser.open('https://google.com', '_blank', 'location=yes'); + ref.addEventListener('loadstop', function () { + ref.close(); + }); + ref.addEventListener('exit', function () { + var ref2 = cordova.InAppBrowser.open('https://google.com', '_blank', 'location=yes,hardwareback=no'); + ref2.addEventListener('loadstop', function () { + ref2.close(); + }); + ref2.addEventListener('exit', function () { + cordova.InAppBrowser.open('https://google.com', '_blank', 'location=yes'); + }); + }); + }, 'openHardwareBackDefaultAfterNo'); +}; diff --git a/plugins/cordova-plugin-inappbrowser/types/index.d.ts b/plugins/cordova-plugin-inappbrowser/types/index.d.ts new file mode 100644 index 0000000..ea3d3ad --- /dev/null +++ b/plugins/cordova-plugin-inappbrowser/types/index.d.ts @@ -0,0 +1,221 @@ +// Type definitions for Apache Cordova InAppBrowser plugin +// Project: https://github.com/apache/cordova-plugin-inappbrowser +// Definitions by: Microsoft Open Technologies Inc +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// +// Copyright (c) Microsoft Open Technologies Inc +// Licensed under the MIT license. + +interface Window { + /** + * Opens a URL in a new InAppBrowser instance, the current browser instance, or the system browser. + * @param url The URL to load. + * @param target The target in which to load the URL, an optional parameter that defaults to _self. + * @param options Options for the InAppBrowser. Optional, defaulting to: location=yes. + * The options string must not contain any blank space, and each feature's + * name/value pairs must be separated by a comma. Feature names are case insensitive. + */ + open(url: string, target?: "_self", options?: string): InAppBrowser; + /** + * Opens a URL in a new InAppBrowser instance, the current browser instance, or the system browser. + * @param url The URL to load. + * @param target The target in which to load the URL, an optional parameter that defaults to _self. + * @param options Options for the InAppBrowser. Optional, defaulting to: location=yes. + * The options string must not contain any blank space, and each feature's + * name/value pairs must be separated by a comma. Feature names are case insensitive. + */ + open(url: string, target?: "_blank", options?: string): InAppBrowser; + /** + * Opens a URL in a new InAppBrowser instance, the current browser instance, or the system browser. + * @param url The URL to load. + * @param target The target in which to load the URL, an optional parameter that defaults to _self. + * @param options Options for the InAppBrowser. Optional, defaulting to: location=yes. + * The options string must not contain any blank space, and each feature's + * name/value pairs must be separated by a comma. Feature names are case insensitive. + */ + open(url: string, target?: "_system", options?: string): InAppBrowser; + /** + * Opens a URL in a new InAppBrowser instance, the current browser instance, or the system browser. + * @param url The URL to load. + * @param target The target in which to load the URL, an optional parameter that defaults to _self. + * @param options Options for the InAppBrowser. Optional, defaulting to: location=yes. + * The options string must not contain any blank space, and each feature's + * name/value pairs must be separated by a comma. Feature names are case insensitive. + */ + open(url: string, target?: string, options?: string, replace?: boolean): InAppBrowser; +} + +/** + * The object returned from a call to window.open. + * NOTE: The InAppBrowser window behaves like a standard web browser, and can't access Cordova APIs. + */ +interface InAppBrowser extends Window { + onloadstart: (type: InAppBrowserEvent) => void; + onloadstop: (type: InAppBrowserEvent) => void; + onloaderror: (type: InAppBrowserEvent) => void; + onexit: (type: InAppBrowserEvent) => void; + // addEventListener overloads + /** + * Adds a listener for an event from the InAppBrowser. + * @param type the event to listen for + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + addEventListener(type: "loadstart", callback: (event: InAppBrowserEvent) => void): void; + /** + * Adds a listener for an event from the InAppBrowser. + * @param type the event to listen for + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + addEventListener(type: "loadstop", callback: (event: InAppBrowserEvent) => void): void; + /** + * Adds a listener for an event from the InAppBrowser. + * @param type the event to listen for + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + addEventListener(type: "loaderror", callback: (event: InAppBrowserEvent) => void): void; + /** + * Adds a listener for an event from the InAppBrowser. + * @param type the event to listen for + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + addEventListener(type: "exit", callback: (event: InAppBrowserEvent) => void): void; + /** + * Adds a listener for an event from the InAppBrowser. + * @param type the event to listen for + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an Event object as a parameter. + */ + addEventListener(type: string, callback: (event: Event) => void): void; + // removeEventListener overloads + /** + * Removes a listener for an event from the InAppBrowser. + * @param type The event to stop listening for. + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + removeEventListener(type: "loadstart", callback: (event: InAppBrowserEvent) => void): void; + /** + * Removes a listener for an event from the InAppBrowser. + * @param type The event to stop listening for. + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + removeEventListener(type: "loadstop", callback: (event: InAppBrowserEvent) => void): void; + /** + * Removes a listener for an event from the InAppBrowser. + * @param type The event to stop listening for. + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + removeEventListener(type: "loaderror", callback: (event: InAppBrowserEvent) => void): void; + /** + * Removes a listener for an event from the InAppBrowser. + * @param type The event to stop listening for. + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an InAppBrowserEvent object as a parameter. + */ + removeEventListener(type: "exit", callback: (event: InAppBrowserEvent) => void): void; + /** + * Removes a listener for an event from the InAppBrowser. + * @param type The event to stop listening for. + * loadstart: event fires when the InAppBrowser starts to load a URL. + * loadstop: event fires when the InAppBrowser finishes loading a URL. + * loaderror: event fires when the InAppBrowser encounters an error when loading a URL. + * exit: event fires when the InAppBrowser window is closed. + * @param callback the function that executes when the event fires. The function is + * passed an Event object as a parameter. + */ + removeEventListener(type: string, callback: (event: Event) => void): void; + /** Closes the InAppBrowser window. */ + close(): void; + /** Hides the InAppBrowser window. Calling this has no effect if the InAppBrowser was already hidden. */ + hide(): void; + /** + * Displays an InAppBrowser window that was opened hidden. Calling this has no effect + * if the InAppBrowser was already visible. + */ + show(): void; + /** + * Injects JavaScript code into the InAppBrowser window. + * @param script Details of the script to run, specifying either a file or code key. + * @param callback The function that executes after the JavaScript code is injected. + * If the injected script is of type code, the callback executes with + * a single parameter, which is the return value of the script, wrapped in an Array. + * For multi-line scripts, this is the return value of the last statement, + * or the last expression evaluated. + */ + executeScript(script: { code: string }, callback: (result: any) => void): void; + /** + * Injects JavaScript code into the InAppBrowser window. + * @param script Details of the script to run, specifying either a file or code key. + * @param callback The function that executes after the JavaScript code is injected. + * If the injected script is of type code, the callback executes with + * a single parameter, which is the return value of the script, wrapped in an Array. + * For multi-line scripts, this is the return value of the last statement, + * or the last expression evaluated. + */ + executeScript(script: { file: string }, callback: (result: any) => void): void; + /** + * Injects CSS into the InAppBrowser window. + * @param css Details of the script to run, specifying either a file or code key. + * @param callback The function that executes after the CSS is injected. + */ + insertCSS(css: { code: string }, callback: () => void): void; + /** + * Injects CSS into the InAppBrowser window. + * @param css Details of the script to run, specifying either a file or code key. + * @param callback The function that executes after the CSS is injected. + */ + insertCSS(css: { file: string }, callback: () => void): void; +} + +interface InAppBrowserEvent extends Event { + /** the eventname, either loadstart, loadstop, loaderror, or exit. */ + type: string; + /** the URL that was loaded. */ + url: string; + /** the error code, only in the case of loaderror. */ + code: number; + /** the error message, only in the case of loaderror. */ + message: string; +} \ No newline at end of file diff --git a/plugins/cordova-plugin-share-content/README.rst b/plugins/cordova-plugin-share-content/README.rst new file mode 100644 index 0000000..b2dbd6c --- /dev/null +++ b/plugins/cordova-plugin-share-content/README.rst @@ -0,0 +1,21 @@ +Supported Platform +================== + +iOS/Android + +Installation +============ + +``cordova plugin add https://github.com/six519/cordova-plugin-share-content.git`` + +Usage +===== +:: + + //share text + window.shareContentPlugin.share('', function(e){ + //success callback + }, function(e){ + //error callback + alert('The error is: ' + e); + }); \ No newline at end of file diff --git a/plugins/cordova-plugin-share-content/package.json b/plugins/cordova-plugin-share-content/package.json new file mode 100644 index 0000000..79197e9 --- /dev/null +++ b/plugins/cordova-plugin-share-content/package.json @@ -0,0 +1,64 @@ +{ + "_from": "cordova-plugin-share-content@^1.0.0", + "_id": "cordova-plugin-share-content@1.0.0", + "_inBundle": false, + "_integrity": "sha1-PME2UYnKuCW8JTmOY4NcQtO5M7Q=", + "_location": "/cordova-plugin-share-content", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "cordova-plugin-share-content@^1.0.0", + "name": "cordova-plugin-share-content", + "escapedName": "cordova-plugin-share-content", + "rawSpec": "^1.0.0", + "saveSpec": null, + "fetchSpec": "^1.0.0" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/cordova-plugin-share-content/-/cordova-plugin-share-content-1.0.0.tgz", + "_shasum": "3cc1365189cab825bc25398e63835c42d3b933b4", + "_spec": "cordova-plugin-share-content@^1.0.0", + "_where": "/home/thrrgilag/workspace/cordova/Goober", + "author": { + "name": "Ferdinand E. Silva" + }, + "bugs": { + "url": "https://github.com/six519/cordova-plugin-share-content/issues" + }, + "bundleDependencies": false, + "cordova": { + "id": "cordova-plugin-share-content", + "platforms": [ + "android", + "ios" + ] + }, + "deprecated": false, + "description": "Cordova Plugin To Share Data To Other Apps", + "engines": [ + { + "name": "cordova-plugman", + "version": ">=4.2.0" + } + ], + "homepage": "https://github.com/six519/cordova-plugin-share-content#readme", + "keywords": [ + "cordova", + "share", + "content", + "ecosystem:cordova", + "cordova-android", + "cordova-ios" + ], + "license": "Apache 2.0", + "name": "cordova-plugin-share-content", + "repository": { + "type": "git", + "url": "git+https://github.com/six519/cordova-plugin-share-content.git" + }, + "version": "1.0.0" +} diff --git a/plugins/cordova-plugin-share-content/plugin.xml b/plugins/cordova-plugin-share-content/plugin.xml new file mode 100644 index 0000000..81c0614 --- /dev/null +++ b/plugins/cordova-plugin-share-content/plugin.xml @@ -0,0 +1,43 @@ + + + Share Content Cordova Plugin + + Apache 2.0 + cordova,share,content + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/cordova-plugin-share-content/scripts/add_swift_support.js b/plugins/cordova-plugin-share-content/scripts/add_swift_support.js new file mode 100644 index 0000000..88df1ef --- /dev/null +++ b/plugins/cordova-plugin-share-content/scripts/add_swift_support.js @@ -0,0 +1,173 @@ +/** + * Copied/pasted this hook from https://github.com/cowbell/cordova-plugin-geofence/blob/master/hooks/add_swift_support.js + * + * Many many thanks to those who provided this hook! :) + ***/ +var child_process = require('child_process'), + fs = require('fs'), + path = require('path'); + +module.exports = function(context) { + var IOS_DEPLOYMENT_TARGET = '8.0', + COMMENT_KEY = /_comment$/, + CORDOVA_VERSION = process.env.CORDOVA_VERSION; + + run(); + + function run() { + var cordova_util = context.requireCordovaModule('cordova-lib/src/cordova/util'), + ConfigParser = CORDOVA_VERSION >= 6.0 + ? context.requireCordovaModule('cordova-common').ConfigParser + : context.requireCordovaModule('cordova-lib/src/configparser/ConfigParser'), + projectRoot = cordova_util.isCordova(), + platform_ios, + xml = cordova_util.projectConfig(projectRoot), + cfg = new ConfigParser(xml), + projectName = cfg.name(), + platform_ios = CORDOVA_VERSION < 5.0 + ? context.requireCordovaModule('cordova-lib/src/plugman/platforms')['ios'] + : context.requireCordovaModule('cordova-lib/src/plugman/platforms/ios'), + iosPlatformPath = path.join(projectRoot, 'platforms', 'ios'), + iosProjectFilesPath = path.join(iosPlatformPath, projectName), + xcconfigPath = path.join(iosPlatformPath, 'cordova', 'build.xcconfig'), + xcconfigContent, + projectFile, + xcodeProject, + bridgingHeaderPath; + + projectFile = platform_ios.parseProjectFile(iosPlatformPath); + xcodeProject = projectFile.xcode; + + if (fs.existsSync(xcconfigPath)) { + xcconfigContent = fs.readFileSync(xcconfigPath, 'utf-8'); + } + + bridgingHeaderPath = getBridgingHeader(projectName, xcconfigContent, xcodeProject); + if(bridgingHeaderPath) { + bridgingHeaderPath = path.join(iosPlatformPath, bridgingHeaderPath); + } else { + bridgingHeaderPath = createBridgingHeader(xcodeProject, projectName, iosProjectFilesPath); + } + + getExistingBridgingHeaders(iosProjectFilesPath, function (headers) { + importBridgingHeaders(bridgingHeaderPath, headers); + var configurations = nonComments(xcodeProject.pbxXCBuildConfigurationSection()), + config, buildSettings; + + for (config in configurations) { + buildSettings = configurations[config].buildSettings; + buildSettings['IPHONEOS_DEPLOYMENT_TARGET'] = IOS_DEPLOYMENT_TARGET; + buildSettings['EMBEDDED_CONTENT_CONTAINS_SWIFT'] = "YES"; + buildSettings['LD_RUNPATH_SEARCH_PATHS'] = '"@executable_path/Frameworks"' + } + console.log('IOS project now has deployment target set as:[' + IOS_DEPLOYMENT_TARGET + '] ...'); + console.log('IOS project option EMBEDDED_CONTENT_CONTAINS_SWIFT set as:[YES] ...'); + console.log('IOS project swift_objc Bridging-Header set to:[' + bridgingHeaderPath + '] ...'); + console.log('IOS project Runpath Search Paths set to: @executable_path/Frameworks ...'); + + projectFile.write(); + }); + } + + function getBridgingHeader(projectName, xcconfigContent, xcodeProject) { + var configurations, + config, + buildSettings, + bridgingHeader; + + if (xcconfigContent) { + var regex = /^SWIFT_OBJC_BRIDGING_HEADER *=(.*)$/m, + match = xcconfigContent.match(regex); + + if (match) { + bridgingHeader = match[1]; + bridgingHeader = bridgingHeader + .replace("$(PROJECT_DIR)/", "") + .replace("$(PROJECT_NAME)", projectName) + .trim(); + + return bridgingHeader; + } + } + + configurations = nonComments(xcodeProject.pbxXCBuildConfigurationSection()); + + for (config in configurations) { + buildSettings = configurations[config].buildSettings; + bridgingHeader = buildSettings['SWIFT_OBJC_BRIDGING_HEADER']; + if (bridgingHeader) { + return unquote(bridgingHeader); + } + } + } + + function createBridgingHeader(xcodeProject, projectName, xcodeProjectRootPath) { + var newBHPath = path.join(xcodeProjectRootPath, "Plugins", "Bridging-Header.h"), + content = ["//", + "// Use this file to import your target's public headers that you would like to expose to Swift.", + "//", + "#import "] + + //fs.openSync(newBHPath, 'w'); + console.log('Creating new Bridging-Header.h at path: ', newBHPath); + fs.writeFileSync(newBHPath, content.join("\n"), { encoding: 'utf-8', flag: 'w' }); + xcodeProject.addHeaderFile("Bridging-Header.h"); + setBridgingHeader(xcodeProject, path.join(projectName, "Plugins", "Bridging-Header.h")); + return newBHPath; + } + + function setBridgingHeader(xcodeProject, headerPath) { + var configurations = nonComments(xcodeProject.pbxXCBuildConfigurationSection()), + config, buildSettings, bridgingHeader; + + for (config in configurations) { + buildSettings = configurations[config].buildSettings; + buildSettings['SWIFT_OBJC_BRIDGING_HEADER'] = '"' + headerPath + '"'; + } + } + + function getExistingBridgingHeaders(xcodeProjectRootPath, callback) { + var searchPath = path.join(xcodeProjectRootPath, 'Plugins'); + + child_process.exec('find . -name "*Bridging-Header*.h"', { cwd: searchPath }, function (error, stdout, stderr) { + var headers = stdout.toString().split('\n').map(function (filePath) { + return path.basename(filePath); + }); + callback(headers); + }); + } + + function importBridgingHeaders(mainBridgingHeader, headers) { + var content = fs.readFileSync(mainBridgingHeader, 'utf-8'), + mainHeaderName = path.basename(mainBridgingHeader); + + headers.forEach(function (header) { + if(header !== mainHeaderName && content.indexOf(header) < 0) { + if (content.charAt(content.length - 1) != '\n') { + content += "\n"; + } + content += "#import \""+header+"\"\n" + console.log('Importing ' + header + ' into main bridging-header at: ' + mainBridgingHeader); + } + }); + fs.writeFileSync(mainBridgingHeader, content, 'utf-8'); + } + + function nonComments(obj) { + var keys = Object.keys(obj), + newObj = {}, + i = 0; + + for (i; i < keys.length; i++) { + if (!COMMENT_KEY.test(keys[i])) { + newObj[keys[i]] = obj[keys[i]]; + } + } + + return newObj; + } + + function unquote(str) { + if (str) return str.replace(/^"(.*)"$/, "$1"); + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-share-content/src/android/ShareContentPlugin.java b/plugins/cordova-plugin-share-content/src/android/ShareContentPlugin.java new file mode 100644 index 0000000..b8320cc --- /dev/null +++ b/plugins/cordova-plugin-share-content/src/android/ShareContentPlugin.java @@ -0,0 +1,62 @@ +package com.ferdinandsilva.android; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ContentValues; +import android.net.Uri; +import android.app.Activity; +import android.telephony.SmsManager; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaInterface; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class ShareContentPlugin extends CordovaPlugin { + public static final String TAG = "ShareContentPlugin"; + + public static Context thisContext; + + public ShareContentPlugin() { + } + + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + ShareContentPlugin.thisContext = cordova.getActivity().getApplicationContext(); + } + + public void share(String text_to_share, CallbackContext callbackContext) { + + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_TEXT, text_to_share); + shareIntent.setType("text/plain"); + shareIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ShareContentPlugin.thisContext.startActivity(shareIntent); + callbackContext.success(text_to_share); + } + + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + + if ("share".equals(action)) { + share(args.get(0).toString(), callbackContext); + } else { + return false; + } + + return true; + } + +} \ No newline at end of file diff --git a/plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin-Bridging-Header.h b/plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin-Bridging-Header.h new file mode 100644 index 0000000..6a4723c --- /dev/null +++ b/plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin-Bridging-Header.h @@ -0,0 +1,6 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import +#import \ No newline at end of file diff --git a/plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin.swift b/plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin.swift new file mode 100644 index 0000000..23efab3 --- /dev/null +++ b/plugins/cordova-plugin-share-content/src/ios/ShareContentPlugin.swift @@ -0,0 +1,25 @@ +import UIKit + +@available(iOS 8.0, *) +@objc(ShareContentPlugin) class ShareContentPlugin : CDVPlugin { + + func share(command: CDVInvokedUrlCommand) { + var pluginResult = CDVPluginResult( + status: CDVCommandStatus_OK + ) + let text_to_share = command.arguments[0] as? String ?? "" + let uIActivityVC = UIActivityViewController(activityItems: [text_to_share], applicationActivities: nil) + + viewController.presentViewController(uIActivityVC, animated: true, completion: nil) + + pluginResult = CDVPluginResult( + status: CDVCommandStatus_OK, + messageAsString: text_to_share + ) + self.commandDelegate!.sendPluginResult( + pluginResult, + callbackId: command.callbackId + ) + + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/CONTRIBUTING.md b/plugins/cordova-plugin-splashscreen/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# Contributing to Apache Cordova + +Anyone can contribute to Cordova. And we need your contributions. + +There are multiple ways to contribute: report bugs, improve the docs, and +contribute code. + +For instructions on this, start with the +[contribution overview](http://cordova.apache.org/contribute/). + +The details are explained there, but the important items are: + - Sign and submit an Apache ICLA (Contributor License Agreement). + - Have a Jira issue open that corresponds to your contribution. + - Run the tests so your patch doesn't break existing functionality. + +We look forward to your contributions! diff --git a/plugins/cordova-plugin-splashscreen/LICENSE b/plugins/cordova-plugin-splashscreen/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/LICENSE @@ -0,0 +1,202 @@ + + 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. \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/NOTICE b/plugins/cordova-plugin-splashscreen/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/NOTICE @@ -0,0 +1,5 @@ +Apache Cordova +Copyright 2012 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/cordova-plugin-splashscreen/README.md b/plugins/cordova-plugin-splashscreen/README.md new file mode 100644 index 0000000..49ff0ef --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/README.md @@ -0,0 +1,522 @@ +--- +title: Splashscreen +description: Control the splash screen for your app. +--- + + +|AppVeyor|Travis CI| +|:-:|:-:| +|[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-plugin-splashscreen?branch=master)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-splashscreen)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-splashscreen)| + +# cordova-plugin-splashscreen + +This plugin is required to work with splash screens. This plugin displays and hides a splash screen during application launch. + +Report issues with this plugin on the [Apache Cordova issue tracker][Apache Cordova issue tracker]. + +## Installation + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + +## Supported Platforms + +- Android +- iOS +- Windows (`cordova-windows` version >= 4.4.0 is required) +- Browser + +__Note__: Extended splashscreen does not require the plugin on Windows (as opposed to Android and iOS) in case you don't use the plugin API, i.e. programmatic hide/show. + +### iOS-specific information + +There are two mechanisms for displaying a launch screen on iOS: + +1. Legacy launch images: images are sized exactly for the device's screen size. Does not support the iPad Pro 12.9's native resolution or split-screen/slide-over multitasking. + +2. Launch storyboard images: Images are sized based on scale, idiom, and size classes. Supports all devices, and can be used with split-screen/slide-over multitasking. + +Apple is moving away from legacy launch images. There is no official support for providing a native-resolution launch image for the iPad Pro 12.9 or for providing launch images that work with split-screen multitasking or slide-over. If your app doesn't need to support these contexts, then you can continue to use legacy launch images for as long as you like. + +The preferred method of providing launch images is to use a launch storyboard. For native app developers, the ideal launch storyboard is an unpopulated version of the app's user interface at launch. For non-native app developers who don't wish to learn Interface Builder, however, this plugin simulates the legacy launch image method as much as is feasible. + +#### Legacy launch images + +If you choose to use legacy launch images, you will use the following syntax in `config.xml`: + +``` + + + + + + + + + +``` + +Technically the filename for the `src` attribute can be anything you want; the filenames are used because they match what will be used when your project is compiled. The width and height attributes determine which launch images are displayed on which devices as follows: + +| width | height | device (orientation) | +|:-----------:|:------------:|:--------------------------------:| +| 320 | 480 | All non-retina iPhones and iPods | +| 640 | 960 | iPhone 4/4s (portrait) | +| 640 | 1136 | iPhone 5/5s/SE (portrait) | +| 750 | 1334 | iPhone 6/6s/7 (portrait) | +| 1242 | 2208 | iPhone 6+/6s+/7+ (portrait) | +| 2208 | 1242 | iPhone 6+/6s+/7+ (landscape) | +| 768 | 1024 | All non-retina iPads (portrait) | +| 1024 | 768 | All non-retina iPads (landscape) | +| 1536 | 2048 | All retina iPads (portrait) | +| 2048 | 1536 | All retina iPads (landscape) | + +Note: It is vitally important that the source image actually matches the size specified in the `width` and `height` attributes. If it does not, the device may fail to render it properly, if at all. + +#### Launch storyboard images + +In order to support newer form factors and split-screen/slide-over multitasking, you should use launch storyboard images. These are similar to the legacy launch images above, but there are crucial differences: + + - images are not specific to a given device. + + - images are scaled to fill the available viewport (while maintaining the aspect ratio). + + - the outer edges of the images will be cropped, and the amount will vary based on device an viewport. + + - there is no need to provide an image for each possible device, viewport, and orientation; iOS will choose the best image for the situation automatically. + +##### Designing launch storyboard images + +The key to designing a launch storyboard image is understanding that the edges of the image will almost certainly be cropped. Therefore, one should not place any important information near the edges of any images provided to the launch storyboard. Only the center is a safe area, and this all but guarantees that following Apple's advice of presenting an unpopulated user interface will not work well. + +Instead, the following tips should enable you to create a launch image that works across a multitude of form factors, viewports, and orientations: + + - Important graphics (logos, icons, titles) should be centered. The safe bounding region will vary, so you will need to test to ensure that the important graphics are never cropped. Better yet, don't supply any important graphics in the first place. + + - You _can_ fine-tune the placement and size of these graphics, but you don't have the same fine-grained control as you did with legacy launch images. + + - Use a simple color wash. If you use two colors, you'll want one color to fill the top half of the image, and the second to fill the bottom half. If you use a gradient, you'll probably want to ensure that the middle of the gradient lines up with the center of the image. + + - Don't worry about pixel perfection -- because the images are scaled, there's almost no chance the images will be perfectly fit to the pixel grid. Since all supported iOS devices use retina screens, users will be hard pressed to notice it anyway. + +It is important to understand the concept of scale, idiom, and size class traits in order to use launch storyboard images effectively. Of the images supplied to the launch storyboard, iOS will choose the image that best matches the device and viewport and render that image. It is possible to supply only one launch image if so desired, but it is also possible to fine-tune the displayed launch image based on traits. When fine-tuning, one can ignore traits that aren't targeted or supported by the app. + +> Note: If you are using launch storyboard images, there is no need to include legacy images. If you do, the legacy images will be copied, but not used. + +##### Scale + +| scale | devices | +|:-----------:|:----------------------:| +| 1x | All non-retina devices | +| 2x | Most retina devices | +| 3x | iPhone 6+/6s+,7s+ | + +In general, you'll want to supply 2x and 3x images. Cordova only supports retina devices now, so there's no point in supplying 1x images. + +##### Idioms + +| idiom | devices | +|:-----------:|:-------------:| +| ipad | All iPads | +| iphone | All iPhones and iPod Touches | +| universal | All devices | + +You only need to provide universal images unless you need to fine-tune for a specific device idiom. + +##### Size classes + +There are two size classes applies to both screen axes. Narrow viewports are considered to be the "compact" size class, and remaining viewports are considered "regular". When supplying images to Xcode, however, one must choose between "any & compact" and "any & regular". To stay consistent with the native terminology, this feature will match based on "any" and "compact". `any` will match regular-sized viewports. + +Note: this feature uses `com` as an abbreviation for "compact" classes. + +The following classes are supported by this feature: + +| width | height | orientation | +|:-----------:|:------------:|:-----------------:| +| any | any | any | +| com | any | portrait | +| any | com | landscape (wide) | +| com | com | landscape (narrow)| + +To see the complete list of size classes associated with devices and viewports, see . + +##### Single-image launch screen + +If your launch image is simple, you may be able to avoid creating a lot of different launch images and supply only one. The launch image needs to meet the following requirements: + + - the image should be square + + - the image should be large enough to fit on an iPad Pro 12.9": 2732x2732 + + - anything important should fit within the center + + Keep in mind that the image will be cropped, possibly quite severely, depending upon the viewport. + +Once the image is created, you can include it in your project by adding the following to `config.xml`: + +``` + +``` + +Because only one image is provided, iOS will utilize it in every context. + +##### Multi-image launch screen + +If a single launch image won't meet your needs, you will probably need to supply at least six images, if not more. Furthermore, keep in mind that it will not be possible to fine tune the image to a specific device, but only to a device class, display factor, and viewport size. + +If you don't need to target images to a specific idiom, you should create six images, as follows: + +| scale | idiom | width | height | size | filename | +|:-----------:|:-----------:|:-----------:|:------------:|:----------:|:--------------:| +| 2x* | universal | any | any | 2732x2732 | `Default@2x~universal~anyany.png` | +| 2x | universal | com | any | 1278x2732 | `Default@2x~universal~comany.png` | +| 2x | universal | com | com | 1334x750 | `Default@2x~universal~comcom.png` | +| 3x* | universal | any | any | 2208x2208 | `Default@3x~universal~anyany.png` | +| 3x | universal | any | com | 2208x1242 | `Default@3x~universal~anycom.png` | +| 3x | universal | com | any | 1242x2208 | `Default@3x~universal~comany.png` | + +\* this image is required in order for iOS utilize the other images within this scale and idiom. + +> Note: If the 3x sizes look small too you, that's because there's only one device class that currently has a 3x density: the iPhone 6+/6s+/7+. + +The above looks like the following snippet when present in `config.xml`: + +``` + + + + + + +``` + +Should one need to further fine tune based upon device idiom, one can do so. This might look like so: + +| scale | idiom | width | height | size | filename | +|:-----------:|:-----------:|:-----------:|:------------:|:----------:|:--------------:| +| 2x* | iphone | any | any | 1334x1334 | `Default@2x~iphone~anyany.png` | +| 2x | iphone | com | any | 750x1334 | `Default@2x~iphone~comany.png` | +| 2x | iphone | com | com | 1334x750 | `Default@2x~iphone~comcom.png` | +| 3x* | iphone | any | any | 2208x2208 | `Default@3x~iphone~anyany.png` | +| 3x | iphone | any | com | 2208x1242 | `Default@3x~iphone~anycom.png` | +| 3x | iphone | com | any | 1242x2208 | `Default@3x~iphone~comany.png` | +| 2x* | ipad | any | any | 2732x2732 | `Default@2x~ipad~anyany.png` | +| 2x | ipad | com | any | 1278x2732 | `Default@2x~ipad~comany.png` | + +\* this image is required in order for iOS utilize the other images within this scale and idiom. + +The above looks like the following in `config.xml`: + +``` + + + + + + + + +``` + +##### Quirks and Known Issues + +1. **App on target may not reflect changes to images** + Once you run the app on a target, iOS caches the launch image. Unfortunately, when you chance the images, iOS does _not_ invalidate the cache, which means you'll still see the old launch image. You should either: delete the app, or reset content & settings (simulator). + +2. **Simulator may not show expected images when launched from CLI** + When Xcode deploys to a specific simulator, it only copies the assets that match the simulator's characteristics. For example, if you try to run an app on the iPhone 6s Plus simulator, only @3x launch images are copied. When compiling from the CLI, however, the default is to assume an iPhone 5s, which means only @2x launch images are copied. Unless your launch images are markedly different, chances are good the difference would go unnoticed, but this does mean that the only accurate method of testing is to test on a physical device. + +3. **`anyany` must be provided for other variations to be used** + If you don't provide an `anyany` version of the launch image for a specific scale and idiom, the other variations (like `anycom`, `comany`, and `comcom`) will ignored. + +## Windows-specific information + +Splash screen images can be defined using the [MRT](https://cordova.apache.org/docs/en/dev/config_ref/images.html#windows) concept. +If you specify src="res/windows/splashscreen.png" the following files will be copied into the application's images folder: +`res/windows/splashscreen.png` | `res/windows/splashscreen.scale-100.png`, `res/windows/splashscreen.scale-125.png`, etc. +The following are supported: + +| Scale, % | Project | Width | Height | Filename | +|:------------:|:-------------------:|:-----------:|:------------:|:---------------------------------:| +| 100 | Windows 10/8.1 | 620 | 300 | `splashscreen.png` \| `splashscreen.scale-100.png` | +| 125 | Windows 10 | 775 | 375 | `splashscreen.scale-125.png` | +| 150 | Windows 10 | 930 | 450 | `splashscreen.scale-150.png` | +| 200 | Windows 10 | 1240 | 600 | `splashscreen.scale-200.png` | +| 400 | Windows 10 | 2480 | 1200 | `splashscreen.scale-400.png` | +| 140 | Windows 8.1 | 868 | 420 | `splashscreen.scale-140.png` | +| 180 | Windows 8.1 | 1116 | 540 | `splashscreen.scale-180.png` | +| 100 | Windows Phone 8.1 | 480 | 800 | `splashscreenphone.png` \| `splashscreenphone.scale-100.png` | +| 140 | Windows Phone 8.1 | 672 | 1120 | `splashscreenphone.scale-140.png` | +| 240 | Windows Phone 8.1 | 1152 | 1920 | `splashscreenphone.scale-240.png` | + +__Note__: SplashScreens size for Windows 10 project should not exceed 200 KBytes. +__Note__: Supported formats are `.png`, `.jpg`, `.jpeg`. Mixing of the extensions within a target is not supported. I.e. you can have `splashscreen.jpg` and `splashscreenphone.png` but not `splashscreen.scale-100.png`, `splashscreen.scale-400.jpg`. +__Note__: You may need to reopen Visual Studio solution after changing the images and doing a `cordova prepare` for the changes to take effect. + +## Example Configuration +In the top-level `config.xml` file (not the one in `platforms`), add configuration elements like those specified here. + +Please notice that the value of the "src" attribute is relative to the project root directory and not to the www directory (see `Directory structure` below). You can name the source image whatever you like. The internal name in the app is determined by Cordova. + +Directory structure: + +``` +projectRoot + hooks + platforms + plugins + www + css + img + js + res + screen + android + ios + windows +``` + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +## Preferences + +#### config.xml + +- `AutoHideSplashScreen` (boolean, default to `true`). Indicates whether to hide splash screen automatically or not. Splash screen hidden after amount of time specified in the `SplashScreenDelay` preference. + +```xml + +``` + +- `SplashScreenDelay` (number, default to 3000). Amount of time in milliseconds to wait before automatically hide splash screen. + +```xml + +``` + +Note also that this value used to be seconds, and not milliseconds, so values less than 30 will still be treated as seconds. ( Consider this a deprecated patch that will disapear in some future version. ) + +To disable the splashscreen add the following preference to `config.xml`: +```xml + +``` + +**Windows Quirk**: You should disable the splashscreen in case you are updating the entire document body dynamically (f.e. with a SPA router) to avoid affecting UI/controls. +Note that you should also directly reference `WinJS/base.js` in the page HTML in this case to avoid the issues with activation context ([CB-11658](https://issues.apache.org/jira/browse/CB-11658)). + +**iOS Quirk**: to disable the splashscreen on `ios` platform you should also add `` to `config.xml`. + +- `FadeSplashScreen` (boolean, defaults to `true`): Set to `false` to + prevent the splash screen from fading in and out when its display + state changes. + +```xml + +``` + +- `FadeSplashScreenDuration` (float, defaults to `500`): Specifies the + number of milliseconds for the splash screen fade effect to execute. + +```xml + +``` + +_Note_: `FadeSplashScreenDuration` is included into `SplashScreenDelay`, for example if you have `` and `` defined in `config.xml`: + +- 00:00 - splashscreen is shown +- 00:02 - fading has started +- 00:03 - splashscreen is hidden + +Turning the fading off via `` technically means fading duration to be `0` so that in this example the overall splash delay will still be 3 seconds. + +_Note_: This only applies to the app startup - you need to take the fading timeout into account when manually showing/hiding the splashscreen in the code: + +```javascript +navigator.splashscreen.show(); +window.setTimeout(function () { + navigator.splashscreen.hide(); +}, splashDuration - fadeDuration); +``` + +- `ShowSplashScreenSpinner` (boolean, defaults to `true`): Set to `false` + to hide the splash-screen spinner. + +```xml + +``` + +### Android Quirks + +In your `config.xml`, you can add the following preferences: + +```xml + + + +``` + +"SplashMaintainAspectRatio" preference is optional. If set to true, splash screen drawable is not stretched to fit screen, but instead simply "covers" the screen, like CSS "background-size:cover". This is very useful when splash screen images cannot be distorted in any way, for example when they contain scenery or text. This setting works best with images that have large margins (safe areas) that can be safely cropped on screens with different aspect ratios. + +The plugin reloads splash drawable whenever orientation changes, so you can specify different drawables for portrait and landscape orientations. + +"SplashShowOnlyFirstTime" preference is also optional and defaults to `true`. When set to `true` splash screen will only appear on application launch. However, if you plan to use `navigator.app.exitApp()` to close application and force splash screen appear on next launch, you should set this property to `false` (this also applies to closing the App with Back button). + +"SplashScreenSpinnerColor" preference is also optional and is ignored when not set. Setting it to a valid color name or HEX color code will change the color of the spinner on Android 5.0+ devices. + +### Browser Quirks + +You can use the following preferences in your `config.xml`: + +```xml + + + + + + + + + +``` + +__Note__: `SplashScreen` value should be absolute in order to work in a sub-page. The `SplashScreen` value is used only for the browser platform. The value will be ignored for other platforms. + +### iOS Quirks + +- In iOS, the splashscreen images are called launch images. These images are mandatory on iOS. + +### Windows Quirks + +- `SplashScreenSpinnerColor` (string, defaults to system accent color): hash, rgb notation or CSS color name. + +```xml + + + +``` + +- `SplashScreenBackgroundColor` (string, defaults to #464646): hex notation. + +```xml + +``` + +## Methods + +- splashscreen.show +- splashscreen.hide + +## splashscreen.hide + +Dismiss the splash screen. + +```js +navigator.splashscreen.hide(); +``` + + +### iOS Quirk + +The `config.xml` file's `AutoHideSplashScreen` setting must be +`false`. To delay hiding the splash screen for two seconds, add a +timer such as the following in the `deviceready` event handler: + +```js +setTimeout(function() { + navigator.splashscreen.hide(); +}, 2000); +``` + +## splashscreen.show + +Displays the splash screen. + +```js +navigator.splashscreen.show(); +``` + +Your application cannot call `navigator.splashscreen.show()` until the app has +started and the `deviceready` event has fired. But since typically the splash +screen is meant to be visible before your app has started, that would seem to +defeat the purpose of the splash screen. Providing some configuration in +`config.xml` will automatically `show` the splash screen immediately after your +app launch and before it has fully started and received the `deviceready` +event. For this reason, it is unlikely you need to call `navigator.splashscreen.show()` to make the splash +screen visible for app startup. + +[Apache Cordova issue tracker]: https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Splashscreen%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC diff --git a/plugins/cordova-plugin-splashscreen/RELEASENOTES.md b/plugins/cordova-plugin-splashscreen/RELEASENOTES.md new file mode 100644 index 0000000..e0c90c9 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/RELEASENOTES.md @@ -0,0 +1,238 @@ + +# Release Notes + +### 5.0.2 (Jan 24, 2018) +* [CB-13750](https://issues.apache.org/jira/browse/CB-13750) Add build-tools-26.0.2 to travis +* [CB-13737](https://issues.apache.org/jira/browse/CB-13737) (iOS): fix Splash screen images for iPhone X + +### 5.0.1 (Dec 27, 2017) +* [CB-13709](https://issues.apache.org/jira/browse/CB-13709) Fix to allow 5.0.0 version install (#144) + +### 5.0.0 (Dec 15, 2017) +* [CB-13677](https://issues.apache.org/jira/browse/CB-13677) Remove deprecated platforms + +### 4.1.0 (Nov 06, 2017) +* [CB-13473](https://issues.apache.org/jira/browse/CB-13473) (CI) Removed **Browser** builds from AppVeyor +* [CB-12011](https://issues.apache.org/jira/browse/CB-12011) (android) added the possibility to change the spinner color on **Android 5.0**+ apps +* [CB-13028](https://issues.apache.org/jira/browse/CB-13028) (CI) **Browser** builds on Travis and AppVeyor +* [CB-13094](https://issues.apache.org/jira/browse/CB-13094) (android) Don't show splash when activity being finished +* [CB-11487](https://issues.apache.org/jira/browse/CB-11487) (browser) Documented `AutoHideSplashScreen` for **Browser** +* [CB-11488](https://issues.apache.org/jira/browse/CB-11488) (browser) The `hide()` call became non re-entrant after the addition of fade out. This fixes the issue. +* [CB-11487](https://issues.apache.org/jira/browse/CB-11487) (browser) The standard `AutoHideSplashScreen` `config.xml` property is now supported by the **Browser** platform. +* [CB-11486](https://issues.apache.org/jira/browse/CB-11486) (browser) `splashScreenDelay` now feed through `parseInt` to ensure it is an integer by the time it's value is passed in to `setTimeout()` in `hide()`. +* [CB-12847](https://issues.apache.org/jira/browse/CB-12847) added `bugs` entry to `package.json`. + +### 4.0.3 (Apr 27, 2017) +* [CB-12622](https://issues.apache.org/jira/browse/CB-12622) Added **Android 6.0** build badge to `README` +* [CB-12685](https://issues.apache.org/jira/browse/CB-12685) added `package.json` to tests folder + +### 4.0.2 (Feb 28, 2017) +* [CB-12353](https://issues.apache.org/jira/browse/CB-12353) Corrected merges usage in `plugin.xml` +* [CB-12369](https://issues.apache.org/jira/browse/CB-12369) Add plugin typings from `DefinitelyTyped` +* [CB-12363](https://issues.apache.org/jira/browse/CB-12363) Added build badges for **iOS 9.3** and **iOS 10.0** +* [CB-12230](https://issues.apache.org/jira/browse/CB-12230) Removed **Windows 8.1** build badges + +### 4.0.1 (Dec 07, 2016) +* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 4.0.1 +* [CB-11751](https://issues.apache.org/jira/browse/CB-11751) 'extendedSplashScreen' is undefined Document that splashscreen needs to be disabled on Windows in case of updating entire document body +* [CB-9287](https://issues.apache.org/jira/browse/CB-9287) Not enough Icons and Splashscreens for **Windows 8.1** and Windows Phone 8.1 +* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…" +* [CB-11830](https://issues.apache.org/jira/browse/CB-11830) (iOS) Fix doc typos in PR#114 +* [CB-11829](https://issues.apache.org/jira/browse/CB-11829) (iOS) Support for CB-9762; docs (CB-11830) +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version. + +### 4.0.0 (Sep 08, 2016) +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* [CB-11326](https://issues.apache.org/jira/browse/CB-11326) Prevent crash when initializing plugin after navigating to another URL +* Fix crash on **iOS** when reloading page from remote **Safari** +* Add badges for paramedic builds on Jenkins +* Add pull request template. +* [CB-11179](https://issues.apache.org/jira/browse/CB-11179) Extend the windows-splashscreen docs +* [CB-11159](https://issues.apache.org/jira/browse/CB-11159) Fix flaky splashscreen native tests +* [CB-11156](https://issues.apache.org/jira/browse/CB-11156) Change default `FadeSplashScreenDuration` value +* [CB-8056](https://issues.apache.org/jira/browse/CB-8056) Updated the dependency version, added it to the docs +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md +* [CB-8056](https://issues.apache.org/jira/browse/CB-8056) Implement splashscreen for **Windows** platform +* [CB-6498](https://issues.apache.org/jira/browse/CB-6498) Misleading documentation in **Android** Quirks + +### 3.2.2 (Apr 15, 2016) +* [CB-10979](https://issues.apache.org/jira/browse/CB-10979) Fix splashscreen **iOS** native tests. Added `jshintignore` for tests/ios +* [CB-10895](https://issues.apache.org/jira/browse/CB-10895) Transparent Splashscreen view sometimes remains +* [CB-10562](https://issues.apache.org/jira/browse/CB-10562) `hide()` not working in latest splashscreen plug in 3.1.0 in **iOS** +* [CB-10688](https://issues.apache.org/jira/browse/CB-10688) Plugin Splashscreen Readme must have examples. +* [CB-10864](https://issues.apache.org/jira/browse/CB-10864) Run **iOS** native tests on Travis + +### 3.2.1 (Mar 09, 2016) +* [CB-10764](https://issues.apache.org/jira/browse/CB-10764) Remove emoji in cordova-plugin-splashscreen +* [CB-10650](https://issues.apache.org/jira/browse/CB-10650) Non-index content.src causes Splashscreen to be not displayed on **Browser** +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add JSHint for plugins +* [CB-10606](https://issues.apache.org/jira/browse/CB-10606) fix deprecation warning for interfaceOrientation on **iOS** +* chore: edit package.json license to match SPDX id + +### 3.2.0 (Feb 09, 2016) +* [CB-10422](https://issues.apache.org/jira/browse/CB-10422) Splashscreen displays black screen with no image on Android +* [CB-10412](https://issues.apache.org/jira/browse/CB-10412) AutoHideSplashScreen "false" isn't taken in account on iOS +* [CB-9516](https://issues.apache.org/jira/browse/CB-9516) Android SplashScreen - Spinner Does Not Display +* [CB-9094](https://issues.apache.org/jira/browse/CB-9094) Smarter autohide logic on Android +* [CB-8396](https://issues.apache.org/jira/browse/CB-8396) Add AutoHideSplashScreen logic to Android's Splashscreen + +### 3.1.0 (Jan 15, 2016) +* [CB-9538](https://issues.apache.org/jira/browse/CB-9538) Implementing `FadeSplashScreen` feature for **Android** +* [CB-9240](https://issues.apache.org/jira/browse/CB-9240) Cordova splash screen plugin **iPad** landscape mode issue +* [CB-10263](https://issues.apache.org/jira/browse/CB-10263) Fix splashscreen plugin filenames for Asset Catalog +* [CB-9374](https://issues.apache.org/jira/browse/CB-9374) **Android** add `SplashShowOnlyFirstTime` as preference +* [CB-10244](https://issues.apache.org/jira/browse/CB-10244) Don't rotate the **iPhone 6 Plus** splash +* [CB-9043](https://issues.apache.org/jira/browse/CB-9043) Fix the **ios** splashscreen being deformed on orientation change +* [CB-10079](https://issues.apache.org/jira/browse/CB-10079) Splashscreen plugin does not honor `SplashScreenDelay` on **iOS** +* [CB-10231](https://issues.apache.org/jira/browse/CB-10231) Fix `FadeSplashScreen` to default to true on **iOS** + +### 3.0.0 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* Fixing contribute link. +* [CB-9750](https://issues.apache.org/jira/browse/CB-9750) `FadeSplashDuration` is now in `msecs` +* [CB-8875](https://issues.apache.org/jira/browse/CB-8875) `FadeSplashScreen` was not fading +* [CB-9467](https://issues.apache.org/jira/browse/CB-9467) SplashScreen does not show any image in hosted app on **Windows 10** +* [CB-7282](https://issues.apache.org/jira/browse/CB-7282) Document `AutoHideSplashScreenpreference` +* [CB-9327](https://issues.apache.org/jira/browse/CB-9327) - Splashscreen not receiving `CDVPageLoadNotification` +* WP8: Avoid config `value` of a wrong element. + +### 2.1.0 (Jun 17, 2015) +* added missing license headers +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-splashscreen documentation translation: cordova-plugin-splashscreen +* fix npm md issue +* Fixed iOS unit tests. +* [CB-3562](https://issues.apache.org/jira/browse/CB-3562): Disable screen rotation for iPhone when splash screen is shown. (closes #47) +* [CB-8988](https://issues.apache.org/jira/browse/CB-8988): Fix rotation on iOS/iPad (closes #46) +* [CB-8904](https://issues.apache.org/jira/browse/CB-8904): Don't reset the static variable when it's destroyed, otherwise we might as well just have a member variable +* Removed wp7 from `plugin.xml` and package.json +* [CB-8750](https://issues.apache.org/jira/browse/CB-8750) [wp8]: Rewrite resoultion helper +* [CB-8750](https://issues.apache.org/jira/browse/CB-8750) [wp8]: Allow resolution-specific splashscreen images +* [CB-8758](https://issues.apache.org/jira/browse/CB-8758) [wp8]: UnauthorizedAccessException on hide() + +### 2.0.0 (Apr 15, 2015) +* give users a way to install the bleeding edge. +* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) gave plugin major version bump +* [CB-8797](https://issues.apache.org/jira/browse/CB-8797) - Splashscreen preferences FadeSplashScreenDuration and FadeSplashScreen (iOS) are missing +* [CB-8836](https://issues.apache.org/jira/browse/CB-8836) - Crashes after animating splashscreen +* [CB-8753](https://issues.apache.org/jira/browse/CB-8753) android: Fix missing import in previous commit +* [CB-8753](https://issues.apache.org/jira/browse/CB-8753) android: Adds `SplashMaintainAspectRatio` preference (close #43) +* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) changed plugin-id to pacakge-name +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) properly updated translated docs to use new id +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) updated translated docs to use new id +* [CB-8345](https://issues.apache.org/jira/browse/CB-8345) Make default for splashscreen resource "screen" (which is what template and CLI assume it to be) +* Revert "CB-8345 android: Make "splash" the default resource ID instead of null" +* Use TRAVIS_BUILD_DIR, install paramedic by npm +* [CB-8345](https://issues.apache.org/jira/browse/CB-8345) android: Make "splash" the default resource ID instead of null +* docs: added Windows to supported platforms +* [CB-7964](https://issues.apache.org/jira/browse/CB-7964) Add cordova-plugin-splashscreen support for browser platform +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* [wp8] oops, Added back config parse result checks +* [WP8] code cleanup, minor refactors, comments to clarify some stuff. +* Extend WP8 Splash Screen to respect SplashScreen and SplashScreenDelay preferences from config file +* [CB-8574](https://issues.apache.org/jira/browse/CB-8574) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-splashscreen documentation translation: cordova-plugin-splashscreen +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file +* [CB-8397](https://issues.apache.org/jira/browse/CB-8397) Add support to 'windows' for showing the Windows Phone splashscreen + +### 1.0.0 (Feb 04, 2015) +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Stop using deprecated IsIpad macro +* [CB-3679](https://issues.apache.org/jira/browse/CB-3679) Add engine tag for Android >= 3.6.0 due to use of `preferences` +* [CB-3679](https://issues.apache.org/jira/browse/CB-3679) Make SplashScreen plugin compatible with cordova-android@4.0.x + +### 0.3.5 (Dec 02, 2014) +* [CB-7204](https://issues.apache.org/jira/browse/CB-7204) - Race condition when hiding and showing spinner (closes #21) +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-splashscreen documentation translation: cordova-plugin-splashscreen + +### 0.3.4 (Oct 03, 2014) +* Finalized iOS splash screen (image name) tests. 176 tests in all, 44 for each type of device (iPad, iPhone, iPhone5, iPhone6, iPhone 6 Plus). +* [CB-7633](https://issues.apache.org/jira/browse/CB-7633) - (Re-fix based on updated unit tests) iPhone 6 Plus support +* Updated iOS tests for locked orientations +* Added more iOS splash screen tests. +* [CB-7633](https://issues.apache.org/jira/browse/CB-7633) - Add support for iPhone 6/6+ +* Added failing iPhone 6/6 Plus tests. +* Added 'npm test' +* [CB-7663](https://issues.apache.org/jira/browse/CB-7663) - iOS unit tests for splash screen +* Properly formatted splashscreen preference docs. + +### 0.3.3 (Sep 17, 2014) +* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-splashscreen documentation translation +* Renamed test dir, added nested `plugin.xml` +* added documentation for manual tests +* [CB-7196](https://issues.apache.org/jira/browse/CB-7196) port splashscreen tests to framework + +### 0.3.2 (Aug 06, 2014) +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs +* [CB-7041](https://issues.apache.org/jira/browse/CB-7041) ios: Fix image filename logic when setting the iPad splash screen +* fixes Splashscreen crash on WP8 +* Remove outdated doc + +### 0.3.1 (Jun 05, 2014) +* documentation translation: cordova-plugin-splashscreen +* Lisa testing pulling in plugins for plugin: cordova-plugin-splashscreen +* Lisa testing pulling in plugins for plugin: cordova-plugin-splashscreen +* Lisa testing pulling in plugins for plugin: cordova-plugin-splashscreen +* Lisa testing pulling in plugins for plugin: cordova-plugin-splashscreen +* [CB-6810](https://issues.apache.org/jira/browse/CB-6810) Add license to CONTRIBUTING.md +* [wp8] updated quirk for and combined iOS,WP8,BB10 quirks as they are all the same +* [wp] implemented OnInit so splash screen can be shown before cordova page is loaded +* [wp] plugin must be autoloaded for AutoHideSplashScreen preference to work +* [CB-6483](https://issues.apache.org/jira/browse/CB-6483) Use splash screen image from manifest on Windows8 +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* Revert "Merge branch 'tizen' of http://github.com/siovene/cordova-plugin-splashscreen" + +### 0.3.0 (Apr 17, 2014) +* Add Tizen support to plugin +* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy +* [CB-4051](https://issues.apache.org/jira/browse/CB-4051): [ios] - Re-fix - Splashscreen rotation problem (closes #13) +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers +* [CB-6465](https://issues.apache.org/jira/browse/CB-6465): Add license headers to Tizen code +* Add NOTICE file + +### 0.2.7 (Feb 05, 2014) +* [CB-3562](https://issues.apache.org/jira/browse/CB-3562) Fix aspect ratio on landscape-only iPhone applications +* [CB-4051](https://issues.apache.org/jira/browse/CB-4051) fix for splashscreen rotation problem + +### 0.2.6 (Jan 02, 2014) +* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc/index.md for Splashscreen plugin +* Handle error when splash image is missing. + +### 0.2.5 (Dec 4, 2013) +* add ubuntu platform +* Added amazon-fireos platform. Change to use amazon-fireos as a platform if the user agent string contains 'cordova-amazon-fireos' +* [CB-5124](https://issues.apache.org/jira/browse/CB-5124) - Remove splashscreen config.xml values from iOS Configuration Docs, move to plugin docs + +### 0.2.4 (Oct 28, 2013) +* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): add repo + issue tag to `plugin.xml` for splashscreen plugin +* [CB-5010](https://issues.apache.org/jira/browse/CB-5010) Incremented plugin version on dev branch. + +### 0.2.3 (Oct 9, 2013) +* [CB-4806](https://issues.apache.org/jira/browse/CB-4806) Re-fix Update splashscreen image bounds for iOS 7 +* [CB-4934](https://issues.apache.org/jira/browse/CB-4934) plugin-splashscreen should not show by default on Windows8 +* [CB-4929](https://issues.apache.org/jira/browse/CB-4929) plugin-splashscreen not loading proxy windows8 +* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch. + +### 0.2.2 (Sept 25, 2013) +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version +* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.splashscreen to org.apache.cordova.splashscreen +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4806](https://issues.apache.org/jira/browse/CB-4806) Update splashscreen image bounds for iOS 7 +* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch. diff --git a/plugins/cordova-plugin-splashscreen/doc/de/README.md b/plugins/cordova-plugin-splashscreen/doc/de/README.md new file mode 100644 index 0000000..f876eff --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/de/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +Dieses Plugin zeigt und verbirgt einen Splash-Screen beim Start der Anwendung. + +## Installation + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## Unterstützte Plattformen + + * Amazon Fire OS + * Android + * BlackBerry 10 + * iOS + * Windows Phone 7 und 8 + * Windows 8 + * Windows + * Browser + +## Methoden + + * SplashScreen.Show + * SplashScreen.Hide + +### Android Eigenarten + +Sie müssen in Ihrem `"config.xml"`fügen Sie die folgenden Einstellungen: + + + + + + +Wo Foo ist der Name der Datei Splashscreen, vorzugsweise eine 9-Patch-Datei. Stellen Sie sicher, Splashcreen Dateien zu Ihrem res/xml-Verzeichnis unter den entsprechenden Ordnern hinzuzufügen. Der zweite Parameter stellt dar, wie lange das Splashscreen in Millisekunden angezeigt werden. Es wird standardmäßig auf 3000 ms. Weitere Informationen finden Sie unter [Symbole und Splash-Screens](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html). + +"SplashMaintainAspectRatio" Präferenz ist optional. Wenn wahr, Splash-Screen zeichenbaren nicht gestreckt wird, um den Bildschirm passen, sondern stattdessen einfach "" den Bildschirm, wie CSS abdeckt "Hintergrund-Größe: Schutz vor". Dies ist sehr nützlich, wenn Splash-Bildschirm Bilder können nicht, in keiner Weise, zum Beispiel verzerrt werden wenn sie Landschaft oder Text enthalten. Diese Einstellung funktioniert am besten mit Bildern, die große Margen (sichere Bereiche) haben, die sicher auf Bildschirme mit unterschiedlichen Seitenverhältnissen zugeschnitten werden können. + +Das Plugin lädt platsch zeichenbaren wenn Ausrichtung ändert, sodass Sie verschiedene Drawables für hoch- und Querformat Ausrichtungen angeben können. + +### Browser-Eigenheiten + +In Ihrem `"config.xml"`können Sie die folgenden Einstellungen: + + + + + + + + + + + +### iOS Macken + + * `FadeSplashScreen` (Boolean, standardmäßig auf `true festgelegt`): um zu verhindern, dass den Begrüßungsbildschirm ein-und ausblenden bei ihrer Anzeige Statusänderungen auf `false` festgelegt. + + + + + * `FadeSplashScreenDuration` (float, Standardwert ist `2`): gibt die Anzahl der Sekunden für den Begrüßungsbildschirm fade Effekt ausgeführt. + + + + + * `ShowSplashScreenSpinner` (Boolean, standardmäßig auf `true festgelegt`): auf `false` festgelegt wird, um den Begrüßungsbildschirm Spinner auszublenden. + + + + +## SplashScreen.Hide + +Schließen Sie den Splash-Screen. + + navigator.splashscreen.hide(); + + +### BlackBerry 10, WP8, iOS Eigenarten + +Die Datei `config.xml` `AutoHideSplashScreen` Einstellung muss `false` sein. Verstecken des Begrüßungsbildschirms für zwei Sekunden Verzögerung, fügen Sie einen Timer wie die folgende in der `deviceready`-Ereignishandler: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## SplashScreen.Show + +Zeigt den Begrüßungsbildschirm. + + navigator.splashscreen.show(); + + +Ihre Anwendung kann nicht `navigator.splashscreen.show()` aufrufen, bis die app begonnen hat und das `deviceready`-Ereignis ausgelöst hat. Aber da in der Regel der Splash-Screen soll sichtbar sein, bevor die Anwendung gestartet wurde, scheint die Niederlage der Zweck des Begrüßungsbildschirms. Somit einige Konfiguration in der Datei `config.xml` werden automatisch die Splash `show` sofort nach Ihrer app-Start und Bildschirm bevor es voll begonnen hat, und das `deviceready`-Ereignis empfangen. Weitere Informationen zu dieser Konfiguration finden Sie unter [Symbole und Splash-Screens](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html). Aus diesem Grund ist es unwahrscheinlich, dass Sie `navigator.splashscreen.show()` damit den Splash-Screen sichtbar ist für app-Start aufrufen müssen. \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/de/index.md b/plugins/cordova-plugin-splashscreen/doc/de/index.md new file mode 100644 index 0000000..b9fc40d --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/de/index.md @@ -0,0 +1,78 @@ + + +# cordova-plugin-splashscreen + +Dieses Plugin zeigt und verbirgt einen Splash-Screen beim Start der Anwendung. + +## Installation + + cordova plugin add cordova-plugin-splashscreen + + +## Unterstützte Plattformen + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Windows Phone 7 und 8 +* Windows 8 + +## Methoden + +* SplashScreen.Show +* SplashScreen.Hide + +### Android Eigenarten + +Sie müssen in der config.xml folgende Einstellungen vornehmen: + + + + + +Wo Foo ist der Name der Datei Splashscreen, vorzugsweise eine 9-Patch-Datei. Stellen Sie sicher, Splashcreen Dateien zu Ihrem res/xml-Verzeichnis unter den entsprechenden Ordnern hinzuzufügen. Der zweite Parameter stellt dar, wie lange das Splashscreen in Millisekunden angezeigt werden. Es wird standardmäßig auf 3000 ms. Weitere Informationen finden Sie unter [Symbole und Splash-Screens][1]. + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## SplashScreen.Hide + +Schließen Sie den Splash-Screen. + + navigator.splashscreen.hide(); + + +### BlackBerry 10, WP8, iOS Eigenarten + +Die Datei `config.xml` `AutoHideSplashScreen` Einstellung muss `false` sein. Verstecken des Begrüßungsbildschirms für zwei Sekunden Verzögerung, fügen Sie einen Timer wie die folgende in der `deviceready`-Ereignishandler: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## SplashScreen.Show + +Zeigt den Begrüßungsbildschirm. + + navigator.splashscreen.show(); + + +Ihre Anwendung kann nicht `navigator.splashscreen.show()` aufrufen, bis die app begonnen hat und das `deviceready`-Ereignis ausgelöst hat. Aber da in der Regel der Splash-Screen soll sichtbar sein, bevor die Anwendung gestartet wurde, scheint die Niederlage der Zweck des Begrüßungsbildschirms. Somit einige Konfiguration in der Datei `config.xml` werden automatisch die Splash `show` sofort nach Ihrer app-Start und Bildschirm bevor es voll begonnen hat, und das `deviceready`-Ereignis empfangen. Weitere Informationen zu dieser Konfiguration finden Sie unter [Symbole und Splash-Screens][1]. Aus diesem Grund ist es unwahrscheinlich, dass Sie `navigator.splashscreen.show()` damit den Splash-Screen sichtbar ist für app-Start aufrufen müssen. diff --git a/plugins/cordova-plugin-splashscreen/doc/es/README.md b/plugins/cordova-plugin-splashscreen/doc/es/README.md new file mode 100644 index 0000000..1a94161 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/es/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +Este plugin muestra y esconde una pantalla de bienvenida durante el inicio de la aplicación. + +## Instalación + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## Plataformas soportadas + + * Amazon fire OS + * Android + * BlackBerry 10 + * iOS + * Windows Phone 7 y 8 + * Windows 8 + * Windows + * Explorador + +## Métodos + + * splashscreen.show + * splashscreen.hide + +### Rarezas Android + +En el `archivo config.xml`, es necesario agregar las siguientes preferencias: + + + + + + +Donde foo es el nombre del archivo splashscreen, preferiblemente un archivo de 9 parche. Asegúrese de agregar tus archivos splashcreen en tu directorio res/xml bajo las carpetas apropiadas. El segundo parámetro representa cuánto aparecerán el splashscreen en milisegundos. Valor predeterminado es ms 3000. Ver [los iconos y salpicadura pantallas](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html) para obtener más información. + +Preferencia "SplashMaintainAspectRatio" es opcional. Si establece en true, pantalla dibujable no es estirado para caber la pantalla, pero en su lugar simplemente "cover" la pantalla, como CSS "background-size: cover". Esto es muy útil cuando las imágenes de pantallas splash no distorsionadas de cualquier manera, por ejemplo cuando contienen texto o paisaje. Esta opción funciona mejor con imágenes que tienen bordes grandes (zonas seguras) que pueden ser recortadas con seguridad en pantallas con diferentes relaciones de aspecto. + +El plugin recarga splash dibujable cuando cambia de orientación, por lo que puede especificar diferente dibujo para orientaciones vertical y horizontal. + +### Navegador rarezas + +Puede utilizar las siguientes preferencias en el `archivo config.xml`: + + + + + + + + + + + +### iOS rarezas + + * `FadeSplashScreen` (booleano, por defecto `true`): establecida en `false` para evitar que la pantalla de bienvenida de descolorarse adentro y hacia fuera cuando cambia su estado de presentación. + + + + + * `FadeSplashScreenDuration` (float, por defecto es `2`): especifica el número de segundos para que la pantalla se descolora efecto para ejecutar. + + + + + * `ShowSplashScreenSpinner` (booleano, por defecto `true`): establecida en `false` para ocultar la ruleta de la pantalla de bienvenida. + + + + +## splashscreen.hide + +Despedir a la pantalla de bienvenida. + + navigator.splashscreen.hide(); + + +### BlackBerry 10, WP8, iOS Quirk + +El `config.xml` del archivo `AutoHideSplashScreen` la configuración debe ser `false` . Para retrasar oculta la pantalla splash durante dos segundos, agregue un temporizador como la siguiente en el `deviceready` controlador de eventos: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +Muestra la pantalla de bienvenida. + + navigator.splashscreen.show(); + + +La aplicación no se puede llamar `navigator.splashscreen.show()` hasta que haya iniciado la aplicación y el `deviceready` evento ha despedido. Pero puesto que normalmente la pantalla está destinada a ser visible antes de que comience su aplicación, que parecería que el propósito de la pantalla de bienvenida. Proporcionar cierta configuración en `config.xml` automáticamente `show` la pantalla de presentación inmediatamente después de su lanzamiento de la aplicación y antes de ser completamente ha iniciado y recibió el `deviceready` evento. Ver [los iconos y salpicadura pantallas](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html) para obtener más información sobre haciendo esta configuración. Por esta razón, es poco probable que necesitas llamar a `navigator.splashscreen.show()` para hacer la pantalla visible para el inicio de la aplicación. \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/es/index.md b/plugins/cordova-plugin-splashscreen/doc/es/index.md new file mode 100644 index 0000000..3295c27 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/es/index.md @@ -0,0 +1,76 @@ + + +# cordova-plugin-splashscreen + +Este plugin muestra y esconde una pantalla de bienvenida durante el inicio de la aplicación. + +## Instalación + + cordova plugin add cordova-plugin-splashscreen + + +## Plataformas soportadas + +* Amazon fire OS +* Android +* BlackBerry 10 +* iOS +* Windows Phone 7 y 8 +* Windows 8 + +## Métodos + +* splashscreen.show +* splashscreen.hide + +### Rarezas Android + +En el archivo config.xml, tienes que añadir las siguientes preferencias: + + < nombre de preferencia = "SplashScreen" value = "foo" / >< nombre de preferencia = "SplashScreenDelay" value = "10000" / > + + +Donde foo es el nombre del archivo splashscreen, preferiblemente un archivo de 9 parche. Asegúrese de agregar tus archivos splashcreen en tu directorio res/xml bajo las carpetas apropiadas. El segundo parámetro representa cuánto aparecerán el splashscreen en milisegundos. Valor predeterminado es ms 3000. Ver [los iconos y salpicadura pantallas][1] para obtener más información. + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.hide + +Despedir a la pantalla de bienvenida. + + Navigator.SplashScreen.Hide(); + + +### BlackBerry 10, WP8, iOS Quirk + +El `config.xml` del archivo `AutoHideSplashScreen` la configuración debe ser `false` . Para retrasar oculta la pantalla splash durante dos segundos, agregue un temporizador como la siguiente en el `deviceready` controlador de eventos: + + setTimeout(function() {navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +Muestra la pantalla de bienvenida. + + Navigator.SplashScreen.Show(); + + +La aplicación no se puede llamar `navigator.splashscreen.show()` hasta que haya iniciado la aplicación y el `deviceready` evento ha despedido. Pero puesto que normalmente la pantalla está destinada a ser visible antes de que comience su aplicación, que parecería que el propósito de la pantalla de bienvenida. Proporcionar cierta configuración en `config.xml` automáticamente `show` la pantalla de presentación inmediatamente después de su lanzamiento de la aplicación y antes de ser completamente ha iniciado y recibió el `deviceready` evento. Ver [los iconos y salpicadura pantallas][1] para obtener más información sobre haciendo esta configuración. Por esta razón, es poco probable que necesitas llamar a `navigator.splashscreen.show()` para hacer la pantalla visible para el inicio de la aplicación. diff --git a/plugins/cordova-plugin-splashscreen/doc/fr/README.md b/plugins/cordova-plugin-splashscreen/doc/fr/README.md new file mode 100644 index 0000000..65f5880 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/fr/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +Ce plugin affiche et masque un écran de démarrage lors du lancement de l'application. + +## Installation + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## Plates-formes supportées + + * Amazon Fire OS + * Android + * BlackBerry 10 + * iOS + * Windows Phone 7 et 8 + * Windows 8 + * Windows + * Navigateur + +## Méthodes + + * splashscreen.Show + * splashscreen.Hide + +### Quirks Android + +Dans votre `fichier config.xml`, vous devez ajouter les préférences suivantes : + + + + + + +Où foo est le nom du fichier splashscreen, préférablement un fichier de 9 correctif. Assurez-vous d'ajouter vos fichiers splashcreen dans votre répertoire res/xml dans les dossiers appropriés. Le deuxième paramètre représente combien de temps le splashscreen apparaîtra en millisecondes. Il est par défaut à 3000 ms. Pour plus d'informations, consultez [icônes et écrans de démarrage](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html). + +Préférence de « SplashMaintainAspectRatio » est facultative. Si défini à true, écran de démarrage drawable n'est pas étirée pour s'adapter écran, mais plutôt simplement « couvre » l'écran, comme CSS "fond-taille : couverture". Ceci est très utile lorsque images écran de démarrage ne peut pas être déformées en quelque sorte, par exemple lorsqu'ils contiennent des décors ou texte. Ce paramètre fonctionne mieux avec des images qui ont des marges importantes (zones de sécurité) qui peuvent être recadrées en toute sécurité sur les écrans avec des proportions différentes. + +Le plugin recharge splash drawable chaque fois que l'orientation change, donc vous pouvez spécifier différents drawables pour les orientations portrait et paysage. + +### Bizarreries navigateur + +Vous pouvez utiliser les préférences suivantes dans votre `fichier config.xml`: + + + + + + + + + + + +### Notes au sujet d'iOS + + * `FadeSplashScreen` (boolean, par défaut est `true`): la valeur `false` pour empêcher l'écran de démarrage de fading in et out lorsque son état d'affichage est modifié. + + + + + * `FadeSplashScreenDuration` (float, la valeur par défaut `2`): spécifie le nombre de secondes que l'écran de démarrage s'estomper l'effet d'exécuter. + + + + + * `ShowSplashScreenSpinner` (boolean, par défaut est `true`): la valeur `false` pour masquer le cône de l'écran de démarrage. + + + + +## splashscreen.Hide + +Faire disparaître de l'écran de démarrage. + + navigator.splashscreen.hide(); + + +### BlackBerry 10, WP8, iOS Quirk + +Paramètre `AutoHideSplashScreen` du fichier `config.xml` doit avoir la valeur `false`. Pour retarder la cacher l'écran de démarrage pendant deux secondes, ajouter un minuteur semblable à la suivante dans le gestionnaire d'événements `deviceready` : + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.Show + +Affiche l'écran de démarrage. + + navigator.splashscreen.show(); + + +Votre application ne peut pas appeler `navigator.splashscreen.show()` jusqu'à ce que l'application a commencé et l'événement `deviceready` est déclenché. Mais puisqu'en général, l'écran de démarrage est destiné à être visible avant que votre application a commencé, qui semblerait à l'encontre des objectifs de l'écran de démarrage. Fournir une configuration dans le fichier `config.xml` automatiquement `show` le splash projettera immédiatement après votre lancement de l'app et avant qu'il a complètement démarré et a reçu l'événement `deviceready`. Voir les [icônes et les écrans de démarrage](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html) pour plus d'informations sur la conduite de cette configuration. Pour cette raison, il est peu probable que vous devez appeler `navigator.splashscreen.show()` pour rendre l'écran de démarrage visible pour le démarrage de l'application. \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/fr/index.md b/plugins/cordova-plugin-splashscreen/doc/fr/index.md new file mode 100644 index 0000000..6d2fd08 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/fr/index.md @@ -0,0 +1,78 @@ + + +# cordova-plugin-splashscreen + +Ce plugin affiche et masque un écran de démarrage lors du lancement de l'application. + +## Installation + + cordova plugin add cordova-plugin-splashscreen + + +## Plates-formes prises en charge + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Windows Phone 7 et 8 +* Windows 8 + +## Méthodes + +* splashscreen.Show +* splashscreen.Hide + +### Quirks Android + +Dans votre fichier config.xml, vous devez ajouter les préférences suivantes : + + + + + +Où foo est le nom du fichier splashscreen, préférablement un fichier de 9 correctif. Assurez-vous d'ajouter vos fichiers splashcreen dans votre répertoire res/xml dans les dossiers appropriés. Le deuxième paramètre représente combien de temps le splashscreen apparaîtra en millisecondes. Il est par défaut à 3000 ms. Pour plus d'informations, consultez [icônes et écrans de démarrage][1]. + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.Hide + +Faire disparaître de l'écran de démarrage. + + navigator.splashscreen.hide(); + + +### BlackBerry 10, WP8, iOS Quirk + +Paramètre `AutoHideSplashScreen` du fichier `config.xml` doit avoir la valeur `false`. Pour retarder la cacher l'écran de démarrage pendant deux secondes, ajouter un minuteur semblable à la suivante dans le gestionnaire d'événements `deviceready` : + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.Show + +Affiche l'écran de démarrage. + + navigator.splashscreen.show(); + + +Votre application ne peut pas appeler `navigator.splashscreen.show()` jusqu'à ce que l'application a commencé et l'événement `deviceready` est déclenché. Mais puisqu'en général, l'écran de démarrage est destiné à être visible avant que votre application a commencé, qui semblerait à l'encontre des objectifs de l'écran de démarrage. Fournir une configuration dans le fichier `config.xml` automatiquement `show` le splash projettera immédiatement après votre lancement de l'app et avant qu'il a complètement démarré et a reçu l'événement `deviceready`. Voir les [icônes et les écrans de démarrage][1] pour plus d'informations sur la conduite de cette configuration. Pour cette raison, il est peu probable que vous devez appeler `navigator.splashscreen.show()` pour rendre l'écran de démarrage visible pour le démarrage de l'application. diff --git a/plugins/cordova-plugin-splashscreen/doc/it/README.md b/plugins/cordova-plugin-splashscreen/doc/it/README.md new file mode 100644 index 0000000..2a6c6ba --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/it/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +Questo plugin Visualizza e nasconde una schermata iniziale durante l'avvio dell'applicazione. + +## Installazione + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## Piattaforme supportate + + * Amazon fuoco OS + * Android + * BlackBerry 10 + * iOS + * Windows Phone 7 e 8 + * Windows 8 + * Windows + * Browser + +## Metodi + + * splashscreen + * splashscreen.Hide + +### Stranezze Android + +Nel vostro `config. XML`, è necessario aggiungere le seguenti preferenze: + + + + + + +Dove foo è il nome del file splashscreen, preferibilmente un file 9 patch. Assicurati di aggiungere i tuoi file splashcreen res/xml nella directory sotto cartelle appropriate. Il secondo parametro rappresenta quanto tempo lo splashscreen apparirà in millisecondi. Il valore predefinito è 3000 ms. Per ulteriori informazioni, vedere [icone e schermate iniziali](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html). + +"SplashMaintainAspectRatio" preferenza è facoltativo. Se impostato su true, schermata iniziale drawable non viene adattata per misura lo schermo, ma invece semplicemente "copre" lo schermo, come CSS "sfondo-dimensione: copertina". Questo è molto utile quando immagini schermata iniziale non possono essere distorta in qualche modo, per esempio quando contengono testo o scenario. Questa impostazione funziona meglio con immagini che hanno grandi margini (zone sicure) che possono essere ritagliati in modo sicuro su schermi con proporzioni diverse. + +Il plugin viene ricaricata splash drawable ogni volta che cambia orientamento, è possibile specificare diversi parte per orientamento verticale e orizzontale. + +### Stranezze browser + +Nel vostro `config. XML`, è possibile utilizzare le seguenti preferenze: + + + + + + + + + + + +### iOS stranezze + + * `FadeSplashScreen` (boolean, impostazioni predefinite a `true`): impostare su `false` per impedire che la schermata iniziale e scompaiono quando cambia il relativo stato di visualizzazione. + + + + + * `FadeSplashScreenDuration` (float, il valore predefinito è `2`): specifica il numero di secondi per la schermata iniziale dissolvenza effetto da eseguire. + + + + + * `ShowSplashScreenSpinner` (boolean, impostazioni predefinite a `true`): impostare su `false` per nascondere la filatrice schermata iniziale. + + + + +## splashscreen.Hide + +Respingere la schermata iniziale. + + navigator.splashscreen.hide(); + + +### BlackBerry 10, WP8, iOS Quirk + +Impostazione `AutoHideSplashScreen` del file `config.xml` deve essere `false`. Per ritardare nascondendo la schermata iniziale per due secondi, aggiungere un timer ad esempio nel gestore eventi `deviceready`: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen + +Visualizza la schermata iniziale. + + navigator.splashscreen.show(); + + +L'applicazione non può chiamare `navigator.splashscreen.show()` fino a quando l'app ha iniziato e ha generato l'evento `deviceready`. Ma poiché in genere la schermata iniziale è destinata ad essere visibile prima app ha iniziato, che sembrerebbe per sconfiggere lo scopo della schermata iniziale. Fornendo qualche configurazione nel `file config.xml` sarà automaticamente `show` il tonfo schermo subito dopo il lancio dell'app e prima che completamente ha iniziato e ha ricevuto l'evento `deviceready`. Per ulteriori informazioni su facendo questa configurazione, vedere [icone e schermate iniziali](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html). Per questo motivo, è improbabile che dovete chiamare `navigator.splashscreen.show()` per rendere la schermata visibile per avvio di app. \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/it/index.md b/plugins/cordova-plugin-splashscreen/doc/it/index.md new file mode 100644 index 0000000..7043541 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/it/index.md @@ -0,0 +1,78 @@ + + +# cordova-plugin-splashscreen + +Questo plugin Visualizza e nasconde una schermata iniziale durante l'avvio dell'applicazione. + +## Installazione + + cordova plugin add cordova-plugin-splashscreen + + +## Piattaforme supportate + +* Amazon fuoco OS +* Android +* BlackBerry 10 +* iOS +* Windows Phone 7 e 8 +* Windows 8 + +## Metodi + +* splashscreen +* splashscreen.Hide + +### Stranezze Android + +Nel vostro config. xml, è necessario aggiungere le seguenti preferenze: + + + + + +Dove foo è il nome del file splashscreen, preferibilmente un file 9 patch. Assicurati di aggiungere i tuoi file splashcreen res/xml nella directory sotto cartelle appropriate. Il secondo parametro rappresenta quanto tempo lo splashscreen apparirà in millisecondi. Il valore predefinito è 3000 ms. Per ulteriori informazioni, vedere [icone e schermate iniziali][1]. + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.Hide + +Respingere la schermata iniziale. + + navigator.splashscreen.hide(); + + +### BlackBerry 10, WP8, iOS Quirk + +Impostazione `AutoHideSplashScreen` del file `config.xml` deve essere `false`. Per ritardare nascondendo la schermata iniziale per due secondi, aggiungere un timer ad esempio nel gestore eventi `deviceready`: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen + +Visualizza la schermata iniziale. + + navigator.splashscreen.show(); + + +L'applicazione non può chiamare `navigator.splashscreen.show()` fino a quando l'app ha iniziato e ha generato l'evento `deviceready`. Ma poiché in genere la schermata iniziale è destinata ad essere visibile prima app ha iniziato, che sembrerebbe per sconfiggere lo scopo della schermata iniziale. Fornendo qualche configurazione nel `file config.xml` sarà automaticamente `show` il tonfo schermo subito dopo il lancio dell'app e prima che completamente ha iniziato e ha ricevuto l'evento `deviceready`. Per ulteriori informazioni su facendo questa configurazione, vedere [icone e schermate iniziali][1]. Per questo motivo, è improbabile che dovete chiamare `navigator.splashscreen.show()` per rendere la schermata visibile per avvio di app. diff --git a/plugins/cordova-plugin-splashscreen/doc/ja/README.md b/plugins/cordova-plugin-splashscreen/doc/ja/README.md new file mode 100644 index 0000000..a688b27 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/ja/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +ã“ã®ãƒ—ラグインãŒè¡¨ç¤ºã•ã‚Œã€ã‚¢ãƒ—リケーションã®èµ·å‹•ä¸­ã«ã‚¹ãƒ—ラッシュ スクリーンをéžè¡¨ç¤ºã«ã—ã¾ã™ã€‚ + +## インストール + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * ã‚¢ãƒžã‚¾ãƒ³ç« OS + * アンドロイド + * ブラックベリー 10 + * iOS + * Windows Phone 7 㨠8 + * Windows 8 + * Windows + * ブラウザー + +## メソッド + + * splashscreen.show + * splashscreen.hide + +### Android ã®ç™– + +ã‚ãªãŸã®`config.xml`内ã®æ¬¡ã®è¨­å®šã‚’追加ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + + + + + + +Foo ãŒã§ãれ㰠9 パッãƒãƒ•ã‚¡ã‚¤ãƒ« splashscreen ファイルã®åå‰ã§ã™ã€‚ 解åƒåº¦/xml ディレクトリã®é©åˆ‡ãªãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã®ä¸‹ã« splashcreen ファイルを追加ã™ã‚‹ã“ã¨ã‚’確èªã—ã¾ã™ã€‚ 2 番目ã®ãƒ‘ラメーターã¯ã€ã‚¹ãƒ—ラッシュ ・ スクリーンãŒã®è¡¨ç¤ºæ™‚é–“ (ミリ秒å˜ä½) を表ã—ã¾ã™ã€‚ デフォルトã§ã¯ 3000 ミリ秒ã§ã™ã€‚ 詳細ã«ã¤ã„ã¦ã¯ã€[アイコンã¨ã‚¹ãƒ—ラッシュ画é¢](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html) ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 + +"SplashMaintainAspectRatio"ã®è¨­å®šã¯ã‚ªãƒ—ションã§ã™ã€‚ True ã®å ´åˆã€ã‚¹ãƒ—ラッシュ画é¢æç”»ã«è¨­å®šç”»é¢ã‚’埋ã‚ã‚‹ãŸã‚ã«æ‹¡å¤§ã•ã‚Œã¾ã›ã‚“ãŒã€ä»£ã‚ã‚Šã«å˜ã«ã€Œã‚«ãƒãƒ¼ã€ç”»é¢ã§ã¯ã€CSS ã®ã‚ˆã†ãªå ´åˆã€ŒèƒŒæ™¯-サイズ: ã‚«ãƒãƒ¼ã€. ã“ã‚Œã¯ã€ãŸã¨ãˆã°é¢¨æ™¯ã¾ãŸã¯ãƒ†ã‚­ã‚¹ãƒˆãŒå«ã¾ã‚Œã¦ã„ã‚‹å ´åˆã€ä»»æ„ã®æ–¹æ³•ã§ã‚¹ãƒ—ラッシュ画é¢ç”»åƒãŒæ­ªã‚€ã“ã¨ãŒã§ããªã„éžå¸¸ã«ä¾¿åˆ©ã§ã™ã€‚ ã“ã®è¨­å®šã¯ã€ç”»é¢ã¨ç•°ãªã‚‹ç¸¦æ¨ªæ¯”ã§å®‰å…¨ã«ãƒˆãƒªãƒŸãƒ³ã‚°ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™å¤§è¦æ¨¡ãªãƒžãƒ¼ã‚¸ãƒ³ (安全ãªåœ°åŸŸ) ã®ç”»åƒã«é©ã—ã¦ã„ã¾ã™ã€‚ + +縦長ã¨æ¨ªé•·ã®ç•°ãªã‚‹ãƒ‰ãƒ­ã‚¦ã‚¢ãƒ–ルを指定ã§ãるよã†ã«ã€ãƒ—ラグインã¯å‘ãを変更ã™ã‚‹ãŸã³ã«ã‚¹ãƒ—ラッシュ ドロウアブルをリロードã—ã¾ã™ã€‚ + +### ブラウザーã®ç™– + +ã‚ãªãŸã®`config.xml`ã§æ¬¡ã®è¨­å®šã‚’使用ã§ãã¾ã™ã€‚ + + + + + + + + + + + +### iOS ã®ç™– + + * `FadeSplashScreen`(ブール値ã€æ—¢å®šã§ [ `true`): スプラッシュ画é¢ãŒãƒ•ã‚§ãƒ¼ãƒ‰ã‚¤ãƒ³ã¨ãƒ•ã‚§ãƒ¼ãƒ‰ã‚¢ã‚¦ãƒˆã®è¡¨ç¤ºçŠ¶æ…‹ãŒå¤‰æ›´ã•ã‚ŒãŸã¨ãã™ã‚‹ã“ã¨ã‚’防ããŸã‚ã«`false`ã«è¨­å®šã—ã¾ã™ã€‚ + + + + + * `FadeSplashScreenDuration`(float, デフォルトã¯`2`): スプラッシュ画é¢ã®ç§’æ•°ã®ãƒ•ã‚§ãƒ¼ãƒ‰ã‚’実行ã™ã‚‹åŠ¹æžœã‚’指定ã—ã¾ã™ã€‚ + + + + + * `ShowSplashScreenSpinner`(ブール値ã€æ—¢å®šã§ [ `true`): スプラッシュ スクリーン スピナーをéžè¡¨ç¤ºã«ã™ã‚‹ã‚’`false`ã«è¨­å®šã—ã¾ã™ã€‚ + + + + +## splashscreen.hide + +スプラッシュ スクリーンを閉ã˜ã¾ã™ã€‚ + + navigator.splashscreen.hide(); + + +### ブラックベリー 10ã€WP8ã€iOS ã®æ°—ã¾ãã‚Œ + +`config.xml` ファイル㮠`AutoHideSplashScreen` ã®è¨­å®šã¯ `false` ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ é…延を 2 秒間スプラッシュ スクリーンをéžè¡¨ç¤ºã« `deviceready` イベント ãƒãƒ³ãƒ‰ãƒ©ãƒ¼ã§ã€æ¬¡ã®ã‚ˆã†ã‚¿ã‚¤ãƒžãƒ¼ã‚’追加ã—ã¾ã™ã€‚ + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +スプラッシュ画é¢ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + + navigator.splashscreen.show(); + + +アプリãŒé–‹å§‹ã•ã‚Œã€`deviceready` イベントãŒç™ºç”Ÿã™ã‚‹ã¾ã§ã€ã‚¢ãƒ—リケーション㯠`navigator.splashscreen.show()` を呼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 ã—ã‹ã—ã€ä»¥æ¥ã€é€šå¸¸ã‚¹ãƒ—ラッシュ画é¢ã‚¢ãƒ—リ開始å‰ã«è¡¨ç¤ºã™ã‚‹ã‚‚ã®ã§ã™ã¨æ€ã‚れるã€ã‚¹ãƒ—ラッシュ スクリーンã®ç›®çš„ã®æ•—北ã—ã¾ã™ã€‚ `config.xml` ã«ã„ãã¤ã‹ã®æ§‹æˆã‚’æä¾›ã™ã‚‹ã¯è‡ªå‹•çš„ã« `表示` スプラッシュ画é¢ã€ã‚¢ãƒ—リを起動後ã™ãã«ã€ãã‚ŒãŒå®Œå…¨ã«èµ·å‹•ã—ã€`deviceready` イベントをå—ä¿¡ã™ã‚‹å‰ã«ã€‚ 詳細ã«ã¤ã„ã¦ã¯ã“ã®æ§‹æˆã‚’è¡Œã†ã«ã¯ã€[アイコンã¨ã‚¹ãƒ—ラッシュ画é¢](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html) ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 ã“ã®ç†ç”±ã®ãŸã‚ã«ã‚¢ãƒ—リ起動時ã®ã‚¹ãƒ—ラッシュ ã‚¹ã‚¯ãƒªãƒ¼ãƒ³ã‚’ç¢ºèª `navigator.splashscreen.show()` をコールã™ã‚‹å¿…è¦ãŒã‚ã‚‹å¯èƒ½æ€§ãŒé«˜ã„ã§ã™ã€‚ \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/ja/index.md b/plugins/cordova-plugin-splashscreen/doc/ja/index.md new file mode 100644 index 0000000..24e72e5 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/ja/index.md @@ -0,0 +1,78 @@ + + +# cordova-plugin-splashscreen + +ã“ã®ãƒ—ラグインãŒè¡¨ç¤ºã•ã‚Œã€ã‚¢ãƒ—リケーションã®èµ·å‹•ä¸­ã«ã‚¹ãƒ—ラッシュ スクリーンをéžè¡¨ç¤ºã«ã—ã¾ã™ã€‚ + +## インストール + + cordova plugin add cordova-plugin-splashscreen + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* ã‚¢ãƒžã‚¾ãƒ³ç« OS +* アンドロイド +* ブラックベリー 10 +* iOS +* Windows Phone 7 㨠8 +* Windows 8 + +## メソッド + +* splashscreen.show +* splashscreen.hide + +### Android ã®ç™– + +ã‚ãªãŸã® config.xml を以下ã®è¨­å®šã‚’追加ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + + + + + +Foo ãŒã§ãれ㰠9 パッãƒãƒ•ã‚¡ã‚¤ãƒ« splashscreen ファイルã®åå‰ã§ã™ã€‚ 解åƒåº¦/xml ディレクトリã®é©åˆ‡ãªãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã®ä¸‹ã« splashcreen ファイルを追加ã™ã‚‹ã“ã¨ã‚’確èªã—ã¾ã™ã€‚ 2 番目ã®ãƒ‘ラメーターã¯ã€ã‚¹ãƒ—ラッシュ ・ スクリーンãŒã®è¡¨ç¤ºæ™‚é–“ (ミリ秒å˜ä½) を表ã—ã¾ã™ã€‚ デフォルトã§ã¯ 3000 ミリ秒ã§ã™ã€‚ 詳細ã«ã¤ã„ã¦ã¯ã€[アイコンã¨ã‚¹ãƒ—ラッシュ画é¢][1] ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.hide + +スプラッシュ スクリーンを閉ã˜ã¾ã™ã€‚ + + navigator.splashscreen.hide(); + + +### ブラックベリー 10ã€WP8ã€iOS ã®æ°—ã¾ãã‚Œ + +`config.xml` ファイル㮠`AutoHideSplashScreen` ã®è¨­å®šã¯ `false` ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ é…延を 2 秒間スプラッシュ スクリーンをéžè¡¨ç¤ºã« `deviceready` イベント ãƒãƒ³ãƒ‰ãƒ©ãƒ¼ã§ã€æ¬¡ã®ã‚ˆã†ã‚¿ã‚¤ãƒžãƒ¼ã‚’追加ã—ã¾ã™ã€‚ + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +スプラッシュ画é¢ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + + navigator.splashscreen.show(); + + +アプリãŒé–‹å§‹ã•ã‚Œã€`deviceready` イベントãŒç™ºç”Ÿã™ã‚‹ã¾ã§ã€ã‚¢ãƒ—リケーション㯠`navigator.splashscreen.show()` を呼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 ã—ã‹ã—ã€ä»¥æ¥ã€é€šå¸¸ã‚¹ãƒ—ラッシュ画é¢ã‚¢ãƒ—リ開始å‰ã«è¡¨ç¤ºã™ã‚‹ã‚‚ã®ã§ã™ã¨æ€ã‚れるã€ã‚¹ãƒ—ラッシュ スクリーンã®ç›®çš„ã®æ•—北ã—ã¾ã™ã€‚ `config.xml` ã«ã„ãã¤ã‹ã®æ§‹æˆã‚’æä¾›ã™ã‚‹ã¯è‡ªå‹•çš„ã« `表示` スプラッシュ画é¢ã€ã‚¢ãƒ—リを起動後ã™ãã«ã€ãã‚ŒãŒå®Œå…¨ã«èµ·å‹•ã—ã€`deviceready` イベントをå—ä¿¡ã™ã‚‹å‰ã«ã€‚ 詳細ã«ã¤ã„ã¦ã¯ã“ã®æ§‹æˆã‚’è¡Œã†ã«ã¯ã€[アイコンã¨ã‚¹ãƒ—ラッシュ画é¢][1] ã‚’å‚ç…§ã—ã¦ãã ã•ã„。 ã“ã®ç†ç”±ã®ãŸã‚ã«ã‚¢ãƒ—リ起動時ã®ã‚¹ãƒ—ラッシュ ã‚¹ã‚¯ãƒªãƒ¼ãƒ³ã‚’ç¢ºèª `navigator.splashscreen.show()` をコールã™ã‚‹å¿…è¦ãŒã‚ã‚‹å¯èƒ½æ€§ãŒé«˜ã„ã§ã™ã€‚ diff --git a/plugins/cordova-plugin-splashscreen/doc/ko/README.md b/plugins/cordova-plugin-splashscreen/doc/ko/README.md new file mode 100644 index 0000000..5e10d20 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/ko/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +ì´ í”ŒëŸ¬ê·¸ì¸ì€ 표시 하 ê³  ì‘ìš© 프로그램 실행 하는 ë™ì•ˆ 시작 í™”ë©´ì„ ìˆ¨ê¹ë‹ˆë‹¤. + +## 설치 + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * 아마존 화재 ìš´ì˜ ì²´ì œ + * 안 ë“œ ë¡œì´ë“œ + * 블랙베리 10 + * iOS + * Windows Phone 7ê³¼ 8 + * 윈ë„ìš° 8 + * 윈ë„ìš° + * 브ë¼ìš°ì € + +## 메서드 + + * splashscreen.show + * splashscreen.hide + +### 안 ë“œ ë¡œì´ë“œ ë‹¨ì  + +`Config.xml`ì— ë‹¤ìŒ í™˜ê²½ ì„¤ì •ì— ì¶”ê°€ 해야 합니다. + + + + + + +여기서 foo splashscreen 파ì¼, 선호 9 패치 파ì¼ì˜ ì´ë¦„입니다. ì ì ˆ í•œ í´ë” 아래 res/xml ë””ë ‰í† ë¦¬ì— splashcreen 파ì¼ì„ 추가 해야 합니다. ë‘ ë²ˆì§¸ 매개 변수는 splashscreen 얼마나 밀리초 단위로 표시 ë©ë‹ˆë‹¤ 나타냅니다. 3000 ms 기본값으로 사용 ë©ë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ [ì•„ì´ì½˜ ë° ì‹œìž‘ 화면ì„](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html) 참조 하십시오. + +"SplashMaintainAspectRatio" ì·¨í–¥ì€ ì„ íƒ ì‚¬í•­ìž…ë‹ˆë‹¤. Drawable, 시작 화면 설정 í™”ë©´ì— ë§žê²Œ 확장 ë˜ì§€ 하지만 대신 단순히 "커버" CSS ê°™ì€ í™”ë©´ "ë°°ê²½-í¬ê¸°: ë®ê°œ". 시작 화면 ì´ë¯¸ì§€ 예: í’ê²½ ë˜ëŠ” í…스트를 í¬í•¨ 하는 경우 ì–´ë–¤ ì‹ìœ¼ë¡œë“ ì—ì„œ 왜곡 ë  ìˆ˜ 없는 ê²½ìš°ì— ë§¤ìš° 유용 합니다. ì´ ì„¤ì •ì€ í° ì—¬ë°± (안전 지역) 안전 하 게 다른 종횡비와 í™”ë©´ì— ìžë¥¼ 수 있는 ì´ë¯¸ì§€ì— 가장 ì í•© 합니다. + +í”ŒëŸ¬ê·¸ì¸ ë‹¤ì‹œ 로드 스플래시 drawable ë°©í–¥ì´ ë³€ê²½ ë  ë•Œë§ˆë‹¤ 세로 ë° ê°€ë¡œ ë°©í–¥ì— ëŒ€ í•œ 다른 drawables를 지정할 수 있ë„ë¡ í•©ë‹ˆë‹¤. + +### 브ë¼ìš°ì € 만지면 + +`Config.xml`ì— ë‹¤ìŒ ê¸°ë³¸ ì„¤ì •ì„ ì‚¬ìš©í•  수 있습니다. + + + + + + + + + + + +### iOS ë‹¨ì  + + * `FadeSplashScreen` (부울 `true`ë¡œ 기본값): 시작 화면 표시 ìƒíƒœë¡œ 변경 ë  ë•Œ 밖으로 퇴색 하지 ì•Šë„ë¡ í•˜ë ¤ë©´ `false` ë¡œ 설정. + + + + + * `FadeSplashScreenDuration` (부ë™, `2`기본값): 시작 í™”ë©´ì— ëŒ€ í•œ ì´ˆ 페ì´ë“œ 효과를 실행 하는 지정 합니다. + + + + + * `ShowSplashScreenSpinner` (부울 `true`ë¡œ 기본값): 스플래시 화면 회전ìžë¥¼ 숨기려면 `false` ë¡œ 설정. + + + + +## splashscreen.hide + +시작 í™”ë©´ì„ ë‹«ìŠµë‹ˆë‹¤. + + navigator.splashscreen.hide(); + + +### 블랙베리 10, WP8, iOS 특질 + +`config.xml` 파ì¼ì˜ `AutoHideSplashScreen` ì„¤ì •ì„ `false` 여야 합니다. 2 ì´ˆ ë™ì•ˆ 시작 í™”ë©´ì„ ìˆ¨ê¸°ê³  지연, `deviceready` ì´ë²¤íŠ¸ 처리기ì—ì„œ 다ìŒê³¼ ê°™ì€ íƒ€ì´ë¨¸ë¥¼ 추가: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +시작 í™”ë©´ì„ í‘œì‹œí•©ë‹ˆë‹¤. + + navigator.splashscreen.show(); + + +ì‘ìš© 프로그램 시작 ë° `deviceready` ì´ë²¤íŠ¸ëŠ” ë°œìƒ ë  ë•Œê¹Œì§€ ì‘ìš© í”„ë¡œê·¸ëž¨ì´ `navigator.splashscreen.show()`ì„ í˜¸ì¶œí•  수 없습니다. 하지만 ê·¸ 스플래시 스í¬ë¦°ì˜ ëª©ì  ê²ƒ 같다 ì¼ë°˜ì ìœ¼ë¡œ 시작 í™”ë©´ì´ ë‹¹ì‹ ì˜ ì• í”Œ 리 ì¼€ì´ ì…˜ 시작 하기 ì „ì— í‘œì‹œ ë  ìš´ëª…ì´ ë‹¤, ì´í›„. `config.xmlì—ì„œ` 몇 가지 êµ¬ì„±ì„ ì œê³µ 하 ìžë™ìœ¼ë¡œ 스플래시 `표시` 화면 애플 리 ì¼€ì´ ì…˜ 출시 ì§í›„와 ê·¸ê²ƒì€ ì™„ë²½ 하 게 시작 하 ê³  `deviceready` ì´ë²¤íŠ¸ë¥¼ ë°›ì€ ì „ì—. ì´ êµ¬ì„± 하 ê³  ìžì„¸í•œ ë‚´ìš©ì€ [ì•„ì´ì½˜ ë° ì‹œìž‘ 화면ì„](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html) 참조 하십시오. ì´ëŸ¬í•œ ì´ìœ ë¡œ, ê·¸ê²ƒì€ ê°€ëŠ¥ì„±ì´ ì‹œìž‘ í™”ë©´ì€ ì‘ìš© 프로그램 ì‹œìž‘ì— ëŒ€ í•œ 표시 ë˜ë„ë¡ `navigator.splashscreen.show()`를 호출 해야입니다. \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/ko/index.md b/plugins/cordova-plugin-splashscreen/doc/ko/index.md new file mode 100644 index 0000000..6a0ea98 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/ko/index.md @@ -0,0 +1,78 @@ + + +# cordova-plugin-splashscreen + +ì´ í”ŒëŸ¬ê·¸ì¸ì€ 표시 하 ê³  ì‘ìš© 프로그램 실행 하는 ë™ì•ˆ 시작 í™”ë©´ì„ ìˆ¨ê¹ë‹ˆë‹¤. + +## 설치 + + cordova plugin add cordova-plugin-splashscreen + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* 아마존 화재 ìš´ì˜ ì²´ì œ +* 안 ë“œ ë¡œì´ë“œ +* 블랙베리 10 +* iOS +* Windows Phone 7ê³¼ 8 +* 윈ë„ìš° 8 + +## 메서드 + +* splashscreen.show +* splashscreen.hide + +### 안 ë“œ ë¡œì´ë“œ ë‹¨ì  + +ë‹¹ì‹ ì˜ config.xmlì— ë‹¤ìŒ í™˜ê²½ ì„¤ì •ì— ì¶”ê°€ 해야 합니다. + + + + + +여기서 foo splashscreen 파ì¼, 선호 9 패치 파ì¼ì˜ ì´ë¦„입니다. ì ì ˆ í•œ í´ë” 아래 res/xml ë””ë ‰í† ë¦¬ì— splashcreen 파ì¼ì„ 추가 해야 합니다. ë‘ ë²ˆì§¸ 매개 변수는 splashscreen 얼마나 밀리초 단위로 표시 ë©ë‹ˆë‹¤ 나타냅니다. 3000 ms 기본값으로 사용 ë©ë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ [ì•„ì´ì½˜ ë° ì‹œìž‘ 화면ì„][1] 참조 하십시오. + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.hide + +시작 í™”ë©´ì„ ë‹«ìŠµë‹ˆë‹¤. + + navigator.splashscreen.hide(); + + +### 블랙베리 10, WP8, iOS 특질 + +`config.xml` 파ì¼ì˜ `AutoHideSplashScreen` ì„¤ì •ì„ `false` 여야 합니다. 2 ì´ˆ ë™ì•ˆ 시작 í™”ë©´ì„ ìˆ¨ê¸°ê³  지연, `deviceready` ì´ë²¤íŠ¸ 처리기ì—ì„œ 다ìŒê³¼ ê°™ì€ íƒ€ì´ë¨¸ë¥¼ 추가: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +시작 í™”ë©´ì„ í‘œì‹œí•©ë‹ˆë‹¤. + + navigator.splashscreen.show(); + + +ì‘ìš© 프로그램 시작 ë° `deviceready` ì´ë²¤íŠ¸ëŠ” ë°œìƒ ë  ë•Œê¹Œì§€ ì‘ìš© í”„ë¡œê·¸ëž¨ì´ `navigator.splashscreen.show()`ì„ í˜¸ì¶œí•  수 없습니다. 하지만 ê·¸ 스플래시 스í¬ë¦°ì˜ ëª©ì  ê²ƒ 같다 ì¼ë°˜ì ìœ¼ë¡œ 시작 í™”ë©´ì´ ë‹¹ì‹ ì˜ ì• í”Œ 리 ì¼€ì´ ì…˜ 시작 하기 ì „ì— í‘œì‹œ ë  ìš´ëª…ì´ ë‹¤, ì´í›„. `config.xmlì—ì„œ` 몇 가지 êµ¬ì„±ì„ ì œê³µ 하 ìžë™ìœ¼ë¡œ 스플래시 `표시` 화면 애플 리 ì¼€ì´ ì…˜ 출시 ì§í›„와 ê·¸ê²ƒì€ ì™„ë²½ 하 게 시작 하 ê³  `deviceready` ì´ë²¤íŠ¸ë¥¼ ë°›ì€ ì „ì—. ì´ êµ¬ì„± 하 ê³  ìžì„¸í•œ ë‚´ìš©ì€ [ì•„ì´ì½˜ ë° ì‹œìž‘ 화면ì„][1] 참조 하십시오. ì´ëŸ¬í•œ ì´ìœ ë¡œ, ê·¸ê²ƒì€ ê°€ëŠ¥ì„±ì´ ì‹œìž‘ í™”ë©´ì€ ì‘ìš© 프로그램 ì‹œìž‘ì— ëŒ€ í•œ 표시 ë˜ë„ë¡ `navigator.splashscreen.show()`를 호출 해야입니다. diff --git a/plugins/cordova-plugin-splashscreen/doc/pl/README.md b/plugins/cordova-plugin-splashscreen/doc/pl/README.md new file mode 100644 index 0000000..0156be5 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/pl/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +Ten plugin wyÅ›wietla i ukrywa ekran powitalny podczas uruchamiania aplikacji. + +## Instalacja + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## ObsÅ‚ugiwane platformy + + * Amazon Fire OS + * Android + * BlackBerry 10 + * iOS + * Windows Phone 7 i 8 + * Windows 8 + * Windows + * PrzeglÄ…darka + +## Metody + + * splashscreen.show + * splashscreen.Hide + +### Dziwactwa Androida + +W pliku `config.xml`musisz dodać nastÄ™pujÄ…ce preferencje: + + + + + + +Gdzie foo jest nazwÄ… pliku ekranu powitalnego, najlepiej 9 Å‚atce. Upewnij siÄ™ dodać pliki splashcreen do katalogu res/xml w odpowiednich folderach. Drugi parametr reprezentuje, jak dÅ‚ugo ekranu powitalnego pojawi siÄ™ w milisekundach. DomyÅ›lnie 3000 ms. Aby uzyskać wiÄ™cej informacji, zobacz [ikony i ekrany powitalne w aplikacjach](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html). + +"SplashMaintainAspectRatio" preferencji jest opcjonalne. JeÅ›li zestaw na wartość true, ekran powitalny dolarowe nie jest rozciÄ…gniÄ™ty do ekranów, ale zamiast po prostu "obejmuje" ekranu, jak CSS "tÅ‚o-rozmiar: okÅ‚adka". Jest to bardzo przydatne, kiedy opryskać tÄ™cza obrazy nie znieksztaÅ‚cony w jakikolwiek sposób, na przykÅ‚ad, gdy zawierajÄ… one dekoracje lub tekst. To ustawienie dziaÅ‚a najlepiej z obrazów, które majÄ… duże marginesy (bezpiecznych obszarów), które mogÄ… być bezpiecznie przyciÄ™te na ekrany z różnych proporcji. + +Plugin Å‚aduje rozchlapać dolarowe, gdy zmienia orientacjÄ™, tak można okreÅ›lić różnych drawables do orientacji pionowej i poziomej. + +### Quirks przeglÄ…darki + +W pliku `config.xml`można użyć nastÄ™pujÄ…ce preferencje: + + + + + + + + + + + +### Dziwactwa iOS + + * `FadeSplashScreen` (wartość logiczna, domyÅ›lnie `true`): zestaw na `false` , aby zapobiec ZnikajÄ…ca i odkÅ‚adane po zmianie stanu wyÅ›wietlania ekranu powitalnego. + + + + + * `FadeSplashScreenDuration` (float, domyÅ›lnie `2`): okreÅ›la liczbÄ™ sekund dla ekranu powitalnego zanikanie efekt do wykonać. + + + + + * `ShowSplashScreenSpinner` (wartość logiczna, domyÅ›lnie `true`): zestaw na `false` , aby ukryć pokrÄ™tÅ‚a ekran powitalny. + + + + +## splashscreen.Hide + +Odrzucić ten opryskaæ têcza. + + navigator.splashscreen.hide(); + + +### Jeżyna 10, WP8, iOS dziwactwo + +Plik `config.xml` `AutoHideSplashScreen` ustawienie musi być `false`. Opóźnienia, ukrywanie ekranu powitalnego przez dwie sekundy, dodać timer nastÄ™pujÄ…cych w `deviceready` obsÅ‚uga zdarzeÅ„: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +WyÅ›wietla ekran powitalny. + + navigator.splashscreen.show(); + + +Aplikacja nie można wywoÅ‚ać `navigator.splashscreen.show()`, aż aplikacja zostaÅ‚a uruchomiona i zdarzenie `deviceready` zostaÅ‚ zwolniony. Ale ponieważ zazwyczaj opryskać tÄ™cza ma być widoczne przed rozpoczÄ™ciem aplikacji, wydaje siÄ™ sprzeczne z celem ekranu powitalnego. Dostarczanie niektórych konfiguracji w `pliku config.xml` bÄ™dzie automatycznie `show` splash na ekranie natychmiast po uruchomienie aplikacji i przed peÅ‚ni rozpoczÄ…Å‚ i odebraÅ‚ zdarzenie `deviceready`. Aby uzyskać wiÄ™cej informacji na robienie tej konfiguracji, zobacz [ikony i ekrany powitalne w aplikacjach](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html). Z tego powodu jest maÅ‚o prawdopodobne, należy zadzwonić `navigator.splashscreen.show()`, aby wyÅ›wietlić ekran powitalny dla uruchamiania aplikacji. \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/pl/index.md b/plugins/cordova-plugin-splashscreen/doc/pl/index.md new file mode 100644 index 0000000..33045cb --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/pl/index.md @@ -0,0 +1,78 @@ + + +# cordova-plugin-splashscreen + +Ten plugin wyÅ›wietla i ukrywa ekran powitalny podczas uruchamiania aplikacji. + +## Instalacja + + cordova plugin add cordova-plugin-splashscreen + + +## ObsÅ‚ugiwane platformy + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Windows Phone 7 i 8 +* Windows 8 + +## Metody + +* splashscreen.show +* splashscreen.Hide + +### Dziwactwa Androida + +W pliku config.xml musisz dodać nastÄ™pujÄ…ce preferencje: + + + + + +Gdzie foo jest nazwÄ… pliku ekranu powitalnego, najlepiej 9 Å‚atce. Upewnij siÄ™ dodać pliki splashcreen do katalogu res/xml w odpowiednich folderach. Drugi parametr reprezentuje, jak dÅ‚ugo ekranu powitalnego pojawi siÄ™ w milisekundach. DomyÅ›lnie 3000 ms. Aby uzyskać wiÄ™cej informacji, zobacz [ikony i ekrany powitalne w aplikacjach][1]. + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.Hide + +Odrzucić ten opryskaæ têcza. + + navigator.splashscreen.hide(); + + +### Jeżyna 10, WP8, iOS dziwactwo + +Plik `config.xml` `AutoHideSplashScreen` ustawienie musi być `false`. Opóźnienia, ukrywanie ekranu powitalnego przez dwie sekundy, dodać timer nastÄ™pujÄ…cych w `deviceready` obsÅ‚uga zdarzeÅ„: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +WyÅ›wietla ekran powitalny. + + navigator.splashscreen.show(); + + +Aplikacja nie można wywoÅ‚ać `navigator.splashscreen.show()`, aż aplikacja zostaÅ‚a uruchomiona i zdarzenie `deviceready` zostaÅ‚ zwolniony. Ale ponieważ zazwyczaj opryskać tÄ™cza ma być widoczne przed rozpoczÄ™ciem aplikacji, wydaje siÄ™ sprzeczne z celem ekranu powitalnego. Dostarczanie niektórych konfiguracji w `pliku config.xml` bÄ™dzie automatycznie `show` splash na ekranie natychmiast po uruchomienie aplikacji i przed peÅ‚ni rozpoczÄ…Å‚ i odebraÅ‚ zdarzenie `deviceready`. Aby uzyskać wiÄ™cej informacji na robienie tej konfiguracji, zobacz [ikony i ekrany powitalne w aplikacjach][1]. Z tego powodu jest maÅ‚o prawdopodobne, należy zadzwonić `navigator.splashscreen.show()`, aby wyÅ›wietlić ekran powitalny dla uruchamiania aplikacji. diff --git a/plugins/cordova-plugin-splashscreen/doc/ru/index.md b/plugins/cordova-plugin-splashscreen/doc/ru/index.md new file mode 100644 index 0000000..635c22d --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/ru/index.md @@ -0,0 +1,75 @@ + + +# cordova-plugin-splashscreen + +Этот плагин отображает и Ñкрывает Ñкран-заÑтавку при запуÑке приложениÑ. + +## УÑтановка + + cordova plugin add cordova-plugin-splashscreen + + +## Поддерживаемые платформы + +* Amazon Fire OS +* Android +* BlackBerry 10 +* iOS +* Windows Phone 7 и 8 +* Windows 8 + +## Методы + +* splashscreen.show +* splashscreen.hide + +### ОÑобенноÑти Android + +Ð’ вашем файле config.xml необходимо добавить Ñледующие наÑтройки: + +`` `` + +Где foo Ñто Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° splashscreen, желательно 9 заплатку. УбедитеÑÑŒ в том добавить ваши splashcreen файлы в папку res/xml в ÑоответÑтвующие папки. Второй параметр предÑтавлÑет, как долго splashscreen поÑвитÑÑ Ð² миллиÑекундах. По умолчанию он 3000 МС. Увидеть [иконки и заÑтавки][1] Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации. + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.hide + +Закройте Ñкран-заÑтавка. + + Navigator.SplashScreen.Hide(); + + +### ОÑобенноÑти BlackBerry 10, WP8, iOS + +`config.xml`Файла `AutoHideSplashScreen` должен быть `false` . Ð”Ð»Ñ Ð·Ð°Ð´ÐµÑ€Ð¶ÐºÐ¸ ÑÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ð·Ð°Ñтавки на две Ñекунды, добавить таймер, например в `deviceready` обработчик Ñобытий: + + setTimeout(function() {navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +Отображает Ñкран-заÑтавку. + + Navigator.SplashScreen.Show(); + + +Ваше приложение не может вызвать `navigator.splashscreen.show()` до тех пор, пока приложение началаÑÑŒ и `deviceready` Ñобытие инициировано. Ðо поÑкольку обычно Ñкран-заÑтавка должен быть видимым до начала вашего приложениÑ, что казалоÑÑŒ бы поражение цели Ñкрана-заÑтавки. ПредоÑтавление некоторых конфигурации в `config.xml` будет автоматичеÑки `show` Ñкран-заÑтавку Ñразу же поÑле запуÑка вашего Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸ перед его полноÑтью запущен и получил `deviceready` Ñобытие. Увидеть [иконки и заÑтавки][1] Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации на делать Ñтой конфигурации. По Ñтой причине маловероÑтно, вам нужно вызвать `navigator.splashscreen.show()` Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñкрана-заÑтавки Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка приложениÑ. diff --git a/plugins/cordova-plugin-splashscreen/doc/zh/README.md b/plugins/cordova-plugin-splashscreen/doc/zh/README.md new file mode 100644 index 0000000..da37405 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/zh/README.md @@ -0,0 +1,119 @@ + + +# cordova-plugin-splashscreen + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-splashscreen.svg)](https://travis-ci.org/apache/cordova-plugin-splashscreen) + +這個外掛程å¼é¡¯ç¤ºå’Œéš±è—在應用程å¼å•Ÿå‹•æœŸé–“çš„åˆå§‹èž¢å¹•ã€‚ + +## å®‰è£ + + // npm hosted (new) id + cordova plugin add cordova-plugin-splashscreen + // you may also install directly from this repo + cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git + + +## 支æ´çš„平臺 + + * 亞馬éœç« OS + * Android 系統 + * 黑莓 10 + * iOS + * Windows Phone 7 å’Œ 8 + * Windows 8 + * Windows + * ç€è¦½å™¨ + +## 方法 + + * splashscreen.show + * splashscreen.hide + +### Android 的怪癖 + +在你的`config.xml`,您需è¦æ·»åŠ ä»¥ä¸‹å„ªæƒ : + + + + + + +美孚在哪裡閃å±æª”,最好是 9 修補程å¼æª”çš„å稱。 請確ä¿æ‚¨çš„ splashcreen 檔添加到 res/xml 目錄下相應的資料夾。 第二個åƒæ•¸è¡¨ç¤ºå¤šä¹…é–ƒå±æœƒé¡¯ç¤ºä»¥æ¯«ç§’為單ä½ã€‚ 它將é è¨­ç‚º 3000 毫秒。 有關更多資訊,請åƒè¦‹ [圖示和啟動畫é¢](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html)。 + +"SplashMaintainAspectRatio"首é¸é …是å¯é¸çš„。 如果設置為 true,å¯ç¹ªè£½çš„åˆå§‹èž¢å¹•ä¸æœƒæ‹‰ä¼¸ä»¥é©åˆèž¢å¹•ï¼Œä½†ç›¸ååªæ˜¯"覆蓋"èž¢å¹•ï¼Œåƒ CSS"背景-大å°: è“‹"。 這是éžå¸¸æœ‰ç”¨çš„ä¸èƒ½ä»¥ä»»ä½•æ–¹å¼ï¼Œä¾‹å¦‚當他們包å«æ–‡æœ¬æˆ–風景畸變閃å±åœ–åƒæ™‚。 此設置é©ç”¨äºŽæœ‰å¤§åˆ©æ½¤ (安全å€),å¯ä»¥å®‰å…¨åœ°è£å‰ªä¸åŒé•·å¯¬æ¯”與螢幕上的圖åƒã€‚ + +該外掛程å¼é‡æ–°è¼‰å…¥åˆå§‹å¯ç¹ªè£½åªè¦æ–¹å‘發生變化,所以您å¯ä»¥æŒ‡å®šä¸åŒçš„ç•«æ¿ç‚ºç¸±å‘和橫å‘æ–¹å‘。 + +### ç€è¦½å™¨çš„怪癖 + +ä½ å¯ä»¥ç”¨ä½ çš„`config.xml`下列優先é¸é …: + + + + + + + + + + + +### iOS 的怪癖 + + * `FadeSplashScreen`(é è¨­ç‚º`true`的布林值): 設置為`false` ,以防止出ç¾é–ƒå±è¡°è½å’Œé€€å‡ºå…¶é¡¯ç¤ºç‹€æ…‹ç™¼ç”Ÿè®ŠåŒ–時。 + + + + + * `FadeSplashScreenDuration`(float,é è¨­ç‚º`2`): 指定的閃å±ç§’數淡出效果來執行。 + + + + + * `ShowSplashScreenSpinner`(boolean, `true`的布林值): 設置為`false`來隱è—åˆå§‹èž¢å¹•å¾®èª¿æ¡†ã€‚ + + + + +## splashscreen.hide + +解雇的閃å±ã€‚ + + navigator.splashscreen.hide(); + + +### 黑莓 10,WP8,iOS 怪癖 + +`config.xml` 檔 `AutoHideSplashScreen` 設置必須是 `å‡` 的。 è‹¥è¦å»¶é²å…©ç§’é˜éš±è—çš„é–ƒå±ï¼Œ`deviceready` 事件處ç†å¸¸å¼ä¸­æ·»åŠ ä¸€å€‹è¨ˆæ™‚器,如下所示: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +顯示åˆå§‹èž¢å¹•ã€‚ + + navigator.splashscreen.show(); + + +您的應用程å¼ç„¡æ³•èª¿ç”¨ `navigator.splashscreen.show()`,直到該應用程å¼å·²å•Ÿå‹•ï¼Œä¸”觸發了 `deviceready` 事件。 但是,由於通常的閃å±ç‚ºäº†æ˜¯å¯è¦‹çš„在您的應用程å¼å•Ÿå‹•ä¹‹å‰ï¼Œé€™ä¼¼ä¹Žæœƒæ‰“æ•—é–ƒå±çš„目的。 æ供一些é…置在 `config.xml` 中的會自動 `show` åˆå§‹èž¢å¹•æ‚¨çš„應用程å¼å•Ÿå‹•å¾Œç«‹å³å’Œä¹‹å‰å®ƒå·²ç¶“完全起步並收到 `deviceready` 事件。 åšé€™ç¨®é…置的詳細資訊,請åƒé–± [圖示和啟動畫é¢](http://cordova.apache.org/docs/en/edge/config_ref_images.md.html)。 出於此原因,ä¸å¤ªå¯èƒ½æ‚¨éœ€è¦èª¿ç”¨ `navigator.splashscreen.show()`,使åˆå§‹èž¢å¹•å¯è¦‹ç‚ºæ‡‰ç”¨ç¨‹å¼å•Ÿå‹•ã€‚ \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/doc/zh/index.md b/plugins/cordova-plugin-splashscreen/doc/zh/index.md new file mode 100644 index 0000000..efb309d --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/doc/zh/index.md @@ -0,0 +1,78 @@ + + +# cordova-plugin-splashscreen + +這個外掛程å¼é¡¯ç¤ºå’Œéš±è—在應用程å¼å•Ÿå‹•æœŸé–“çš„åˆå§‹èž¢å¹•ã€‚ + +## å®‰è£ + + cordova plugin add cordova-plugin-splashscreen + + +## 支æ´çš„平臺 + +* 亞馬éœç« OS +* Android 系統 +* 黑莓 10 +* iOS +* Windows Phone 7 å’Œ 8 +* Windows 8 + +## 方法 + +* splashscreen.show +* splashscreen.hide + +### Android 的怪癖 + +在你的 config.xml,您需è¦æ·»åŠ ä»¥ä¸‹å„ªæƒ ï¼š + + + + + +美孚在哪裡閃å±æª”,最好是 9 修補程å¼æª”çš„å稱。 請確ä¿æ‚¨çš„ splashcreen 檔添加到 res/xml 目錄下相應的資料夾。 第二個åƒæ•¸è¡¨ç¤ºå¤šä¹…é–ƒå±æœƒé¡¯ç¤ºä»¥æ¯«ç§’為單ä½ã€‚ 它將é è¨­ç‚º 3000 毫秒。 有關更多資訊,請åƒè¦‹ [圖示和啟動畫é¢][1]。 + + [1]: http://cordova.apache.org/docs/en/edge/config_ref_images.md.html + +## splashscreen.hide + +解雇的閃å±ã€‚ + + navigator.splashscreen.hide(); + + +### 黑莓 10,WP8,iOS 怪癖 + +`config.xml` 檔 `AutoHideSplashScreen` 設置必須是 `å‡` 的。 è‹¥è¦å»¶é²å…©ç§’é˜éš±è—çš„é–ƒå±ï¼Œ`deviceready` 事件處ç†å¸¸å¼ä¸­æ·»åŠ ä¸€å€‹è¨ˆæ™‚器,如下所示: + + setTimeout(function() { + navigator.splashscreen.hide(); + }, 2000); + + +## splashscreen.show + +顯示åˆå§‹èž¢å¹•ã€‚ + + navigator.splashscreen.show(); + + +您的應用程å¼ç„¡æ³•èª¿ç”¨ `navigator.splashscreen.show()`,直到該應用程å¼å·²å•Ÿå‹•ï¼Œä¸”觸發了 `deviceready` 事件。 但是,由於通常的閃å±ç‚ºäº†æ˜¯å¯è¦‹çš„在您的應用程å¼å•Ÿå‹•ä¹‹å‰ï¼Œé€™ä¼¼ä¹Žæœƒæ‰“æ•—é–ƒå±çš„目的。 æ供一些é…置在 `config.xml` 中的會自動 `show` åˆå§‹èž¢å¹•æ‚¨çš„應用程å¼å•Ÿå‹•å¾Œç«‹å³å’Œä¹‹å‰å®ƒå·²ç¶“完全起步並收到 `deviceready` 事件。 åšé€™ç¨®é…置的詳細資訊,請åƒé–± [圖示和啟動畫é¢][1]。 出於此原因,ä¸å¤ªå¯èƒ½æ‚¨éœ€è¦èª¿ç”¨ `navigator.splashscreen.show()`,使åˆå§‹èž¢å¹•å¯è¦‹ç‚ºæ‡‰ç”¨ç¨‹å¼å•Ÿå‹•ã€‚ diff --git a/plugins/cordova-plugin-splashscreen/package.json b/plugins/cordova-plugin-splashscreen/package.json new file mode 100644 index 0000000..d65c3e0 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/package.json @@ -0,0 +1,83 @@ +{ + "_from": "cordova-plugin-splashscreen@^5.0.2", + "_id": "cordova-plugin-splashscreen@5.0.2", + "_inBundle": false, + "_integrity": "sha1-dH509W4gHNWFvGLRS8oZ9oZ/8e0=", + "_location": "/cordova-plugin-splashscreen", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "cordova-plugin-splashscreen@^5.0.2", + "name": "cordova-plugin-splashscreen", + "escapedName": "cordova-plugin-splashscreen", + "rawSpec": "^5.0.2", + "saveSpec": null, + "fetchSpec": "^5.0.2" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-5.0.2.tgz", + "_shasum": "747e74f56e201cd585bc62d14bca19f6867ff1ed", + "_spec": "cordova-plugin-splashscreen@^5.0.2", + "_where": "/home/thrrgilag/workspace/cordova/Goober", + "author": { + "name": "Apache Software Foundation" + }, + "bugs": { + "url": "https://issues.apache.org/jira/browse/CB" + }, + "bundleDependencies": false, + "cordova": { + "id": "cordova-plugin-splashscreen", + "platforms": [ + "android", + "ubuntu", + "ios", + "windows", + "browser" + ] + }, + "deprecated": false, + "description": "Cordova Splashscreen Plugin", + "devDependencies": { + "jshint": "^2.6.0" + }, + "engines": { + "cordovaDependencies": { + "2.0.0": { + "cordova-android": ">=3.6.0" + }, + "4.0.0": { + "cordova-android": ">=3.6.0", + "cordova-windows": ">=4.4.0" + }, + "6.0.0": { + "cordova": ">100" + } + } + }, + "homepage": "https://github.com/apache/cordova-plugin-splashscreen#readme", + "keywords": [ + "cordova", + "splashscreen", + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "cordova-windows" + ], + "license": "Apache-2.0", + "name": "cordova-plugin-splashscreen", + "repository": { + "type": "git", + "url": "git+https://github.com/apache/cordova-plugin-splashscreen.git" + }, + "scripts": { + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests", + "test": "npm run jshint" + }, + "types": "./types/index.d.ts", + "version": "5.0.2" +} diff --git a/plugins/cordova-plugin-splashscreen/plugin.xml b/plugins/cordova-plugin-splashscreen/plugin.xml new file mode 100644 index 0000000..bf3c8a4 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/plugin.xml @@ -0,0 +1,82 @@ + + + + + Splashscreen + Cordova Splashscreen Plugin + Apache 2.0 + cordova,splashscreen + https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git + https://issues.apache.org/jira/browse/CB/component/12320653 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-splashscreen/src/android/SplashScreen.java b/plugins/cordova-plugin-splashscreen/src/android/SplashScreen.java new file mode 100644 index 0000000..6f56c6c --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/android/SplashScreen.java @@ -0,0 +1,413 @@ +/* + 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.splashscreen; + +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Configuration; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Handler; +import android.view.Display; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.AlphaAnimation; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; + +public class SplashScreen extends CordovaPlugin { + private static final String LOG_TAG = "SplashScreen"; + // Cordova 3.x.x has a copy of this plugin bundled with it (SplashScreenInternal.java). + // Enable functionality only if running on 4.x.x. + private static final boolean HAS_BUILT_IN_SPLASH_SCREEN = Integer.valueOf(CordovaWebView.CORDOVA_VERSION.split("\\.")[0]) < 4; + private static final int DEFAULT_SPLASHSCREEN_DURATION = 3000; + private static final int DEFAULT_FADE_DURATION = 500; + private static Dialog splashDialog; + private static ProgressDialog spinnerDialog; + private static boolean firstShow = true; + private static boolean lastHideAfterDelay; // https://issues.apache.org/jira/browse/CB-9094 + + /** + * Displays the splash drawable. + */ + private ImageView splashImageView; + + /** + * Remember last device orientation to detect orientation changes. + */ + private int orientation; + + // Helper to be compile-time compatible with both Cordova 3.x and 4.x. + private View getView() { + try { + return (View)webView.getClass().getMethod("getView").invoke(webView); + } catch (Exception e) { + return (View)webView; + } + } + + private int getSplashId() { + int drawableId = 0; + String splashResource = preferences.getString("SplashScreen", "screen"); + if (splashResource != null) { + drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getClass().getPackage().getName()); + if (drawableId == 0) { + drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getPackageName()); + } + } + return drawableId; + } + + @Override + protected void pluginInitialize() { + if (HAS_BUILT_IN_SPLASH_SCREEN) { + return; + } + // Make WebView invisible while loading URL + // CB-11326 Ensure we're calling this on UI thread + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + getView().setVisibility(View.INVISIBLE); + } + }); + int drawableId = getSplashId(); + + // Save initial orientation. + orientation = cordova.getActivity().getResources().getConfiguration().orientation; + + if (firstShow) { + boolean autoHide = preferences.getBoolean("AutoHideSplashScreen", true); + showSplashScreen(autoHide); + } + + if (preferences.getBoolean("SplashShowOnlyFirstTime", true)) { + firstShow = false; + } + } + + /** + * Shorter way to check value of "SplashMaintainAspectRatio" preference. + */ + private boolean isMaintainAspectRatio () { + return preferences.getBoolean("SplashMaintainAspectRatio", false); + } + + private int getFadeDuration () { + int fadeSplashScreenDuration = preferences.getBoolean("FadeSplashScreen", true) ? + preferences.getInteger("FadeSplashScreenDuration", DEFAULT_FADE_DURATION) : 0; + + if (fadeSplashScreenDuration < 30) { + // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10 + // they mean 10 seconds, and not the meaningless 10ms + fadeSplashScreenDuration *= 1000; + } + + return fadeSplashScreenDuration; + } + + @Override + public void onPause(boolean multitasking) { + if (HAS_BUILT_IN_SPLASH_SCREEN) { + return; + } + // hide the splash screen to avoid leaking a window + this.removeSplashScreen(true); + } + + @Override + public void onDestroy() { + if (HAS_BUILT_IN_SPLASH_SCREEN) { + return; + } + // hide the splash screen to avoid leaking a window + this.removeSplashScreen(true); + // If we set this to true onDestroy, we lose track when we go from page to page! + //firstShow = true; + } + + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if (action.equals("hide")) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.postMessage("splashscreen", "hide"); + } + }); + } else if (action.equals("show")) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.postMessage("splashscreen", "show"); + } + }); + } else { + return false; + } + + callbackContext.success(); + return true; + } + + @Override + public Object onMessage(String id, Object data) { + if (HAS_BUILT_IN_SPLASH_SCREEN) { + return null; + } + if ("splashscreen".equals(id)) { + if ("hide".equals(data.toString())) { + this.removeSplashScreen(false); + } else { + this.showSplashScreen(false); + } + } else if ("spinner".equals(id)) { + if ("stop".equals(data.toString())) { + getView().setVisibility(View.VISIBLE); + } + } else if ("onReceivedError".equals(id)) { + this.spinnerStop(); + } + return null; + } + + // Don't add @Override so that plugin still compiles on 3.x.x for a while + public void onConfigurationChanged(Configuration newConfig) { + if (newConfig.orientation != orientation) { + orientation = newConfig.orientation; + + // Splash drawable may change with orientation, so reload it. + if (splashImageView != null) { + int drawableId = getSplashId(); + if (drawableId != 0) { + splashImageView.setImageDrawable(cordova.getActivity().getResources().getDrawable(drawableId)); + } + } + } + } + + private void removeSplashScreen(final boolean forceHideImmediately) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + if (splashDialog != null && splashDialog.isShowing()) { + final int fadeSplashScreenDuration = getFadeDuration(); + // CB-10692 If the plugin is being paused/destroyed, skip the fading and hide it immediately + if (fadeSplashScreenDuration > 0 && forceHideImmediately == false) { + AlphaAnimation fadeOut = new AlphaAnimation(1, 0); + fadeOut.setInterpolator(new DecelerateInterpolator()); + fadeOut.setDuration(fadeSplashScreenDuration); + + splashImageView.setAnimation(fadeOut); + splashImageView.startAnimation(fadeOut); + + fadeOut.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + spinnerStop(); + } + + @Override + public void onAnimationEnd(Animation animation) { + if (splashDialog != null && splashDialog.isShowing()) { + splashDialog.dismiss(); + splashDialog = null; + splashImageView = null; + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + } else { + spinnerStop(); + splashDialog.dismiss(); + splashDialog = null; + splashImageView = null; + } + } + } + }); + } + + /** + * Shows the splash screen over the full Activity + */ + @SuppressWarnings("deprecation") + private void showSplashScreen(final boolean hideAfterDelay) { + final int splashscreenTime = preferences.getInteger("SplashScreenDelay", DEFAULT_SPLASHSCREEN_DURATION); + final int drawableId = getSplashId(); + + final int fadeSplashScreenDuration = getFadeDuration(); + final int effectiveSplashDuration = Math.max(0, splashscreenTime - fadeSplashScreenDuration); + + lastHideAfterDelay = hideAfterDelay; + + // Prevent to show the splash dialog if the activity is in the process of finishing + if (cordova.getActivity().isFinishing()) { + return; + } + // If the splash dialog is showing don't try to show it again + if (splashDialog != null && splashDialog.isShowing()) { + return; + } + if (drawableId == 0 || (splashscreenTime <= 0 && hideAfterDelay)) { + return; + } + + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + // Get reference to display + Display display = cordova.getActivity().getWindowManager().getDefaultDisplay(); + Context context = webView.getContext(); + + // Use an ImageView to render the image because of its flexible scaling options. + splashImageView = new ImageView(context); + splashImageView.setImageResource(drawableId); + LayoutParams layoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + splashImageView.setLayoutParams(layoutParams); + + splashImageView.setMinimumHeight(display.getHeight()); + splashImageView.setMinimumWidth(display.getWidth()); + + // TODO: Use the background color of the webView's parent instead of using the preference. + splashImageView.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK)); + + if (isMaintainAspectRatio()) { + // CENTER_CROP scale mode is equivalent to CSS "background-size:cover" + splashImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + } + else { + // FIT_XY scales image non-uniformly to fit into image view. + splashImageView.setScaleType(ImageView.ScaleType.FIT_XY); + } + + // Create and show the dialog + splashDialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar); + // check to see if the splash screen should be full screen + if ((cordova.getActivity().getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) + == WindowManager.LayoutParams.FLAG_FULLSCREEN) { + splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + splashDialog.setContentView(splashImageView); + splashDialog.setCancelable(false); + splashDialog.show(); + + if (preferences.getBoolean("ShowSplashScreenSpinner", true)) { + spinnerStart(); + } + + // Set Runnable to remove splash screen just in case + if (hideAfterDelay) { + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + public void run() { + if (lastHideAfterDelay) { + removeSplashScreen(false); + } + } + }, effectiveSplashDuration); + } + } + }); + } + + // Show only spinner in the center of the screen + private void spinnerStart() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + spinnerStop(); + + spinnerDialog = new ProgressDialog(webView.getContext()); + spinnerDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + spinnerDialog = null; + } + }); + + spinnerDialog.setCancelable(false); + spinnerDialog.setIndeterminate(true); + + RelativeLayout centeredLayout = new RelativeLayout(cordova.getActivity()); + centeredLayout.setGravity(Gravity.CENTER); + centeredLayout.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + + ProgressBar progressBar = new ProgressBar(webView.getContext()); + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); + progressBar.setLayoutParams(layoutParams); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + String colorName = preferences.getString("SplashScreenSpinnerColor", null); + if(colorName != null){ + int[][] states = new int[][] { + new int[] { android.R.attr.state_enabled}, // enabled + new int[] {-android.R.attr.state_enabled}, // disabled + new int[] {-android.R.attr.state_checked}, // unchecked + new int[] { android.R.attr.state_pressed} // pressed + }; + int progressBarColor = Color.parseColor(colorName); + int[] colors = new int[] { + progressBarColor, + progressBarColor, + progressBarColor, + progressBarColor + }; + ColorStateList colorStateList = new ColorStateList(states, colors); + progressBar.setIndeterminateTintList(colorStateList); + } + } + + centeredLayout.addView(progressBar); + + spinnerDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + spinnerDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + + spinnerDialog.show(); + spinnerDialog.setContentView(centeredLayout); + } + }); + } + + private void spinnerStop() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + if (spinnerDialog != null && spinnerDialog.isShowing()) { + spinnerDialog.dismiss(); + spinnerDialog = null; + } + } + }); + } +} diff --git a/plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js b/plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js new file mode 100644 index 0000000..1a5cd30 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/browser/SplashScreenProxy.js @@ -0,0 +1,170 @@ +/* + * + * 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. + * +*/ + +// Default parameter values including image size can be changed in `config.xml` +var splashImageWidth = 170; +var splashImageHeight = 200; +var position = { x: 0, y: 0, width: splashImageWidth, height: splashImageHeight }; +var localSplash; // the image to display +var localSplashImage; +var bgColor = "#464646"; +var imageSrc = '/img/logo.png'; +var splashScreenDelay = 3000; // in milliseconds +var showSplashScreen = true; // show splashcreen by default +var cordova = require('cordova'); +var configHelper = cordova.require('cordova/confighelper'); +var autoHideSplashScreen = true; + +function updateImageLocation() { + position.width = Math.min(splashImageWidth, window.innerWidth); + position.height = position.width * (splashImageHeight / splashImageWidth); + + localSplash.style.width = window.innerWidth + "px"; + localSplash.style.height = window.innerHeight + "px"; + localSplash.style.top = "0px"; + localSplash.style.left = "0px"; + + localSplashImage.style.top = "50%"; + localSplashImage.style.left = "50%"; + localSplashImage.style.height = position.height + "px"; + localSplashImage.style.width = position.width + "px"; + localSplashImage.style.marginTop = (-position.height / 2) + "px"; + localSplashImage.style.marginLeft = (-position.width / 2) + "px"; +} + +function onResize() { + updateImageLocation(); +} + +var SplashScreen = { + setBGColor: function (cssBGColor) { + bgColor = cssBGColor; + if (localSplash) { + localSplash.style.backgroundColor = bgColor; + } + }, + show: function () { + if(!localSplash) { + window.addEventListener("resize", onResize, false); + localSplash = document.createElement("div"); + localSplash.style.backgroundColor = bgColor; + localSplash.style.position = "absolute"; + localSplash.style["z-index"] = "99999"; + + localSplashImage = document.createElement("img"); + localSplashImage.src = imageSrc; + localSplashImage.style.position = "absolute"; + + updateImageLocation(); + + localSplash.appendChild(localSplashImage); + document.body.appendChild(localSplash); + + // deviceready fires earlier than the plugin init on cold-start + if (SplashScreen.shouldHideImmediately) { + SplashScreen.shouldHideImmediately = false; + window.setTimeout(function () { + SplashScreen.hide(); + }, 1000); + } + } + }, + hide: function () { + if(localSplash) { + var innerLocalSplash = localSplash; + localSplash = null; + window.removeEventListener("resize", onResize, false); + + innerLocalSplash.style.opacity = '0'; + innerLocalSplash.style["-webkit-transition"] = "opacity 1s ease-in-out"; + innerLocalSplash.style["-moz-transition"] = "opacity 1s ease-in-out"; + innerLocalSplash.style["-ms-transition"] = "opacity 1s ease-in-out"; + innerLocalSplash.style["-o-transition"] = "opacity 1s ease-in-out"; + + window.setTimeout(function () { + document.body.removeChild(innerLocalSplash); + innerLocalSplash = null; + }, 1000); + } else { + SplashScreen.shouldHideImmediately = true; + } + } +}; + +/** + * Reads preferences via ConfigHelper and substitutes default parameters. + */ +function readPreferencesFromCfg(cfg) { + try { + var value = cfg.getPreferenceValue('ShowSplashScreen'); + if(typeof value != 'undefined') { + showSplashScreen = value === 'true'; + } + + splashScreenDelay = cfg.getPreferenceValue('SplashScreenDelay') || splashScreenDelay; + splashScreenDelay = parseInt(splashScreenDelay, 10); + + imageSrc = cfg.getPreferenceValue('SplashScreen') || imageSrc; + bgColor = cfg.getPreferenceValue('SplashScreenBackgroundColor') || bgColor; + splashImageWidth = cfg.getPreferenceValue('SplashScreenWidth') || splashImageWidth; + splashImageHeight = cfg.getPreferenceValue('SplashScreenHeight') || splashImageHeight; + autoHideSplashScreen = cfg.getPreferenceValue('AutoHideSplashScreen') || autoHideSplashScreen; + autoHideSplashScreen = (autoHideSplashScreen === true || autoHideSplashScreen.toLowerCase() === 'true'); + } catch(e) { + var msg = '[Browser][SplashScreen] Error occurred on loading preferences from config.xml: ' + JSON.stringify(e); + console.error(msg); + } +} + +/** + * Shows and hides splashscreen if it is enabled, with a delay according the current preferences. + */ +function showAndHide() { + if(showSplashScreen) { + SplashScreen.show(); + + window.setTimeout(function() { + SplashScreen.hide(); + }, splashScreenDelay); + } +} + +/** + * Tries to read config.xml and override default properties and then shows and hides splashscreen if it is enabled. + */ +(function initAndShow() { + configHelper.readConfig(function(config) { + readPreferencesFromCfg(config); + if (autoHideSplashScreen) { + showAndHide(); + } else { + SplashScreen.show(); + } + + }, function(err) { + console.error(err); + }); +})(); + +module.exports = SplashScreen; + +require("cordova/exec/proxy").add("SplashScreen", SplashScreen); + diff --git a/plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.h b/plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.h new file mode 100644 index 0000000..dcc0d77 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.h @@ -0,0 +1,46 @@ +/* + 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 +#import + +typedef struct { + BOOL iPhone; + BOOL iPad; + BOOL iPhone4; + BOOL iPhone5; + BOOL iPhone6; + BOOL iPhone6Plus; + BOOL retina; + BOOL iPhoneX; + +} CDV_iOSDevice; + +@interface CDVSplashScreen : CDVPlugin { + UIActivityIndicatorView* _activityView; + UIImageView* _imageView; + NSString* _curImageName; + BOOL _visible; + BOOL _destroyed; +} + +- (void)show:(CDVInvokedUrlCommand*)command; +- (void)hide:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.m b/plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.m new file mode 100644 index 0000000..9b04582 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/ios/CDVSplashScreen.m @@ -0,0 +1,514 @@ +/* + 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 "CDVSplashScreen.h" +#import +#import +#import "CDVViewController+SplashScreen.h" + +#define kSplashScreenDurationDefault 3000.0f +#define kFadeDurationDefault 500.0f + + +@implementation CDVSplashScreen + +- (void)pluginInitialize +{ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil]; + + [self setVisible:YES]; +} + +- (void)show:(CDVInvokedUrlCommand*)command +{ + [self setVisible:YES]; +} + +- (void)hide:(CDVInvokedUrlCommand*)command +{ + [self setVisible:NO andForce:YES]; +} + +- (void)pageDidLoad +{ + id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]]; + + // if value is missing, default to yes + if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) { + [self setVisible:NO]; + } +} + +- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context +{ + [self updateImage]; +} + +- (void)createViews +{ + /* + * The Activity View is the top spinning throbber in the status/battery bar. We init it with the default Grey Style. + * + * whiteLarge = UIActivityIndicatorViewStyleWhiteLarge + * white = UIActivityIndicatorViewStyleWhite + * gray = UIActivityIndicatorViewStyleGray + * + */ + + // Determine whether rotation should be enabled for this device + // Per iOS HIG, landscape is only supported on iPad and iPhone 6+ + CDV_iOSDevice device = [self getCurrentDevice]; + BOOL autorotateValue = (device.iPad || device.iPhone6Plus || device.iPhoneX) ? + [(CDVViewController *)self.viewController shouldAutorotateDefaultValue] : + NO; + + [(CDVViewController *)self.viewController setEnabledAutorotation:autorotateValue]; + + NSString* topActivityIndicator = [self.commandDelegate.settings objectForKey:[@"TopActivityIndicator" lowercaseString]]; + UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray; + + if ([topActivityIndicator isEqualToString:@"whiteLarge"]) + { + topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge; + } + else if ([topActivityIndicator isEqualToString:@"white"]) + { + topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhite; + } + else if ([topActivityIndicator isEqualToString:@"gray"]) + { + topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray; + } + + UIView* parentView = self.viewController.view; + parentView.userInteractionEnabled = NO; // disable user interaction while splashscreen is shown + _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle]; + _activityView.center = CGPointMake(parentView.bounds.size.width / 2, parentView.bounds.size.height / 2); + _activityView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin + | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; + [_activityView startAnimating]; + + // Set the frame & image later. + _imageView = [[UIImageView alloc] init]; + [parentView addSubview:_imageView]; + + id showSplashScreenSpinnerValue = [self.commandDelegate.settings objectForKey:[@"ShowSplashScreenSpinner" lowercaseString]]; + // backwards compatibility - if key is missing, default to true + if ((showSplashScreenSpinnerValue == nil) || [showSplashScreenSpinnerValue boolValue]) + { + [parentView addSubview:_activityView]; + } + + // Frame is required when launching in portrait mode. + // Bounds for landscape since it captures the rotation. + [parentView addObserver:self forKeyPath:@"frame" options:0 context:nil]; + [parentView addObserver:self forKeyPath:@"bounds" options:0 context:nil]; + + [self updateImage]; + _destroyed = NO; +} + +- (void)hideViews +{ + [_imageView setAlpha:0]; + [_activityView setAlpha:0]; +} + +- (void)destroyViews +{ + _destroyed = YES; + [(CDVViewController *)self.viewController setEnabledAutorotation:[(CDVViewController *)self.viewController shouldAutorotateDefaultValue]]; + + [_imageView removeFromSuperview]; + [_activityView removeFromSuperview]; + _imageView = nil; + _activityView = nil; + _curImageName = nil; + + self.viewController.view.userInteractionEnabled = YES; // re-enable user interaction upon completion + @try { + [self.viewController.view removeObserver:self forKeyPath:@"frame"]; + [self.viewController.view removeObserver:self forKeyPath:@"bounds"]; + } + @catch (NSException *exception) { + // When reloading the page from a remotely connected Safari, there + // are no observers, so the removeObserver method throws an exception, + // that we can safely ignore. + // Alternatively we can check whether there are observers before calling removeObserver + } +} + +- (CDV_iOSDevice) getCurrentDevice +{ + CDV_iOSDevice device; + + UIScreen* mainScreen = [UIScreen mainScreen]; + CGFloat mainScreenHeight = mainScreen.bounds.size.height; + CGFloat mainScreenWidth = mainScreen.bounds.size.width; + + int limit = MAX(mainScreenHeight,mainScreenWidth); + + device.iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); + device.iPhone = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone); + device.retina = ([mainScreen scale] == 2.0); + device.iPhone4 = (device.iPhone && limit == 480.0); + device.iPhone5 = (device.iPhone && limit == 568.0); + // note these below is not a true device detect, for example if you are on an + // iPhone 6/6+ but the app is scaled it will prob set iPhone5 as true, but + // this is appropriate for detecting the runtime screen environment + device.iPhone6 = (device.iPhone && limit == 667.0); + device.iPhone6Plus = (device.iPhone && limit == 736.0); + device.iPhoneX = (device.iPhone && limit == 812.0); + + return device; +} + +- (BOOL) isUsingCDVLaunchScreen { + NSString* launchStoryboardName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchStoryboardName"]; + if (launchStoryboardName) { + return ([launchStoryboardName isEqualToString:@"CDVLaunchScreen"]); + } else { + return NO; + } +} + +- (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(id)orientationDelegate device:(CDV_iOSDevice)device +{ + // Use UILaunchImageFile if specified in plist. Otherwise, use Default. + NSString* imageName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"]; + + // detect if we are using CB-9762 Launch Storyboard; if so, return the associated image instead + if ([self isUsingCDVLaunchScreen]) { + imageName = @"LaunchStoryboard"; + return imageName; + } + + NSUInteger supportedOrientations = [orientationDelegate supportedInterfaceOrientations]; + + // Checks to see if the developer has locked the orientation to use only one of Portrait or Landscape + BOOL supportsLandscape = (supportedOrientations & UIInterfaceOrientationMaskLandscape); + BOOL supportsPortrait = (supportedOrientations & UIInterfaceOrientationMaskPortrait || supportedOrientations & UIInterfaceOrientationMaskPortraitUpsideDown); + // this means there are no mixed orientations in there + BOOL isOrientationLocked = !(supportsPortrait && supportsLandscape); + + if (imageName) + { + imageName = [imageName stringByDeletingPathExtension]; + } + else + { + imageName = @"Default"; + } + + // Add Asset Catalog specific prefixes + if ([imageName isEqualToString:@"LaunchImage"]) + { + if (device.iPhone4 || device.iPhone5 || device.iPad) { + imageName = [imageName stringByAppendingString:@"-700"]; + } else if(device.iPhone6) { + imageName = [imageName stringByAppendingString:@"-800"]; + } else if(device.iPhone6Plus || device.iPhoneX ) { + if(device.iPhone6Plus) { + imageName = [imageName stringByAppendingString:@"-800"]; + } else { + imageName = [imageName stringByAppendingString:@"-1100"]; + } + if (currentOrientation == UIInterfaceOrientationPortrait || currentOrientation == UIInterfaceOrientationPortraitUpsideDown) + { + imageName = [imageName stringByAppendingString:@"-Portrait"]; + } + } + } + + if (device.iPhone5) + { // does not support landscape + imageName = [imageName stringByAppendingString:@"-568h"]; + } + else if (device.iPhone6) + { // does not support landscape + imageName = [imageName stringByAppendingString:@"-667h"]; + } + else if (device.iPhone6Plus || device.iPhoneX) + { // supports landscape + if (isOrientationLocked) + { + imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"")]; + } + else + { + switch (currentOrientation) + { + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + imageName = [imageName stringByAppendingString:@"-Landscape"]; + break; + default: + break; + } + } + if (device.iPhoneX) { + imageName = [imageName stringByAppendingString:@"-2436h"]; + } else { + imageName = [imageName stringByAppendingString:@"-736h"]; + } + } + else if (device.iPad) + { // supports landscape + if (isOrientationLocked) + { + imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"-Portrait")]; + } + else + { + switch (currentOrientation) + { + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + imageName = [imageName stringByAppendingString:@"-Landscape"]; + break; + + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + default: + imageName = [imageName stringByAppendingString:@"-Portrait"]; + break; + } + } + } + + return imageName; +} + +- (UIInterfaceOrientation)getCurrentOrientation +{ + UIInterfaceOrientation iOrientation = [UIApplication sharedApplication].statusBarOrientation; + UIDeviceOrientation dOrientation = [UIDevice currentDevice].orientation; + + bool landscape; + + if (dOrientation == UIDeviceOrientationUnknown || dOrientation == UIDeviceOrientationFaceUp || dOrientation == UIDeviceOrientationFaceDown) { + // If the device is laying down, use the UIInterfaceOrientation based on the status bar. + landscape = UIInterfaceOrientationIsLandscape(iOrientation); + } else { + // If the device is not laying down, use UIDeviceOrientation. + landscape = UIDeviceOrientationIsLandscape(dOrientation); + + // There's a bug in iOS!!!! http://openradar.appspot.com/7216046 + // So values needs to be reversed for landscape! + if (dOrientation == UIDeviceOrientationLandscapeLeft) + { + iOrientation = UIInterfaceOrientationLandscapeRight; + } + else if (dOrientation == UIDeviceOrientationLandscapeRight) + { + iOrientation = UIInterfaceOrientationLandscapeLeft; + } + else if (dOrientation == UIDeviceOrientationPortrait) + { + iOrientation = UIInterfaceOrientationPortrait; + } + else if (dOrientation == UIDeviceOrientationPortraitUpsideDown) + { + iOrientation = UIInterfaceOrientationPortraitUpsideDown; + } + } + + return iOrientation; +} + +// Sets the view's frame and image. +- (void)updateImage +{ + NSString* imageName = [self getImageName:[self getCurrentOrientation] delegate:(id)self.viewController device:[self getCurrentDevice]]; + + if (![imageName isEqualToString:_curImageName]) + { + UIImage* img = [UIImage imageNamed:imageName]; + _imageView.image = img; + _curImageName = imageName; + } + + // Check that splash screen's image exists before updating bounds + if (_imageView.image) + { + [self updateBounds]; + } + else + { + NSLog(@"WARNING: The splashscreen image named %@ was not found", imageName); + } +} + +- (void)updateBounds +{ + if ([self isUsingCDVLaunchScreen]) { + // CB-9762's launch screen expects the image to fill the screen and be scaled using AspectFill. + CGSize viewportSize = [UIApplication sharedApplication].delegate.window.bounds.size; + _imageView.frame = CGRectMake(0, 0, viewportSize.width, viewportSize.height); + _imageView.contentMode = UIViewContentModeScaleAspectFill; + return; + } + + UIImage* img = _imageView.image; + CGRect imgBounds = (img) ? CGRectMake(0, 0, img.size.width, img.size.height) : CGRectZero; + + CGSize screenSize = [self.viewController.view convertRect:[UIScreen mainScreen].bounds fromView:nil].size; + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + CGAffineTransform imgTransform = CGAffineTransformIdentity; + + /* If and only if an iPhone application is landscape-only as per + * UISupportedInterfaceOrientations, the view controller's orientation is + * landscape. In this case the image must be rotated in order to appear + * correctly. + */ + CDV_iOSDevice device = [self getCurrentDevice]; + if (UIInterfaceOrientationIsLandscape(orientation) && !device.iPhone6Plus && !device.iPad && !device.iPhoneX) + { + imgTransform = CGAffineTransformMakeRotation(M_PI / 2); + imgBounds.size = CGSizeMake(imgBounds.size.height, imgBounds.size.width); + } + + // There's a special case when the image is the size of the screen. + if (CGSizeEqualToSize(screenSize, imgBounds.size)) + { + CGRect statusFrame = [self.viewController.view convertRect:[UIApplication sharedApplication].statusBarFrame fromView:nil]; + if (!(IsAtLeastiOSVersion(@"7.0"))) + { + imgBounds.origin.y -= statusFrame.size.height; + } + } + else if (imgBounds.size.width > 0) + { + CGRect viewBounds = self.viewController.view.bounds; + CGFloat imgAspect = imgBounds.size.width / imgBounds.size.height; + CGFloat viewAspect = viewBounds.size.width / viewBounds.size.height; + // This matches the behaviour of the native splash screen. + CGFloat ratio; + if (viewAspect > imgAspect) + { + ratio = viewBounds.size.width / imgBounds.size.width; + } + else + { + ratio = viewBounds.size.height / imgBounds.size.height; + } + imgBounds.size.height *= ratio; + imgBounds.size.width *= ratio; + } + + _imageView.transform = imgTransform; + _imageView.frame = imgBounds; +} + +- (void)setVisible:(BOOL)visible +{ + [self setVisible:visible andForce:NO]; +} + +- (void)setVisible:(BOOL)visible andForce:(BOOL)force +{ + if (visible != _visible || force) + { + _visible = visible; + + id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreen" lowercaseString]]; + id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreenDuration" lowercaseString]]; + + float fadeDuration = fadeSplashScreenDuration == nil ? kFadeDurationDefault : [fadeSplashScreenDuration floatValue]; + + id splashDurationString = [self.commandDelegate.settings objectForKey: [@"SplashScreenDelay" lowercaseString]]; + float splashDuration = splashDurationString == nil ? kSplashScreenDurationDefault : [splashDurationString floatValue]; + + id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]]; + BOOL autoHideSplashScreen = true; + + if (autoHideSplashScreenValue != nil) { + autoHideSplashScreen = [autoHideSplashScreenValue boolValue]; + } + + if (!autoHideSplashScreen) { + // CB-10412 SplashScreenDelay does not make sense if the splashscreen is hidden manually + splashDuration = 0; + } + + + if (fadeSplashScreenValue == nil) + { + fadeSplashScreenValue = @"true"; + } + + if (![fadeSplashScreenValue boolValue]) + { + fadeDuration = 0; + } + else if (fadeDuration < 30) + { + // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10 + // they mean 10 seconds, and not the meaningless 10ms + fadeDuration *= 1000; + } + + if (_visible) + { + if (_imageView == nil) + { + [self createViews]; + } + } + else if (fadeDuration == 0 && splashDuration == 0) + { + [self destroyViews]; + } + else + { + __weak __typeof(self) weakSelf = self; + float effectiveSplashDuration; + + // [CB-10562] AutoHideSplashScreen may be "true" but we should still be able to hide the splashscreen manually. + if (!autoHideSplashScreen || force) { + effectiveSplashDuration = (fadeDuration) / 1000; + } else { + effectiveSplashDuration = (splashDuration - fadeDuration) / 1000; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t) effectiveSplashDuration * NSEC_PER_SEC), dispatch_get_main_queue(), CFBridgingRelease(CFBridgingRetain(^(void) { + if (!_destroyed) { + [UIView transitionWithView:self.viewController.view + duration:(fadeDuration / 1000) + options:UIViewAnimationOptionTransitionNone + animations:^(void) { + [weakSelf hideViews]; + } + completion:^(BOOL finished) { + // Always destroy views, otherwise you could have an + // invisible splashscreen that is overlayed over your active views + // which causes that no touch events are passed + if (!_destroyed) { + [weakSelf destroyViews]; + // TODO: It might also be nice to have a js event happen here -jm + } + } + ]; + } + }))); + } + } +} + +@end diff --git a/plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.h b/plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.h new file mode 100644 index 0000000..a948ea3 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.h @@ -0,0 +1,28 @@ +/* + 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 + +@interface CDVViewController (SplashScreen) + +@property (nonatomic, assign) BOOL enabledAutorotation; +@property (nonatomic, readonly) BOOL shouldAutorotateDefaultValue; + + +@end diff --git a/plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.m b/plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.m new file mode 100644 index 0000000..e483def --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/ios/CDVViewController+SplashScreen.m @@ -0,0 +1,89 @@ +/* + 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 "CDVViewController+SplashScreen.h" +#import + +@implementation CDVViewController (SplashScreen) + +@dynamic enabledAutorotation; + +- (void)setEnabledAutorotation:(BOOL)value +{ + objc_setAssociatedObject(self, + @selector(enabledAutorotation), + [NSNumber numberWithBool:value], + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)enabledAutorotation +{ + NSNumber *number = (NSNumber *)objc_getAssociatedObject(self, @selector(enabledAutorotation)); + + // Defaulting to YES to correspond parent CDVViewController behavior + if (number == nil) + { + return YES; + } + + return [number boolValue]; +} + ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class class = [self class]; + + SEL originalSelector = @selector(shouldAutorotate); + SEL swizzledSelector = @selector(splash_shouldAutorotate); + + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + + BOOL didAddMethod = class_addMethod(class, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)); + + if (didAddMethod) { + class_replaceMethod(class, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + } else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } + }); +} + +#pragma mark - Method Swizzling + +- (BOOL)splash_shouldAutorotate +{ + return self.enabledAutorotation; +} + + +- (BOOL)shouldAutorotateDefaultValue +{ + return [self splash_shouldAutorotate]; +} + +@end diff --git a/plugins/cordova-plugin-splashscreen/src/wp/ResolutionHelper.cs b/plugins/cordova-plugin-splashscreen/src/wp/ResolutionHelper.cs new file mode 100644 index 0000000..050c392 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/wp/ResolutionHelper.cs @@ -0,0 +1,39 @@ +/* + 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. +*/ + +using Microsoft.Phone.Info; +using System; +using System.Windows; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public enum Resolutions { WVGA, WXGA, HD }; + + public static class ResolutionHelper + { + public static Resolutions CurrentResolution + { + get + { + switch (Application.Current.Host.Content.ScaleFactor) + { + case 100: return Resolutions.WVGA; + case 160: return Resolutions.WXGA; + case 150: return Resolutions.HD; + } + throw new InvalidOperationException("Unknown resolution"); + } + } + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/src/wp/SplashScreen.cs b/plugins/cordova-plugin-splashscreen/src/wp/SplashScreen.cs new file mode 100644 index 0000000..c56d4ad --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/src/wp/SplashScreen.cs @@ -0,0 +1,255 @@ +/* + 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. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Info; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using System.Windows.Media.Imaging; +using System.Windows.Resources; +using System.IO; +using System.Xml.Linq; +using System.Linq; +using System.Windows.Threading; + +namespace WPCordovaClassLib.Cordova.Commands +{ + /// + /// Listens for changes to the state of the battery on the device. + /// Currently only the "isPlugged" parameter available via native APIs. + /// + public class SplashScreen : BaseCommand + { + private Popup popup; + + // Time until we dismiss the splashscreen + private int prefDelay = 3000; + + // Whether we hide it by default + private bool prefAutoHide = true; + + // Path to image to use + private string prefImagePath = "SplashScreenImage.jpg"; + + // static because autodismiss is only ever applied once, at app launch + // subsequent page loads should not cause the SplashScreen to be shown. + private static bool WasShown = false; + + public SplashScreen() + { + LoadConfigPrefs(); + + Image SplashScreen = new Image() + { + Height = Application.Current.Host.Content.ActualHeight, + Width = Application.Current.Host.Content.ActualWidth, + Stretch = Stretch.Fill + }; + + var imageResource = GetSplashScreenImageResource(); + if (imageResource != null) + { + BitmapImage splash_image = new BitmapImage(); + splash_image.SetSource(imageResource.Stream); + SplashScreen.Source = splash_image; + } + + // Instansiate the popup and set the Child property of Popup to SplashScreen + popup = new Popup() { IsOpen = false, + Child = SplashScreen, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Center + + }; + } + + public override void OnInit() + { + // we only want to autoload on the first page load. + // but OnInit is called for every page load. + if (!SplashScreen.WasShown) + { + SplashScreen.WasShown = true; + show(); + } + } + + private void LoadConfigPrefs() + { + StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("config.xml", UriKind.Relative)); + if (streamInfo != null) + { + using (StreamReader sr = new StreamReader(streamInfo.Stream)) + { + //This will Read Keys Collection for the xml file + XDocument configFile = XDocument.Parse(sr.ReadToEnd()); + + string configAutoHide = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "AutoHideSplashScreen") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + + bool bVal; + prefAutoHide = bool.TryParse(configAutoHide, out bVal) ? bVal : prefAutoHide; + + string configDelay = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "SplashScreenDelay") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + int nVal; + prefDelay = int.TryParse(configDelay, out nVal) ? nVal : prefDelay; + + string configImage = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "SplashScreen") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + + if (!String.IsNullOrEmpty(configImage)) + { + prefImagePath = configImage; + } + } + } + } + + private StreamResourceInfo GetSplashScreenImageResource() + { + // Get the base filename for the splash screen images + string imageName = System.IO.Path.GetFileNameWithoutExtension(prefImagePath); + Uri imageUri = null; + StreamResourceInfo imageResource = null; + + // First, try to get a resolution-specific splashscreen + try + { + // Determine the device's resolution + switch (ResolutionHelper.CurrentResolution) + { + case Resolutions.HD: + imageUri = new Uri(imageName + ".screen-720p.jpg", UriKind.Relative); + break; + + case Resolutions.WVGA: + imageUri = new Uri(imageName + ".screen-WVGA.jpg", UriKind.Relative); + break; + + case Resolutions.WXGA: + default: + imageUri = new Uri(imageName + ".screen-WXGA.jpg", UriKind.Relative); + break; + } + + imageResource = Application.GetResourceStream(imageUri); + } + catch (Exception) + { + // It's OK if we didn't get a resolution-specific image + } + + // Fallback to the default image name without decoration + if (imageResource == null) + { + imageUri = new Uri(prefImagePath, UriKind.Relative); + imageResource = Application.GetResourceStream(imageUri); + } + + if (imageUri != null) Debug.WriteLine("INFO :: SplashScreen: using image {0}", imageUri.OriginalString); + + return imageResource; + } + + public void show(string options = null) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (!popup.IsOpen) + { + popup.Child.Opacity = 0; + + Storyboard story = new Storyboard(); + DoubleAnimation animation = new DoubleAnimation() + { + From = 0.0, + To = 1.0, + Duration = new Duration(TimeSpan.FromSeconds(0.2)) + }; + + Storyboard.SetTarget(animation, popup.Child); + Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity")); + story.Children.Add(animation); + + story.Begin(); + + popup.IsOpen = true; + + if (prefAutoHide) + { + StartAutoHideTimer(); + } + } + }); + } + + public void hide(string options = null) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (popup.IsOpen) + { + popup.Child.Opacity = 1.0; + + Storyboard story = new Storyboard(); + DoubleAnimation animation = new DoubleAnimation() + { + From = 1.0, + To = 0.0, + Duration = new Duration(TimeSpan.FromSeconds(0.4)) + }; + + Storyboard.SetTarget(animation, popup.Child); + Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity")); + story.Children.Add(animation); + story.Completed += (object sender, EventArgs e) => + { + popup.IsOpen = false; + }; + story.Begin(); + } + }); + } + + private void StartAutoHideTimer() + { + var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(prefDelay) }; + timer.Tick += (object sender, EventArgs e) => + { + hide(); + timer.Stop(); + }; + timer.Start(); + } + } +} diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/contents.xcworkspacedata b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..2dd325a --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout new file mode 100644 index 0000000..7e4cdb9 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + 6BE9AD73-1B9F-4362-98D7-DC631BEC6185 + IDESourceControlProjectName + CDVSplashScreenTest + IDESourceControlProjectOriginsDictionary + + BEF5A5D0FF64801E558286389440357A9233D7DB + https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git + + IDESourceControlProjectPath + tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + BEF5A5D0FF64801E558286389440357A9233D7DB + ../../../../.. + + IDESourceControlProjectURL + https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + BEF5A5D0FF64801E558286389440357A9233D7DB + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + BEF5A5D0FF64801E558286389440357A9233D7DB + IDESourceControlWCCName + cordova-plugin-splashscreen + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/xcschemes/CordovaLib.xcscheme b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/xcschemes/CordovaLib.xcscheme new file mode 100644 index 0000000..13f9a15 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest.xcworkspace/xcshareddata/xcschemes/CordovaLib.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/.npmignore b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/.npmignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/.npmignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTest.m b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTest.m new file mode 100644 index 0000000..1637d24 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTest.m @@ -0,0 +1,702 @@ +/* + 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 +#import +#import +#import "CDVSplashScreen.h" +#import "ImageNameTestDelegates.h" + +const CDV_iOSDevice CDV_iOSDeviceZero = { 0, 0, 0, 0, 0, 0 }; + +@interface ImageNameTest : XCTestCase + +@property (nonatomic, strong) CDVSplashScreen* plugin; + +@end + +@interface CDVSplashScreen () + +// expose private interface +- (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(id)orientationDelegate device:(CDV_iOSDevice)device; + +@end + +@implementation ImageNameTest + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. + + self.plugin = [[CDVSplashScreen alloc] init]; +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void) orientationHelper:(id)delegate expectedImageNameDictionary:(NSDictionary*)expectedImageNameDictionary device:(CDV_iOSDevice)device{ + + NSString* name = nil; + NSString* expectedImageName = nil; + UIInterfaceOrientation currentOrientation; + NSString* deviceName = device.iPad? @"iPad" : device.iPhone6Plus? @"iPhone6Plus": device.iPhone6? @"iPhone6": device.iPhone5? @"iPhone5" : @"iPhone"; + + // LandscapeLeft, should always return expectedImageName + currentOrientation = UIInterfaceOrientationLandscapeLeft; + name = [self.plugin getImageName:currentOrientation delegate:delegate device:device]; + expectedImageName = [expectedImageNameDictionary objectForKey:@"landscapeLeft"]; + XCTAssertTrue([expectedImageName isEqualToString:name], @"%@ - %@ failed (%@)", @"Landscape", deviceName, name); + + // LandscapeRight - should always return expectedImageName + currentOrientation = UIInterfaceOrientationLandscapeRight; + name = [self.plugin getImageName:currentOrientation delegate:delegate device:device]; + expectedImageName = [expectedImageNameDictionary objectForKey:@"landscapeRight"]; + XCTAssertTrue([expectedImageName isEqualToString:name], @"%@ - %@ failed (%@)", @"Landscape", deviceName, name); + + // Portrait - should always return expectedImageName + currentOrientation = UIInterfaceOrientationPortrait; + name = [self.plugin getImageName:currentOrientation delegate:delegate device:device]; + expectedImageName = [expectedImageNameDictionary objectForKey:@"portrait"]; + XCTAssertTrue([expectedImageName isEqualToString:name], @"%@ - %@ failed (%@)", @"Portrait", deviceName, name); + + // PortraitUpsideDown - should always return expectedImageName + currentOrientation = UIInterfaceOrientationPortraitUpsideDown; + name = [self.plugin getImageName:currentOrientation delegate:delegate device:device]; + expectedImageName = [expectedImageNameDictionary objectForKey:@"portraitUpsideDown"]; + XCTAssertTrue([expectedImageName isEqualToString:name], @"%@ - %@ failed (%@)", @"Portrait", deviceName, name); +} + +- (void)testiPadOrientation { + + CDV_iOSDevice device = CDV_iOSDeviceZero; + device.iPad = YES; + + // One orientation + + PortraitOnly* delegate = [[PortraitOnly alloc] init]; + [self orientationHelper:delegate expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Portrait", + @"landscapeRight" : @"Default-Portrait", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; + + PortraitUpsideDownOnly* delegate2 = [[PortraitUpsideDownOnly alloc] init]; + [self orientationHelper:delegate2 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Portrait", + @"landscapeRight" : @"Default-Portrait", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; + + LandscapeLeftOnly* delegate3 = [[LandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate3 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Landscape", + @"portraitUpsideDown" : @"Default-Landscape" + } + device:device]; + + LandscapeRightOnly* delegate4 = [[LandscapeRightOnly alloc] init]; + [self orientationHelper:delegate4 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Landscape", + @"portraitUpsideDown" : @"Default-Landscape" + } + device:device]; + + // All Portrait + + AllPortraitOnly* delegate5 = [[AllPortraitOnly alloc] init]; + [self orientationHelper:delegate5 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Portrait", + @"landscapeRight" : @"Default-Portrait", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; + + // All Landscape + + AllLandscapeOnly* delegate6 = [[AllLandscapeOnly alloc] init]; + [self orientationHelper:delegate6 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Landscape", + @"portraitUpsideDown" : @"Default-Landscape" + } + device:device]; + + + // All orientations + + AllOrientations* delegate7 = [[AllOrientations alloc] init]; + [self orientationHelper:delegate7 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; + + // Portrait and Landscape Left + + PortraitAndLandscapeLeftOnly* delegate8 = [[PortraitAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate8 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; + + // Portrait and Landscape Right + + PortraitAndLandscapeRightOnly* delegate9 = [[PortraitAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate9 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; + + // PortraitUpsideDown and Landscape Left + + PortraitUpsideDownAndLandscapeLeftOnly* delegate10 = [[PortraitUpsideDownAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate10 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; + + // PortraitUpsideDown and Landscape Right + + PortraitUpsideDownAndLandscapeRightOnly* delegate11 = [[PortraitUpsideDownAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate11 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape", + @"landscapeRight" : @"Default-Landscape", + @"portrait" : @"Default-Portrait", + @"portraitUpsideDown" : @"Default-Portrait" + } + device:device]; +} + +- (void)testiPhoneOrientation { + + CDV_iOSDevice device = CDV_iOSDeviceZero; + device.iPhone = YES; + + // One orientation + + PortraitOnly* delegate = [[PortraitOnly alloc] init]; + [self orientationHelper:delegate expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + PortraitUpsideDownOnly* delegate2 = [[PortraitUpsideDownOnly alloc] init]; + [self orientationHelper:delegate2 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + LandscapeLeftOnly* delegate3 = [[LandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate3 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + LandscapeRightOnly* delegate4 = [[LandscapeRightOnly alloc] init]; + [self orientationHelper:delegate4 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + // All Portrait + + AllPortraitOnly* delegate5 = [[AllPortraitOnly alloc] init]; + [self orientationHelper:delegate5 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + // All Landscape + + AllLandscapeOnly* delegate6 = [[AllLandscapeOnly alloc] init]; + [self orientationHelper:delegate6 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + + // All orientations + + AllOrientations* delegate7 = [[AllOrientations alloc] init]; + [self orientationHelper:delegate7 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + // Portrait and Landscape Left + + PortraitAndLandscapeLeftOnly* delegate8 = [[PortraitAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate8 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + // Portrait and Landscape Right + + PortraitAndLandscapeRightOnly* delegate9 = [[PortraitAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate9 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + // PortraitUpsideDown and Landscape Left + + PortraitUpsideDownAndLandscapeLeftOnly* delegate10 = [[PortraitUpsideDownAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate10 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; + + // PortraitUpsideDown and Landscape Right + + PortraitUpsideDownAndLandscapeRightOnly* delegate11 = [[PortraitUpsideDownAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate11 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default", + @"landscapeRight" : @"Default", + @"portrait" : @"Default", + @"portraitUpsideDown" : @"Default" + } + device:device]; +} + +- (void)testiPhone5Orientation { + + CDV_iOSDevice device = CDV_iOSDeviceZero; + device.iPhone = YES; + device.iPhone5 = YES; + + // One orientation + + PortraitOnly* delegate = [[PortraitOnly alloc] init]; + [self orientationHelper:delegate expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + PortraitUpsideDownOnly* delegate2 = [[PortraitUpsideDownOnly alloc] init]; + [self orientationHelper:delegate2 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + LandscapeLeftOnly* delegate3 = [[LandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate3 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + LandscapeRightOnly* delegate4 = [[LandscapeRightOnly alloc] init]; + [self orientationHelper:delegate4 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + // All Portrait + + AllPortraitOnly* delegate5 = [[AllPortraitOnly alloc] init]; + [self orientationHelper:delegate5 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + // All Landscape + + AllLandscapeOnly* delegate6 = [[AllLandscapeOnly alloc] init]; + [self orientationHelper:delegate6 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + + // All orientations + + AllOrientations* delegate7 = [[AllOrientations alloc] init]; + [self orientationHelper:delegate7 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + // Portrait and Landscape Left + + PortraitAndLandscapeLeftOnly* delegate8 = [[PortraitAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate8 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + // Portrait and Landscape Right + + PortraitAndLandscapeRightOnly* delegate9 = [[PortraitAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate9 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + // PortraitUpsideDown and Landscape Left + + PortraitUpsideDownAndLandscapeLeftOnly* delegate10 = [[PortraitUpsideDownAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate10 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; + + // PortraitUpsideDown and Landscape Right + + PortraitUpsideDownAndLandscapeRightOnly* delegate11 = [[PortraitUpsideDownAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate11 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-568h", + @"landscapeRight" : @"Default-568h", + @"portrait" : @"Default-568h", + @"portraitUpsideDown" : @"Default-568h" + } + device:device]; +} + +- (void)testiPhone6Orientation { + + CDV_iOSDevice device = CDV_iOSDeviceZero; + device.iPhone = YES; + device.iPhone6 = YES; + + // One orientation + + PortraitOnly* delegate = [[PortraitOnly alloc] init]; + [self orientationHelper:delegate expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + PortraitUpsideDownOnly* delegate2 = [[PortraitUpsideDownOnly alloc] init]; + [self orientationHelper:delegate2 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + LandscapeLeftOnly* delegate3 = [[LandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate3 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + LandscapeRightOnly* delegate4 = [[LandscapeRightOnly alloc] init]; + [self orientationHelper:delegate4 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + // All Portrait + + AllPortraitOnly* delegate5 = [[AllPortraitOnly alloc] init]; + [self orientationHelper:delegate5 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + // All Landscape + + AllLandscapeOnly* delegate6 = [[AllLandscapeOnly alloc] init]; + [self orientationHelper:delegate6 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + + // All orientations + + AllOrientations* delegate7 = [[AllOrientations alloc] init]; + [self orientationHelper:delegate7 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + // Portrait and Landscape Left + + PortraitAndLandscapeLeftOnly* delegate8 = [[PortraitAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate8 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + // Portrait and Landscape Right + + PortraitAndLandscapeRightOnly* delegate9 = [[PortraitAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate9 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + // PortraitUpsideDown and Landscape Left + + PortraitUpsideDownAndLandscapeLeftOnly* delegate10 = [[PortraitUpsideDownAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate10 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; + + // PortraitUpsideDown and Landscape Right + + PortraitUpsideDownAndLandscapeRightOnly* delegate11 = [[PortraitUpsideDownAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate11 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-667h", + @"landscapeRight" : @"Default-667h", + @"portrait" : @"Default-667h", + @"portraitUpsideDown" : @"Default-667h" + } + device:device]; +} + +- (void)testiPhone6PlusOrientation { + + CDV_iOSDevice device = CDV_iOSDeviceZero; + device.iPhone = YES; + device.iPhone6Plus = YES; + + // One orientation + + PortraitOnly* delegate = [[PortraitOnly alloc] init]; + [self orientationHelper:delegate expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-736h", + @"landscapeRight" : @"Default-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; + + PortraitUpsideDownOnly* delegate2 = [[PortraitUpsideDownOnly alloc] init]; + [self orientationHelper:delegate2 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-736h", + @"landscapeRight" : @"Default-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; + + LandscapeLeftOnly* delegate3 = [[LandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate3 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-Landscape-736h", + @"portraitUpsideDown" : @"Default-Landscape-736h" + } + device:device]; + + LandscapeRightOnly* delegate4 = [[LandscapeRightOnly alloc] init]; + [self orientationHelper:delegate4 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-Landscape-736h", + @"portraitUpsideDown" : @"Default-Landscape-736h" + } + device:device]; + + // All Portrait + + AllPortraitOnly* delegate5 = [[AllPortraitOnly alloc] init]; + [self orientationHelper:delegate5 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-736h", + @"landscapeRight" : @"Default-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; + + // All Landscape + + AllLandscapeOnly* delegate6 = [[AllLandscapeOnly alloc] init]; + [self orientationHelper:delegate6 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-Landscape-736h", + @"portraitUpsideDown" : @"Default-Landscape-736h" + } + device:device]; + + + // All orientations + + AllOrientations* delegate7 = [[AllOrientations alloc] init]; + [self orientationHelper:delegate7 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; + + // Portrait and Landscape Left + + PortraitAndLandscapeLeftOnly* delegate8 = [[PortraitAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate8 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; + + // Portrait and Landscape Right + + PortraitAndLandscapeRightOnly* delegate9 = [[PortraitAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate9 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; + + // PortraitUpsideDown and Landscape Left + + PortraitUpsideDownAndLandscapeLeftOnly* delegate10 = [[PortraitUpsideDownAndLandscapeLeftOnly alloc] init]; + [self orientationHelper:delegate10 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; + + // PortraitUpsideDown and Landscape Right + + PortraitUpsideDownAndLandscapeRightOnly* delegate11 = [[PortraitUpsideDownAndLandscapeRightOnly alloc] init]; + [self orientationHelper:delegate11 expectedImageNameDictionary:@{ + @"landscapeLeft" : @"Default-Landscape-736h", + @"landscapeRight" : @"Default-Landscape-736h", + @"portrait" : @"Default-736h", + @"portraitUpsideDown" : @"Default-736h" + } + device:device]; +} + + + +@end diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.h b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.h new file mode 100644 index 0000000..be4a788 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.h @@ -0,0 +1,57 @@ +/* + 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 +#import + +@interface PortraitOnly : NSObject +@end + +@interface PortraitUpsideDownOnly : NSObject +@end + +@interface AllPortraitOnly : NSObject +@end + + +@interface LandscapeLeftOnly : NSObject +@end + +@interface LandscapeRightOnly : NSObject +@end + +@interface AllLandscapeOnly : NSObject +@end + + +@interface AllOrientations : NSObject +@end + +@interface PortraitAndLandscapeLeftOnly : NSObject +@end + +@interface PortraitAndLandscapeRightOnly : NSObject +@end + +@interface PortraitUpsideDownAndLandscapeLeftOnly : NSObject +@end + +@interface PortraitUpsideDownAndLandscapeRightOnly : NSObject +@end + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.m b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.m new file mode 100644 index 0000000..b5a1b23 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/ImageNameTestDelegates.m @@ -0,0 +1,200 @@ +/* + 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 +#import "ImageNameTestDelegates.h" + +@implementation PortraitOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation PortraitUpsideDownOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortraitUpsideDown; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation AllPortraitOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + + +@implementation LandscapeLeftOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskLandscapeLeft; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation LandscapeRightOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskLandscapeRight; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation AllLandscapeOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + + +@implementation AllOrientations + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskAll; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation PortraitAndLandscapeLeftOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation PortraitAndLandscapeRightOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeRight; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation PortraitUpsideDownAndLandscapeLeftOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortraitUpsideDown | UIInterfaceOrientationMaskLandscapeLeft; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + +@implementation PortraitUpsideDownAndLandscapeRightOnly + +- (NSUInteger)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortraitUpsideDown | UIInterfaceOrientationMaskLandscapeRight; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + return [self supportedInterfaceOrientations] & (1 << interfaceOrientation) ; +} + +- (BOOL)shouldAutorotate { + return YES; +} + +@end + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/Info.plist b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/Info.plist new file mode 100644 index 0000000..95c8add --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenLibTests/Info.plist @@ -0,0 +1,44 @@ + + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.apache.cordova.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.pbxproj b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ce820d8 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.pbxproj @@ -0,0 +1,505 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 7E9F51AB19DA10AE00DA31AC /* CDVSplashScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E9F51A919DA10AE00DA31AC /* CDVSplashScreen.m */; }; + 7E9F51B119DA114400DA31AC /* ImageNameTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E9F51B019DA114400DA31AC /* ImageNameTest.m */; }; + 7E9F51B319DA116500DA31AC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E9F51B219DA116500DA31AC /* Foundation.framework */; }; + 7E9F51B519DA127E00DA31AC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E9F51B419DA127E00DA31AC /* UIKit.framework */; }; + 7E9F51B819DA14FD00DA31AC /* ImageNameTestDelegates.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E9F51B719DA14FD00DA31AC /* ImageNameTestDelegates.m */; }; + 7E9F51B919DA1B1600DA31AC /* libCDVSplashScreenLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E9F519519DA102000DA31AC /* libCDVSplashScreenLib.a */; }; + 7E9F51BA19DA1B2000DA31AC /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E9F519019DA0F8300DA31AC /* libCordova.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7E9F518F19DA0F8300DA31AC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7E9F518B19DA0F8300DA31AC /* CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 68A32D7114102E1C006B237C; + remoteInfo = CordovaLib; + }; + 7E9F51AC19DA10DE00DA31AC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7E9F517219DA09CE00DA31AC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7E9F519419DA102000DA31AC; + remoteInfo = CDVSplashScreenLib; + }; + 7E9F51AE19DA10E100DA31AC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7E9F518B19DA0F8300DA31AC /* CordovaLib.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = D2AAC07D0554694100DB518D; + remoteInfo = CordovaLib; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7E9F519319DA102000DA31AC /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 7E9F518B19DA0F8300DA31AC /* CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CordovaLib.xcodeproj; path = "../node_modules/cordova-ios/CordovaLib/CordovaLib.xcodeproj"; sourceTree = ""; }; + 7E9F519519DA102000DA31AC /* libCDVSplashScreenLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCDVSplashScreenLib.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 7E9F519F19DA102000DA31AC /* CDVSplashScreenLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CDVSplashScreenLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7E9F51A219DA102000DA31AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7E9F51A919DA10AE00DA31AC /* CDVSplashScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVSplashScreen.m; path = ../../../src/ios/CDVSplashScreen.m; sourceTree = SOURCE_ROOT; }; + 7E9F51AA19DA10AE00DA31AC /* CDVSplashScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVSplashScreen.h; path = ../../../src/ios/CDVSplashScreen.h; sourceTree = SOURCE_ROOT; }; + 7E9F51B019DA114400DA31AC /* ImageNameTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageNameTest.m; sourceTree = ""; }; + 7E9F51B219DA116500DA31AC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 7E9F51B419DA127E00DA31AC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 7E9F51B619DA12C600DA31AC /* ImageNameTestDelegates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageNameTestDelegates.h; sourceTree = ""; }; + 7E9F51B719DA14FD00DA31AC /* ImageNameTestDelegates.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageNameTestDelegates.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7E9F519219DA102000DA31AC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7E9F519C19DA102000DA31AC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7E9F51BA19DA1B2000DA31AC /* libCordova.a in Frameworks */, + 7E9F51B919DA1B1600DA31AC /* libCDVSplashScreenLib.a in Frameworks */, + 7E9F51B519DA127E00DA31AC /* UIKit.framework in Frameworks */, + 7E9F51B319DA116500DA31AC /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7E9F517119DA09CE00DA31AC = { + isa = PBXGroup; + children = ( + 7E9F51B419DA127E00DA31AC /* UIKit.framework */, + 7E9F51B219DA116500DA31AC /* Foundation.framework */, + 7E9F518B19DA0F8300DA31AC /* CordovaLib.xcodeproj */, + 7E9F519619DA102000DA31AC /* CDVSplashScreenLib */, + 7E9F51A019DA102000DA31AC /* CDVSplashScreenLibTests */, + 7E9F517D19DA0A0A00DA31AC /* Products */, + ); + sourceTree = ""; + }; + 7E9F517D19DA0A0A00DA31AC /* Products */ = { + isa = PBXGroup; + children = ( + 7E9F519519DA102000DA31AC /* libCDVSplashScreenLib.a */, + 7E9F519F19DA102000DA31AC /* CDVSplashScreenLibTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 7E9F518C19DA0F8300DA31AC /* Products */ = { + isa = PBXGroup; + children = ( + 7E9F519019DA0F8300DA31AC /* libCordova.a */, + ); + name = Products; + sourceTree = ""; + }; + 7E9F519619DA102000DA31AC /* CDVSplashScreenLib */ = { + isa = PBXGroup; + children = ( + 7E9F51A919DA10AE00DA31AC /* CDVSplashScreen.m */, + 7E9F51AA19DA10AE00DA31AC /* CDVSplashScreen.h */, + ); + path = CDVSplashScreenLib; + sourceTree = SOURCE_ROOT; + }; + 7E9F51A019DA102000DA31AC /* CDVSplashScreenLibTests */ = { + isa = PBXGroup; + children = ( + 7E9F51A119DA102000DA31AC /* Supporting Files */, + 7E9F51B019DA114400DA31AC /* ImageNameTest.m */, + 7E9F51B619DA12C600DA31AC /* ImageNameTestDelegates.h */, + 7E9F51B719DA14FD00DA31AC /* ImageNameTestDelegates.m */, + ); + path = CDVSplashScreenLibTests; + sourceTree = ""; + }; + 7E9F51A119DA102000DA31AC /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 7E9F51A219DA102000DA31AC /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7E9F519419DA102000DA31AC /* CDVSplashScreenLib */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7E9F51A319DA102000DA31AC /* Build configuration list for PBXNativeTarget "CDVSplashScreenLib" */; + buildPhases = ( + 7E9F519119DA102000DA31AC /* Sources */, + 7E9F519219DA102000DA31AC /* Frameworks */, + 7E9F519319DA102000DA31AC /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CDVSplashScreenLib; + productName = CDVSplashScreenLib; + productReference = 7E9F519519DA102000DA31AC /* libCDVSplashScreenLib.a */; + productType = "com.apple.product-type.library.static"; + }; + 7E9F519E19DA102000DA31AC /* CDVSplashScreenLibTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7E9F51A619DA102000DA31AC /* Build configuration list for PBXNativeTarget "CDVSplashScreenLibTests" */; + buildPhases = ( + 7E9F519B19DA102000DA31AC /* Sources */, + 7E9F519C19DA102000DA31AC /* Frameworks */, + 7E9F519D19DA102000DA31AC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7E9F51AF19DA10E100DA31AC /* PBXTargetDependency */, + 7E9F51AD19DA10DE00DA31AC /* PBXTargetDependency */, + ); + name = CDVSplashScreenLibTests; + productName = CDVSplashScreenLibTests; + productReference = 7E9F519F19DA102000DA31AC /* CDVSplashScreenLibTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7E9F517219DA09CE00DA31AC /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0600; + TargetAttributes = { + 7E9F519419DA102000DA31AC = { + CreatedOnToolsVersion = 6.0; + }; + 7E9F519E19DA102000DA31AC = { + CreatedOnToolsVersion = 6.0; + }; + }; + }; + buildConfigurationList = 7E9F517519DA09CE00DA31AC /* Build configuration list for PBXProject "CDVSplashScreenTest" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7E9F517119DA09CE00DA31AC; + productRefGroup = 7E9F517D19DA0A0A00DA31AC /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 7E9F518C19DA0F8300DA31AC /* Products */; + ProjectRef = 7E9F518B19DA0F8300DA31AC /* CordovaLib.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 7E9F519419DA102000DA31AC /* CDVSplashScreenLib */, + 7E9F519E19DA102000DA31AC /* CDVSplashScreenLibTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 7E9F519019DA0F8300DA31AC /* libCordova.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libCordova.a; + remoteRef = 7E9F518F19DA0F8300DA31AC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 7E9F519D19DA102000DA31AC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7E9F519119DA102000DA31AC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7E9F51AB19DA10AE00DA31AC /* CDVSplashScreen.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7E9F519B19DA102000DA31AC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7E9F51B119DA114400DA31AC /* ImageNameTest.m in Sources */, + 7E9F51B819DA14FD00DA31AC /* ImageNameTestDelegates.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7E9F51AD19DA10DE00DA31AC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7E9F519419DA102000DA31AC /* CDVSplashScreenLib */; + targetProxy = 7E9F51AC19DA10DE00DA31AC /* PBXContainerItemProxy */; + }; + 7E9F51AF19DA10E100DA31AC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CordovaLib; + targetProxy = 7E9F51AE19DA10E100DA31AC /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 7E9F517619DA09CE00DA31AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + 7E9F517719DA09CE00DA31AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + 7E9F51A419DA102000DA31AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"", + "\"$(OBJROOT)/UninstalledProducts/include\"", + "\"$(BUILT_PRODUCTS_DIR)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 7E9F51A519DA102000DA31AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"", + "\n\"$(OBJROOT)/UninstalledProducts/include\"\n\"$(BUILT_PRODUCTS_DIR)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7E9F51A719DA102000DA31AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = CDVSplashScreenLibTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 7E9F51A819DA102000DA31AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = CDVSplashScreenLibTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7E9F517519DA09CE00DA31AC /* Build configuration list for PBXProject "CDVSplashScreenTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7E9F517619DA09CE00DA31AC /* Debug */, + 7E9F517719DA09CE00DA31AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7E9F51A319DA102000DA31AC /* Build configuration list for PBXNativeTarget "CDVSplashScreenLib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7E9F51A419DA102000DA31AC /* Debug */, + 7E9F51A519DA102000DA31AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7E9F51A619DA102000DA31AC /* Build configuration list for PBXNativeTarget "CDVSplashScreenLibTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7E9F51A719DA102000DA31AC /* Debug */, + 7E9F51A819DA102000DA31AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7E9F517219DA09CE00DA31AC /* Project object */; +} diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..8f91278 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout new file mode 100644 index 0000000..7e4cdb9 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/project.xcworkspace/xcshareddata/CDVSplashScreenTest.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + 6BE9AD73-1B9F-4362-98D7-DC631BEC6185 + IDESourceControlProjectName + CDVSplashScreenTest + IDESourceControlProjectOriginsDictionary + + BEF5A5D0FF64801E558286389440357A9233D7DB + https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git + + IDESourceControlProjectPath + tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + BEF5A5D0FF64801E558286389440357A9233D7DB + ../../../../.. + + IDESourceControlProjectURL + https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + BEF5A5D0FF64801E558286389440357A9233D7DB + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + BEF5A5D0FF64801E558286389440357A9233D7DB + IDESourceControlWCCName + cordova-plugin-splashscreen + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLib.xcscheme b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLib.xcscheme new file mode 100644 index 0000000..b97b863 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLib.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLibTests.xcscheme b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLibTests.xcscheme new file mode 100644 index 0000000..6a2a526 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/CDVSplashScreenTest/CDVSplashScreenTest.xcodeproj/xcshareddata/xcschemes/CDVSplashScreenLibTests.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/README.md new file mode 100644 index 0000000..97ee9df --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/README.md @@ -0,0 +1,40 @@ + + +# iOS Tests for CDVSplashScreen + +You need to install `node.js` to pull in `cordova-ios`. + +First install cordova-ios: + + npm install + +... in the current folder. + + +# Testing from Xcode + +1. Launch the `CDVSplashScreenTest.xcworkspace` file. +2. Choose "CDVSplashScreenLibTests" from the scheme drop-down menu +3. Click and hold on the `Play` button, and choose the `Wrench` icon to run the tests + + +# Testing from the command line + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/de/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/de/README.md new file mode 100644 index 0000000..9c7f0a4 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/de/README.md @@ -0,0 +1,39 @@ + + +# iOS-Tests für CDVSplashScreen + +Sie müssen installieren `node.js` in `Cordova-Ios` zu ziehen. + +Installieren Sie Cordova-Ios zum ersten Mal: + + npm install + + +... im aktuellen Ordner. + +# Testen von Xcode + + 1. Starten Sie die Datei `CDVSplashScreenTest.xcworkspace` . + 2. Wählen Sie im Dropdown-Schema "CDVSplashScreenLibTests" + 3. Klicken Sie und halten Sie auf den `Play` -Button und wählen Sie das `Schraubenschlüssel` -Symbol zum Ausführen der tests + +# Tests von der Befehlszeile aus + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/es/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/es/README.md new file mode 100644 index 0000000..2176c92 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/es/README.md @@ -0,0 +1,39 @@ + + +# Pruebas de iOS para CDVSplashScreen + +Necesita instalar `node.js` en `Córdoba-ios`. + +Primero instalar cordova-ios: + + npm install + + +... en la carpeta actual. + +# Prueba de Xcode + + 1. Iniciar el archivo `CDVSplashScreenTest.xcworkspace` . + 2. Elija "CDVSplashScreenLibTests" en el menú de lista desplegable esquema + 3. Haga clic y mantenga el botón de `Play` y elegir el icono de `llave inglesa` para ejecutar las pruebas + +# Pruebas desde la línea de comandos + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/fr/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/fr/README.md new file mode 100644 index 0000000..0dbbd0d --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/fr/README.md @@ -0,0 +1,39 @@ + + +# Tests d'iOS pour CDVSplashScreen + +Vous devez installer `node.js` à `cordova-ios`. + +Commencez par installer cordova-ios : + + npm install + + +... dans le dossier actuel. + +# Tests de Xcode + + 1. Lancez le fichier `CDVSplashScreenTest.xcworkspace` . + 2. Choisissez « CDVSplashScreenLibTests » dans le menu déroulant de régime + 3. Cliquez et maintenez sur la touche `Play` et cliquez sur l'icône de `clé` pour exécuter les tests + +# Test de la ligne de commande + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/it/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/it/README.md new file mode 100644 index 0000000..2a42df6 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/it/README.md @@ -0,0 +1,39 @@ + + +# Test di iOS per CDVSplashScreen + +È necessario installare `node. js` per tirare in `cordova-ios`. + +In primo luogo installare cordova-ios: + + npm install + + +... nella cartella corrente. + +# Test da Xcode + + 1. Lanciare il file `CDVSplashScreenTest.xcworkspace` . + 2. Scegli "CDVSplashScreenLibTests" dal menu a discesa Schema + 3. Fare clic e tenere premuto il pulsante `Play` e scegliere l'icona della `chiave inglese` per eseguire i test + +# Test dalla riga di comando + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/ja/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/ja/README.md new file mode 100644 index 0000000..011b824 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/ja/README.md @@ -0,0 +1,39 @@ + + +# CDVSplashScreen ã® iOS ã®ãƒ†ã‚¹ãƒˆ + +`Node.js` `コルドãƒ`ios をプルã™ã‚‹ã‚’インストールã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚. + +コルドムios をインストールã—ã¾ã™ã€‚ + + npm install + + +ç¾åœ¨ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã«. + +# Xcode ã‹ã‚‰ãƒ†ã‚¹ãƒˆ + + 1. `CDVSplashScreenTest.xcworkspace`ファイルを起動ã—ã¾ã™ã€‚ + 2. スキーム] ドロップダウン メニューã‹ã‚‰"CDVSplashScreenLibTests"ã‚’é¸æŠžã—ã¾ã™ã€‚ + 3. クリックã—ã€`å†ç”Ÿ`ボタンを押ã—ã€ãƒ†ã‚¹ãƒˆã‚’実行ã™ã‚‹`レンãƒ`ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’é¸æŠž + +# コマンドラインã‹ã‚‰ãƒ†ã‚¹ãƒˆ + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/ko/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/ko/README.md new file mode 100644 index 0000000..6981207 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/ko/README.md @@ -0,0 +1,39 @@ + + +# CDVSplashScreenì— ëŒ€ í•œ iOS 테스트 + +`Node.js` `코르ë„ë°”` iosì—서를 설치 해야. + +코르ë„ë°”-ios를 설치 하는 첫번째는: + + npm install + + +현재 í´ë”ì—.... + +# Xcodeì—ì„œ 테스트 + + 1. `CDVSplashScreenTest.xcworkspace` 파ì¼ì„ 시작 합니다. + 2. 구성표 드롭 다운 메뉴ì—ì„œ "CDVSplashScreenLibTests"를 ì„ íƒ + 3. í´ë¦­ 하 ê³  `재ìƒ` 버튼ì—는 테스트를 실행 하려면 `공구 모양` ì•„ì´ì½˜ì„ ì„ íƒ + +# 명령줄ì—ì„œ 테스트 + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/pl/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/pl/README.md new file mode 100644 index 0000000..f13828f --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/pl/README.md @@ -0,0 +1,39 @@ + + +# iOS testy dla CDVSplashScreen + +Musisz zainstalować `node.js` ciÄ…gnąć w `cordova-ios`. + +Najpierw zainstalować cordova-ios: + + npm install + + +... w folderze bieżącym. + +# Badania z Xcode + + 1. Uruchom plik `CDVSplashScreenTest.xcworkspace` . + 2. Wybierz z menu rozwijanego systemu "CDVSplashScreenLibTests" + 3. Kliknij i przytrzymaj przycisk `Play` i wybrać ikonÄ™ `klucz` do testów + +# Badania z wiersza polecenia + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/doc/zh/README.md b/plugins/cordova-plugin-splashscreen/tests/ios/doc/zh/README.md new file mode 100644 index 0000000..3a04bcd --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/doc/zh/README.md @@ -0,0 +1,39 @@ + + +# CDVSplashScreen çš„ iOS 測試 + +您需è¦å®‰è£`node.js`拉`科爾多瓦 ios`中. + +第一次安è£ç§‘爾多瓦 ios: + + npm install + + +在當å‰è³‡æ–™å¤¾ä¸­ã€‚ + +# 從 Xcode 測試 + + 1. å•Ÿå‹•`CDVSplashScreenTest.xcworkspace`檔。 + 2. 從方案下拉å¼åŠŸèƒ½è¡¨ä¸­é¸æ“‡"CDVSplashScreenLibTests" + 3. 按一下並堅æŒ`播放`按鈕,然後é¸æ“‡è¦é‹è¡Œçš„測試的`扳手`圖示 + +# 從命令列測試 + + npm test diff --git a/plugins/cordova-plugin-splashscreen/tests/ios/package.json b/plugins/cordova-plugin-splashscreen/tests/ios/package.json new file mode 100644 index 0000000..67f0edc --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/ios/package.json @@ -0,0 +1,13 @@ +{ + "name": "cordova-plugin-splashscreen-test-ios", + "version": "1.0.0", + "description": "iOS Unit Tests for Splashscreen Plugin", + "author": "Apache Software Foundation", + "license": "Apache Version 2.0", + "dependencies": { + "cordova-ios": "*" + }, + "scripts": { + "test": "xcodebuild test -workspace CDVSplashScreenTest.xcworkspace -scheme CDVSplashScreenLibTests -destination 'platform=iOS Simulator,name=iPhone 5' CONFIGURATION_BUILD_DIR='/tmp' HEADER_SEARCH_PATHS='$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include'" + } +} diff --git a/plugins/cordova-plugin-splashscreen/tests/package.json b/plugins/cordova-plugin-splashscreen/tests/package.json new file mode 100644 index 0000000..199a6f5 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/package.json @@ -0,0 +1,14 @@ +{ + "name": "cordova-plugin-splashscreen-tests", + "version": "4.0.3-dev", + "description": "", + "cordova": { + "id": "cordova-plugin-splashscreen-tests", + "platforms": [] + }, + "keywords": [ + "ecosystem:cordova" + ], + "author": "", + "license": "Apache 2.0" +} diff --git a/plugins/cordova-plugin-splashscreen/tests/plugin.xml b/plugins/cordova-plugin-splashscreen/tests/plugin.xml new file mode 100644 index 0000000..60d7084 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/plugin.xml @@ -0,0 +1,29 @@ + + + + + Cordova Splashscreen Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-splashscreen/tests/tests.js b/plugins/cordova-plugin-splashscreen/tests/tests.js new file mode 100644 index 0000000..7b55a81 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/tests/tests.js @@ -0,0 +1,64 @@ +/* + * + * 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. + * +*/ + +/* jshint jasmine: true */ + +exports.defineAutoTests = function () { + describe('Splashscreen (cordova)', function () { + it("splashscreen.spec.1 should exist", function () { + expect(navigator.splashscreen).toBeDefined(); + }); + + it("splashscreen.spec.2 show method should exist", function () { + expect(navigator.splashscreen.show).toBeDefined(); + expect(typeof navigator.splashscreen.show).toBe('function'); + }); + + it("splashscreen.spec.3 hide method should exist", function () { + expect(navigator.splashscreen.hide).toBeDefined(); + expect(typeof navigator.splashscreen.hide).toBe('function'); + }); + }); +}; + +exports.defineManualTests = function (contentEl, createActionButton) { + function showFor(duration) { + navigator.splashscreen.show(); + window.setTimeout(function () { + navigator.splashscreen.hide(); + }, 1000 * duration); + } + + contentEl.innerHTML = '

Splashscreen Tests

' + + '

Note for WP: AutoHideSplashScreen must be set to false in config.xml

' + + '
' + + 'Expected result: Will show the Cordova splashscreen for 1 second' + + '

' + + 'Expected result: Will show the Cordova splashscreen for 5 seconds'; + + createActionButton('Show for 1 second', function () { + showFor(1); + }, 'show1'); + + createActionButton('Show for 5 seconds', function () { + showFor(5); + }, 'show5'); +}; diff --git a/plugins/cordova-plugin-splashscreen/types/index.d.ts b/plugins/cordova-plugin-splashscreen/types/index.d.ts new file mode 100644 index 0000000..968b340 --- /dev/null +++ b/plugins/cordova-plugin-splashscreen/types/index.d.ts @@ -0,0 +1,17 @@ +// Type definitions for Apache Cordova Splashscreen plugin +// Project: https://github.com/apache/cordova-plugin-splashscreen +// Definitions by: Microsoft Open Technologies Inc +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// +// Copyright (c) Microsoft Open Technologies Inc +// Licensed under the MIT license. + +interface Navigator { + /** This plugin displays and hides a splash screen during application launch. */ + splashscreen: { + /** Dismiss the splash screen. */ + hide(): void; + /** Displays the splash screen. */ + show(): void; + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/CONTRIBUTING.md b/plugins/cordova-plugin-statusbar/CONTRIBUTING.md new file mode 100644 index 0000000..4c8e6a5 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# Contributing to Apache Cordova + +Anyone can contribute to Cordova. And we need your contributions. + +There are multiple ways to contribute: report bugs, improve the docs, and +contribute code. + +For instructions on this, start with the +[contribution overview](http://cordova.apache.org/contribute/). + +The details are explained there, but the important items are: + - Sign and submit an Apache ICLA (Contributor License Agreement). + - Have a Jira issue open that corresponds to your contribution. + - Run the tests so your patch doesn't break existing functionality. + +We look forward to your contributions! diff --git a/plugins/cordova-plugin-statusbar/LICENSE b/plugins/cordova-plugin-statusbar/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-statusbar/LICENSE @@ -0,0 +1,202 @@ + + 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. \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/NOTICE b/plugins/cordova-plugin-statusbar/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/NOTICE @@ -0,0 +1,5 @@ +Apache Cordova +Copyright 2012 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/cordova-plugin-statusbar/README.md b/plugins/cordova-plugin-statusbar/README.md new file mode 100644 index 0000000..d6bf29c --- /dev/null +++ b/plugins/cordova-plugin-statusbar/README.md @@ -0,0 +1,332 @@ +--- +title: Statusbar +description: Control the device status bar. +--- + + +|AppVeyor|Travis CI| +|:-:|:-:| +|[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-plugin-statusbar?branch=master)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-statusbar)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-statusbar)| + +# cordova-plugin-statusbar + +StatusBar +====== + +> The `StatusBar` object provides some functions to customize the iOS and Android StatusBar. + +:warning: Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22cordova-plugin-statusbar%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + + +## Installation + +This installation method requires cordova 5.0+ + + cordova plugin add cordova-plugin-statusbar +Older versions of cordova can still install via the __deprecated__ id + + cordova plugin add org.apache.cordova.statusbar +It is also possible to install via repo url directly ( unstable ) + + cordova plugin add https://github.com/apache/cordova-plugin-statusbar.git + + +Preferences +----------- + +#### config.xml + +- __StatusBarOverlaysWebView__ (boolean, defaults to true). On iOS 7, make the statusbar overlay or not overlay the WebView at startup. + + + +- __StatusBarBackgroundColor__ (color hex string, no default value). On iOS 7, set the background color of the statusbar by a hex string (#RRGGBB) at startup. If this value is not set, the background color will be transparent. + + + +- __StatusBarStyle__ (status bar style, defaults to lightcontent). On iOS 7, set the status bar style. Available options default, lightcontent, blacktranslucent, blackopaque. + + + +- __StatusBarDefaultScrollToTop__ (boolean, defaults to false). On iOS 7, allows the Cordova WebView to use default scroll-to-top behavior. Defaults to false so you can listen to the "statusTap" event (described below) and customize the behavior instead. + + + +### Android Quirks +The Android 5+ guidelines specify using a different color for the statusbar than your main app color (unlike the uniform statusbar color of many iOS 7+ apps), so you may want to set the statusbar color at runtime instead via `StatusBar.backgroundColorByHexString` or `StatusBar.backgroundColorByName`. One way to do that would be: +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +Hiding at startup +----------- + +During runtime you can use the StatusBar.hide function below, but if you want the StatusBar to be hidden at app startup, you must modify your app's Info.plist file. + +Add/edit these two attributes if not present. Set **"Status bar is initially hidden"** to **"YES"** and set **"View controller-based status bar appearance"** to **"NO"**. If you edit it manually without Xcode, the keys and values are: + + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +Methods +------- +This plugin defines global `StatusBar` object. + +Although in the global scope, it is not available until after the `deviceready` event. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + +- StatusBar.overlaysWebView +- StatusBar.styleDefault +- StatusBar.styleLightContent +- StatusBar.styleBlackTranslucent +- StatusBar.styleBlackOpaque +- StatusBar.backgroundColorByName +- StatusBar.backgroundColorByHexString +- StatusBar.hide +- StatusBar.show + +Properties +-------- + +- StatusBar.isVisible + +Events +------ + +- statusTap + +StatusBar.overlaysWebView +================= + +On iOS 7, make the statusbar overlay or not overlay the WebView. + + StatusBar.overlaysWebView(true); + +Description +----------- + +On iOS 7, set to false to make the statusbar appear like iOS 6. Set the style and background color to suit using the other functions. + + +Supported Platforms +------------------- + +- iOS + +Quick Example +------------- + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + +StatusBar.styleDefault +================= + +Use the default statusbar (dark text, for light backgrounds). + + StatusBar.styleDefault(); + + +Supported Platforms +------------------- + +- iOS +- Android 6+ +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + +StatusBar.styleLightContent +================= + +Use the lightContent statusbar (light text, for dark backgrounds). + + StatusBar.styleLightContent(); + + +Supported Platforms +------------------- + +- iOS +- Android 6+ +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + +StatusBar.styleBlackTranslucent +================= + +Use the blackTranslucent statusbar (light text, for dark backgrounds). + + StatusBar.styleBlackTranslucent(); + + +Supported Platforms +------------------- + +- iOS +- Android 6+ +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + +StatusBar.styleBlackOpaque +================= + +Use the blackOpaque statusbar (light text, for dark backgrounds). + + StatusBar.styleBlackOpaque(); + + +Supported Platforms +------------------- + +- iOS +- Android 6+ +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + + +StatusBar.backgroundColorByName +================= + +On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false, you can set the background color of the statusbar by color name. + + StatusBar.backgroundColorByName("red"); + +Supported color names are: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +Supported Platforms +------------------- + +- iOS +- Android 5+ +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + +StatusBar.backgroundColorByHexString +================= + +Sets the background color of the statusbar by a hex string. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + +CSS shorthand properties are also supported. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + +On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false, you can set the background color of the statusbar by a hex string (#RRGGBB). + +On WP7 and WP8 you can also specify values as #AARRGGBB, where AA is an alpha value + +Supported Platforms +------------------- + +- iOS +- Android 5+ +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + +StatusBar.hide +================= + +Hide the statusbar. + + StatusBar.hide(); + + +Supported Platforms +------------------- + +- iOS +- Android +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + +StatusBar.show +================= + +Shows the statusbar. + + StatusBar.show(); + + +Supported Platforms +------------------- + +- iOS +- Android +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + + +StatusBar.isVisible +================= + +Read this property to see if the statusbar is visible or not. + + if (StatusBar.isVisible) { + // do something + } + + +Supported Platforms +------------------- + +- iOS +- Android +- Windows Phone 7 +- Windows Phone 8 +- Windows Phone 8.1 + + +statusTap +========= + +Listen for this event to know if the statusbar was tapped. + + window.addEventListener('statusTap', function() { + // scroll-up with document.body.scrollTop = 0; or do whatever you want + }); + + +Supported Platforms +------------------- + +- iOS diff --git a/plugins/cordova-plugin-statusbar/RELEASENOTES.md b/plugins/cordova-plugin-statusbar/RELEASENOTES.md new file mode 100644 index 0000000..e79187c --- /dev/null +++ b/plugins/cordova-plugin-statusbar/RELEASENOTES.md @@ -0,0 +1,172 @@ + +# Release Notes + +### 2.4.2 (Apr 12, 2018) +* [CB-12679](https://issues.apache.org/jira/browse/CB-12679) Remove Permissions section + +### 2.4.1 (Dec 27, 2017) +* [CB-13712](https://issues.apache.org/jira/browse/CB-13712) (iOS): fix overlaysWebView reset on rotation (#92) + +### 2.4.0 (Dec 15, 2017) +* [CB-13623](https://issues.apache.org/jira/browse/CB-13623) (iOS): Remove **iOS** 6-7 code + +### 2.3.0 (Nov 06, 2017) +* [CB-13476](https://issues.apache.org/jira/browse/CB-13476) (iOS): handle double size statusbar on SDK 10 for **iOS 11** +* [CB-13394](https://issues.apache.org/jira/browse/CB-13394) (iOS): fix `iPhone X` StatusBar rendering in landscape +* [CB-11858](https://issues.apache.org/jira/browse/CB-11858) (android) Add `StatusBarStyle` feature support for **Android M+** +* [CB-13311](https://issues.apache.org/jira/browse/CB-13311) (iOS) Statusbar does not overlay correctly on `iPhone X` +* [CB-13028](https://issues.apache.org/jira/browse/CB-13028) (CI) **Browser** builds on Travis and AppVeyor +* [CB-12812](https://issues.apache.org/jira/browse/CB-12812) (browser) Fix statusbar plugin with **Browser** platform +* [CB-12847](https://issues.apache.org/jira/browse/CB-12847) added `bugs` entry to `package.json`. + +### 2.2.3 (Apr 27, 2017) +* [CB-12622](https://issues.apache.org/jira/browse/CB-12622) Added **Android 6.0** build badge to `README` +* [CB-10879](https://issues.apache.org/jira/browse/CB-10879) Enable overlaysWebView on **Android** API 21+ +* [CB-12685](https://issues.apache.org/jira/browse/CB-12685) added `package.json` to tests folder + +### 2.2.2 (Feb 28, 2017) +* [CB-12188](https://issues.apache.org/jira/browse/CB-12188) Status Bar is not changing in some specific **Android** phone (Red MI 3s Prime) +* [CB-12369](https://issues.apache.org/jira/browse/CB-12369) Add plugin typings from `DefinitelyTyped` +* [CB-12363](https://issues.apache.org/jira/browse/CB-12363) Added build badges for **iOS 9.3** and **iOS 10.0** +* [CB-12196](https://issues.apache.org/jira/browse/CB-12196) **iOS** fix Status Bar Not Hiding +* [CB-12141](https://issues.apache.org/jira/browse/CB-12141) **iOS** fix white app screen after camera overlay shown on iPad +* [CB-12230](https://issues.apache.org/jira/browse/CB-12230) Removed **Windows 8.1** build badges + +### 2.2.1 (Dec 07, 2016) +* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 2.2.1 +* [CB-10288](https://issues.apache.org/jira/browse/CB-10288) statusbar plugin interaction with iOS multitasking +* [CB-10158](https://issues.apache.org/jira/browse/CB-10158) (ios) fix StatusBar issue when recovering from fullscreen video +* [CB-10341](https://issues.apache.org/jira/browse/CB-10341) ios, document statusTap event +* [CB-11191](https://issues.apache.org/jira/browse/CB-11191) Statusbar plugin causing issues with webview size +* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…" +* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version. + +### 2.2.0 (Sep 08, 2016) +* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies +* Handle extended status bar on **iOS** +* Plugin uses `Android Log class` and not `Cordova LOG class` +* [CB-11287](https://issues.apache.org/jira/browse/CB-11287) (**ios**) - fix webview resize after modal on **iPhones** +* [CB-11485](https://issues.apache.org/jira/browse/CB-11485) fix resize on rotation with popover +* Add badges for paramedic builds on Jenkins +* [CB-11197](https://issues.apache.org/jira/browse/CB-11197) Keep status bar hidden when keyboard pops up +* Add pull request template. +* [CB-10866](https://issues.apache.org/jira/browse/CB-10866) Adding engine info to `package.json` +* patched missing `_ready` method, and changed the way the proxy is installed +* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to `README.md` + +### 2.1.3 (Apr 15, 2016) +* [CB-11018](https://issues.apache.org/jira/browse/CB-11018) Fix statusbar with `inappbrowser` causing incorrect orientation on **iOS8** +* [CB-10884](https://issues.apache.org/jira/browse/CB-10884) `Inappbrowser` breaks UI while Screen orientation changes from landscape to portrait on **iOS** + +### 2.1.2 (Mar 09, 2016) +* [CB-10752](https://issues.apache.org/jira/browse/CB-10752) for for status bar overlays the webview on **iOS** 6 in some cases +* [CB-10683](https://issues.apache.org/jira/browse/CB-10683) Fix wrong StatusBar.isVisible initial value on **Windows** +* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add JSHint for plugins +* [CB-10047](https://issues.apache.org/jira/browse/CB-10047) fix **iOS** 8 deprecated warnings + +### 2.1.1 (Feb 09, 2016) +* [CB-10102](https://issues.apache.org/jira/browse/CB-10102) The removeObserver code was wrong and it might crash on plugin deallocation + +### 2.1.0 (Jan 15, 2016) +* [CB-9513](https://issues.apache.org/jira/browse/CB-9513) Allow to show/hide status bar in fullscreen mode. +* [CB-8720](https://issues.apache.org/jira/browse/CB-8720) Fix status bar position when app started upside down on **iOS 7**. +* [CB-10118](https://issues.apache.org/jira/browse/CB-10118) Fixes plugin loading error for **Browser** platform + +### 2.0.0 (Nov 18, 2015) +* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest +* Added `weakSelf` reference for block use +* Fixes [CB-4712](https://issues.apache.org/jira/browse/CB-4712), [CB-5439](https://issues.apache.org/jira/browse/CB-5439) statusbar issues +* Fixing contribute link. +* [CB-7965](https://issues.apache.org/jira/browse/CB-7965) Add cordova-plugin-statusbar support for **Browser** platform +* Don't use `IsAtLeastiOSVersion` macro to determine height +* Use correct statusbar height for landscape orientation in iOS >= 8 +* remove travis-ci +* [CB-9202](https://issues.apache.org/jira/browse/CB-9202) updated repo url to github mirror in package.json +* Added verbose install text for users on < cordova 5.0 +* update docs for `StatusBarBackgroundColor` + +### 1.0.1 (Jun 17, 2015) +* add auto-tests for basic api +* [CB-9180](https://issues.apache.org/jira/browse/CB-9180) Add correct supported check for **Windows 8.1** desktop +* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-statusbar documentation translation: cordova-plugin-statusbar +* fix npm md issue + +### 1.0.0 (Apr 15, 2015) +* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) gave plugin major version bump +* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) changed plugin-id to pacakge-name +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) properly updated translated docs to use new id +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) updated translated docs to use new id +* Use TRAVIS_BUILD_DIR, install paramedic by npm +* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme +* - Use StatusBarBackgroundColor instead of AndroidStatusBarBackgroundColor, and added a quirk to the readme. +* - Add support for StatusBar.backgroundColorByHexString (and StatusBar.backgroundColorByName) on Android 5 and up +* Allow setting the statusbar backgroundcolor on Android +* [CB-8575](https://issues.apache.org/jira/browse/CB-8575) Integrate TravisCI +* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-statusbar documentation translation: cordova-plugin-statusbar +* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file + +### 0.1.10 (Feb 04, 2015) +* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension + +### 0.1.9 (Dec 02, 2014) +* Fix onload attribute within to be a +* [CB-8010](https://issues.apache.org/jira/browse/CB-8010) - Statusbar colour does not change to orange +* added checks for running on windows when StatusBar is NOT available +* [CB-7986](https://issues.apache.org/jira/browse/CB-7986) Add cordova-plugin-statusbar support for **Windows Phone 8.1** +* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs +* [CB-7979](https://issues.apache.org/jira/browse/CB-7979) Each plugin doc should have a ## Installation section +* Inserting leading space after # for consistency +* [CB-7549](https://issues.apache.org/jira/browse/CB-7549) - (Re-fix) `StatusBar` **iOS 8** Landscape issue (closes #15) +* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-statusbar documentation translation: cordova-plugin-statusbar +* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin + +### 0.1.8 (Sep 17, 2014) +* [CB-7549](https://issues.apache.org/jira/browse/CB-7549) [StatusBar][iOS 8] Landscape issue +* [CB-7486](https://issues.apache.org/jira/browse/CB-7486) Remove StatusBarBackgroundColor intial preference (black background) so background will be initially transparent +* Renamed test dir, added nested plugin.xml +* added documentation for manual tests, moved background color test below overlay test +* [CB-7195](https://issues.apache.org/jira/browse/CB-7195) ported statusbar tests to framework + +### 0.1.7 (Aug 06, 2014) +* Add LICENSE and NOTICE +* Update statusbar.js +* Update backgroundColorByHexString function +* ios: Use a persistent callbackId instead of calling sendJs +* [CB-6626](https://issues.apache.org/jira/browse/CB-6626) ios: Add a JS event for tapping on statusbar +* ios: Fix hide to adjust webview's frame only when status bar is not overlaying webview +* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs +* android: Fix StatusBar.initialize() not running on UI thread + +### 0.1.6 (Jun 05, 2014) +* [CB-6783](https://issues.apache.org/jira/browse/CB-6783) - added StatusBarStyle config preference, updated docs (closes #9) +* [CB-6812](https://issues.apache.org/jira/browse/CB-6812) Add license +* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md +* [CB-6264](https://issues.apache.org/jira/browse/CB-6264) minor formatting issue +* Update docs with recent WP changes, remove 'clear' from the loist of named colors in documentation +* [CB-6513](https://issues.apache.org/jira/browse/CB-6513) - Statusbar plugin for Android is not compiling + +### 0.1.5 (Apr 17, 2014) (First release as a core Cordova Plugin) +* [CB-6316](https://issues.apache.org/jira/browse/CB-6316): Added README.md which point to the new location for docs +* [CB-6316](https://issues.apache.org/jira/browse/CB-6316): Added license header to the documentation. Added README.md which point to the new location for docs +* [CB-6316](https://issues.apache.org/jira/browse/CB-6316): Moved StatusBar plugin documentation to docs folder +* [CB-6314](https://issues.apache.org/jira/browse/CB-6314): [android] Add StatusBar.isVisible support to Android +* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers diff --git a/plugins/cordova-plugin-statusbar/doc/de/README.md b/plugins/cordova-plugin-statusbar/doc/de/README.md new file mode 100644 index 0000000..9247598 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/de/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> Das `StatusBar` Objekt stellt einige Funktionen zum Anpassen des iOS und Android StatusBar. + +## Installation + + cordova plugin add cordova-plugin-statusbar + + +## "Einstellungen" + +#### "config.xml" + + * **StatusBarOverlaysWebView** (Boolean, der Standardwert ist True). Stellen Sie auf iOS 7 die Statusbar-Overlay oder keine Ãœberlagerung der WebView beim Start. + + + + + * **StatusBarBackgroundColor** (Farbe hex String, Standardwert ist #000000). Auf iOS legen 7 und Android 5, Sie die Hintergrundfarbe der Statusbar von eine hexadezimale Zeichenfolge (#RRGGBB) beim Start. + + + + + * **StatusBarStyle** (Status Bar-Stil, der Standardwert ist Lightcontent). Legen Sie auf iOS 7 den Status-Bar-Stil. Verfügbaren Optionen Standard, Lightcontent, Blacktranslucent, Blackopaque. + + + + +### Android Eigenarten + +Die Android 5 + Leitlinien angeben, verwenden eine andere Farbe für die Statusbar als Ihre Hauptanwendung Farbe (anders als die einheitliche Statusbar Farbe viele iOS 7 + apps), so Sie die Statusbar Farbe zur Laufzeit statt über `StatusBar.backgroundColorByHexString` oder `StatusBar.backgroundColorByName`festzulegen möchten vielleicht. Eine Möglichkeit dazu wäre: + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## Beim Start ausblenden + +Während der Laufzeit können Sie die StatusBar.hide-Funktion unten, aber die StatusBar beim Start der app versteckt werden soll, müssen Sie Ihre app Info.plist Datei ändern. + +Diese beiden Attribute hinzufügen/bearbeiten, wenn nicht vorhanden. Legen Sie **"Statusleiste ist anfangs ausgeblendet"** auf **"YES"** und **"View Controller-basierte Status Bar aussehen"** auf **"NO"**. Wenn Sie es manuell ohne Xcode bearbeiten, werden die Schlüssel und Werte: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Methoden + +Dieses Plugin wird globales `StatusBar`-Objekt definiert. + +Obwohl im globalen Gültigkeitsbereich, steht es nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## Eigenschaften + + * StatusBar.isVisible + +## Berechtigungen + +#### "config.xml" + + + + + + +# StatusBar.overlaysWebView + +Stellen Sie auf iOS 7 Statusbar überlagern oder nicht überlagert die WebView. + + StatusBar.overlaysWebView(true); + + +## Beschreibung + +Auf iOS 7 zu der Statusbar wie iOS 6 erscheinen auf False festgelegt. Legen Sie die Stil und Hintergrund Farbe entsprechend mit den anderen Funktionen. + +## Unterstützte Plattformen + + * iOS + +## Kurzes Beispiel + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Verwenden Sie die Standard-Statusbar (dunkle Text, für helle Hintergründe). + + StatusBar.styleDefault(); + + +## Unterstützte Plattformen + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.styleLightContent + +Verwenden Sie die LightContent-Statusbar (heller Text, für dunkle Hintergründe). + + StatusBar.styleLightContent(); + + +## Unterstützte Plattformen + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.styleBlackTranslucent + +Verwenden Sie die BlackTranslucent-Statusbar (heller Text, für dunkle Hintergründe). + + StatusBar.styleBlackTranslucent(); + + +## Unterstützte Plattformen + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.styleBlackOpaque + +Verwenden Sie die BlackOpaque-Statusbar (heller Text, für dunkle Hintergründe). + + StatusBar.styleBlackOpaque(); + + +## Unterstützte Plattformen + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.backgroundColorByName + +Auf iOS 7 Wenn Sie StatusBar.statusBarOverlaysWebView auf False festlegen, können Sie die Hintergrundfarbe der Statusbar von Farbnamen festlegen. + + StatusBar.backgroundColorByName("red"); + + +Unterstützte Farbnamen sind: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Unterstützte Plattformen + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.backgroundColorByHexString + +Legt die Hintergrundfarbe der Statusbar von eine hexadezimale Zeichenfolge fest. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +CSS-Kurzschrift-Eigenschaften werden ebenfalls unterstützt. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Auf iOS 7 Wenn Sie StatusBar.statusBarOverlaysWebView auf False festlegen, können Sie die Hintergrundfarbe der Statusbar von eine hexadezimale Zeichenfolge (#RRGGBB) festlegen. + +Auf WP7 und WP8 können Sie auch Werte wie #AARRGGBB, angeben wo AA einen alpha-Wert ist + +## Unterstützte Plattformen + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.hide + +Ausblenden der Statusleiste. + + StatusBar.hide(); + + +## Unterstützte Plattformen + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.show + +Zeigt die Statusleiste. + + StatusBar.show(); + + +## Unterstützte Plattformen + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 + +# StatusBar.isVisible + +Lesen Sie diese Eigenschaft, um festzustellen, ob die Statusbar sichtbar oder nicht ist. + + if (StatusBar.isVisible) { + // do something + } + + +## Unterstützte Plattformen + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone-8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/de/index.md b/plugins/cordova-plugin-statusbar/doc/de/index.md new file mode 100644 index 0000000..9f913c5 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/de/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> Das `StatusBar` Objekt stellt einige Funktionen zum Anpassen des iOS und Android StatusBar. + +## Installation + + cordova plugin add cordova-plugin-statusbar + + +## "Einstellungen" + +#### config.xml + +* **StatusBarOverlaysWebView** (Boolean, der Standardwert ist True). Stellen Sie auf iOS 7 die Statusbar-Overlay oder keine Ãœberlagerung der WebView beim Start. + + + + +* **StatusBarBackgroundColor** (Farbe hex String, der Standardwert ist #000000). Legen Sie auf iOS 7 die Hintergrundfarbe der Statusbar von eine hexadezimale Zeichenfolge (#RRGGBB) beim Start. + + + + +* **StatusBarStyle** (Status Bar-Stil, der Standardwert ist Lightcontent). Legen Sie auf iOS 7 den Status-Bar-Stil. Verfügbaren Optionen Standard, Lightcontent, Blacktranslucent, Blackopaque. + + + + +## Beim Start ausblenden + +Während der Laufzeit können Sie die StatusBar.hide-Funktion unten, aber die StatusBar beim Start der app versteckt werden soll, müssen Sie Ihre app Info.plist Datei ändern. + +Diese beiden Attribute hinzufügen/bearbeiten, wenn nicht vorhanden. Legen Sie **"Statusleiste ist anfangs ausgeblendet"** auf **"YES"** und **"View Controller-basierte Status Bar aussehen"** auf **"NO"**. Wenn Sie es manuell ohne Xcode bearbeiten, werden die Schlüssel und Werte: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Methoden + +Dieses Plugin wird globales `StatusBar`-Objekt definiert. + +Obwohl im globalen Gültigkeitsbereich, steht es nicht bis nach dem `deviceready`-Ereignis. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## Eigenschaften + +* StatusBar.isVisible + +## Berechtigungen + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +Stellen Sie auf iOS 7 Statusbar überlagern oder nicht überlagert die WebView. + + StatusBar.overlaysWebView(true); + + +## Beschreibung + +Auf iOS 7 zu der Statusbar wie iOS 6 erscheinen auf False festgelegt. Legen Sie die Stil und Hintergrund Farbe entsprechend mit den anderen Funktionen. + +## Unterstützte Plattformen + +* iOS + +## Kurzes Beispiel + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Verwenden Sie die Standard-Statusbar (dunkle Text, für helle Hintergründe). + + StatusBar.styleDefault(); + + +## Unterstützte Plattformen + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.styleLightContent + +Verwenden Sie die LightContent-Statusbar (heller Text, für dunkle Hintergründe). + + StatusBar.styleLightContent(); + + +## Unterstützte Plattformen + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.styleBlackTranslucent + +Verwenden Sie die BlackTranslucent-Statusbar (heller Text, für dunkle Hintergründe). + + StatusBar.styleBlackTranslucent(); + + +## Unterstützte Plattformen + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.styleBlackOpaque + +Verwenden Sie die BlackOpaque-Statusbar (heller Text, für dunkle Hintergründe). + + StatusBar.styleBlackOpaque(); + + +## Unterstützte Plattformen + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.backgroundColorByName + +Auf iOS 7 Wenn Sie StatusBar.statusBarOverlaysWebView auf False festlegen, können Sie die Hintergrundfarbe der Statusbar von Farbnamen festlegen. + + StatusBar.backgroundColorByName("red"); + + +Unterstützte Farbnamen sind: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Unterstützte Plattformen + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.backgroundColorByHexString + +Legt die Hintergrundfarbe der Statusbar von eine hexadezimale Zeichenfolge fest. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +CSS-Kurzschrift-Eigenschaften werden ebenfalls unterstützt. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Auf iOS 7 Wenn Sie StatusBar.statusBarOverlaysWebView auf False festlegen, können Sie die Hintergrundfarbe der Statusbar von eine hexadezimale Zeichenfolge (#RRGGBB) festlegen. + +Auf WP7 und WP8 können Sie auch Werte wie #AARRGGBB, angeben wo AA einen alpha-Wert ist + +## Unterstützte Plattformen + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.hide + +Ausblenden der Statusleiste. + + StatusBar.hide(); + + +## Unterstützte Plattformen + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.show + +Zeigt die Statusleiste. + + StatusBar.show(); + + +## Unterstützte Plattformen + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 + +# StatusBar.isVisible + +Lesen Sie diese Eigenschaft, um festzustellen, ob die Statusbar sichtbar oder nicht ist. + + if (StatusBar.isVisible) { + // do something + } + + +## Unterstützte Plattformen + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone-8.1 diff --git a/plugins/cordova-plugin-statusbar/doc/es/README.md b/plugins/cordova-plugin-statusbar/doc/es/README.md new file mode 100644 index 0000000..8be769d --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/es/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> El `StatusBar` objeto proporciona algunas funciones para personalizar el iOS y Android StatusBar. + +## Instalación + + cordova plugin add cordova-plugin-statusbar + + +## Preferencias + +#### config.xml + + * **StatusBarOverlaysWebView** (boolean, true por defecto). En iOS 7, hacer la superposición statusbar o no superponer la WebView al inicio. + + + + + * **StatusBarBackgroundColor** (color hex string por defecto #000000). IOS 7 y 5 Android, configurar el color de fondo de la barra de estado por una cadena hexadecimal (#RRGGBB) en el arranque. + + + + + * **StatusBarStyle** (status bar estilo por defecto lightcontent). En iOS 7, definir el estilo de barra de estado. Por defecto las opciones disponibles, lightcontent, blacktranslucent, blackopaque. + + + + +### Rarezas Android + +Android 5 + pautas especifican utilizando un color diferente para la barra de estado que la aplicación principal de color (a diferencia del color de barra de estado uniforme de muchas apps de iOS 7 +), por lo que puede establecer el color de la barra de estado en tiempo de ejecución en su lugar a través de `StatusBar.backgroundColorByHexString` o `StatusBar.backgroundColorByName`. Una forma de hacerlo sería: + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## Escondido en el arranque + +Durante el tiempo de ejecución puede utilizar la función StatusBar.hide abajo, pero si quieres la barra de estado que está escondido en el inicio de la aplicación, se debe modificar el archivo Info.plist de su aplicación. + +Agregar/editar estos dos atributos si no está presente. Defina **"inicialmente se esconde la barra de estado"** a **"YES"** y **"Aparición de vista basado en controlador estatus bar"** a **"NO"**. Si se edita manualmente sin Xcode, las claves y valores son: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Métodos + +Este plugin define global `StatusBar` objeto. + +Aunque en el ámbito global, no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## Propiedades + + * StatusBar.isVisible + +## Permisos + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +En iOS 7, hacer la barra de estado superposición o no superponer la vista Web. + + StatusBar.overlaysWebView(true); + + +## Descripción + +En iOS 7, establecida en false para que la barra de estado aparezca como iOS 6. Establece el color de fondo y estilo para utilizar las otras funciones. + +## Plataformas soportadas + + * iOS + +## Ejemplo rápido + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Utilice la barra de estado por defecto (texto oscuro, para fondos de luz). + + StatusBar.styleDefault(); + + +## Plataformas soportadas + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleLightContent + +Utilice la barra de estado lightContent (texto ligero, para fondos oscuros). + + StatusBar.styleLightContent(); + + +## Plataformas soportadas + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Utilice la barra de estado blackTranslucent (texto ligero, para fondos oscuros). + + StatusBar.styleBlackTranslucent(); + + +## Plataformas soportadas + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Utilice la barra de estado blackOpaque (texto ligero, para fondos oscuros). + + StatusBar.styleBlackOpaque(); + + +## Plataformas soportadas + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +En iOS 7, al establecer StatusBar.statusBarOverlaysWebView a false, se puede establecer el color de fondo de la barra de estado nombre del color. + + StatusBar.backgroundColorByName("red"); + + +Nombres de los colores admitidos son: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Plataformas soportadas + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Establece el color de fondo de la barra de estado por una cadena hexadecimal. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +Propiedades CSS abreviada también son compatibles. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +En iOS 7, cuando se establece StatusBar.statusBarOverlaysWebView en false, se puede establecer el color de fondo de la barra de estado una cadena hexadecimal (#RRGGBB). + +En WP7 y WP8 también puede especificar valores como #AARRGGBB, donde AA es un valor alfa + +## Plataformas soportadas + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.hide + +Ocultar la barra de estado. + + StatusBar.hide(); + + +## Plataformas soportadas + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.show + +Muestra la barra de estado. + + StatusBar.show(); + + +## Plataformas soportadas + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.isVisible + +Lea esta propiedad para ver si la barra de estado es visible o no. + + if (StatusBar.isVisible) { + // do something + } + + +## Plataformas soportadas + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/es/index.md b/plugins/cordova-plugin-statusbar/doc/es/index.md new file mode 100644 index 0000000..fa4ba67 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/es/index.md @@ -0,0 +1,252 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> El `StatusBar` objeto proporciona algunas funciones para personalizar el iOS y Android StatusBar. + +## Instalación + + Cordova plugin agregar cordova-plugin-statusbar + + +## Preferencias + +#### config.xml + +* **StatusBarOverlaysWebView** (boolean, true por defecto). En iOS 7, hacer la superposición statusbar o no superponer la WebView al inicio. + + + + +* **StatusBarBackgroundColor** (cadena hexadecimal color, #000000 por defecto). En iOS 7, establecer el color de fondo de la barra de estado por una cadena hexadecimal (#RRGGBB) en el arranque. + + + + +* **StatusBarStyle** (status bar estilo por defecto lightcontent). En iOS 7, definir el estilo de barra de estado. Por defecto las opciones disponibles, lightcontent, blacktranslucent, blackopaque. + + + + +## Escondido en el arranque + +Durante el tiempo de ejecución puede utilizar la función StatusBar.hide abajo, pero si quieres la barra de estado que está escondido en el inicio de la aplicación, se debe modificar el archivo Info.plist de su aplicación. + +Agregar/editar estos dos atributos si no está presente. Defina **"inicialmente se esconde la barra de estado"** a **"YES"** y **"Aparición de vista basado en controlador estatus bar"** a **"NO"**. Si se edita manualmente sin Xcode, las claves y valores son: + + < llave > UIStatusBarHidden < / key >< verdadero / >< llave > UIViewControllerBasedStatusBarAppearance < / key >< falso / > + + +## Métodos + +Este plugin define global `StatusBar` objeto. + +Aunque en el ámbito global, no estará disponible hasta después de la `deviceready` evento. + + document.addEventListener ("deviceready", onDeviceReady, false); + function onDeviceReady() {console.log(StatusBar)}; + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## Propiedades + +* StatusBar.isVisible + +## Permisos + +#### config.xml + + < nombre de la función = "StatusBar" >< nombre param = "ios-paquete" value = "CDVStatusBar" onload = "true" / >< / característica > + + +# StatusBar.overlaysWebView + +En iOS 7, hacer la barra de estado superposición o no superponer la vista Web. + + StatusBar.overlaysWebView(true); + + +## Descripción + +En iOS 7, establecida en false para que la barra de estado aparezca como iOS 6. Establece el color de fondo y estilo para utilizar las otras funciones. + +## Plataformas soportadas + +* iOS + +## Ejemplo rápido + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Utilice la barra de estado por defecto (texto oscuro, para fondos de luz). + + StatusBar.styleDefault(); + + +## Plataformas soportadas + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleLightContent + +Utilice la barra de estado lightContent (texto ligero, para fondos oscuros). + + StatusBar.styleLightContent(); + + +## Plataformas soportadas + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Utilice la barra de estado blackTranslucent (texto ligero, para fondos oscuros). + + StatusBar.styleBlackTranslucent(); + + +## Plataformas soportadas + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Utilice la barra de estado blackOpaque (texto ligero, para fondos oscuros). + + StatusBar.styleBlackOpaque(); + + +## Plataformas soportadas + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +En iOS 7, al establecer StatusBar.statusBarOverlaysWebView a false, se puede establecer el color de fondo de la barra de estado nombre del color. + + StatusBar.backgroundColorByName("red"); + + +Nombres de los colores admitidos son: + + negro, gris oscuro, lightGray, blanco, gris, rojo, verde, azul, cian, amarillo, magenta, naranja, púrpura, marrón + + +## Plataformas soportadas + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Establece el color de fondo de la barra de estado por una cadena hexadecimal. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +Propiedades CSS abreviada también son compatibles. + + StatusBar.backgroundColorByHexString("#333"); = > StatusBar.backgroundColorByHexString("#FAB") #333333; = > #FFAABB + + +En iOS 7, cuando se establece StatusBar.statusBarOverlaysWebView en false, se puede establecer el color de fondo de la barra de estado una cadena hexadecimal (#RRGGBB). + +En WP7 y WP8 también puede especificar valores como #AARRGGBB, donde AA es un valor alfa + +## Plataformas soportadas + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.hide + +Ocultar la barra de estado. + + StatusBar.hide(); + + +## Plataformas soportadas + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.show + +Muestra la barra de estado. + + StatusBar.show(); + + +## Plataformas soportadas + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.isVisible + +Lea esta propiedad para ver si la barra de estado es visible o no. + + Si (StatusBar.isVisible) {/ / hacer algo} + + +## Plataformas soportadas + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 diff --git a/plugins/cordova-plugin-statusbar/doc/fr/README.md b/plugins/cordova-plugin-statusbar/doc/fr/README.md new file mode 100644 index 0000000..6f7f9bf --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/fr/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> Le `StatusBar` objet fournit quelques fonctions pour personnaliser les iOS et Android StatusBar. + +## Installation + + cordova plugin add cordova-plugin-statusbar + + +## Préférences + +#### config.Xml + + * **StatusBarOverlaysWebView** (boolean, la valeur par défaut true). Sur iOS 7, faire la superposition de statusbar ou pas superposition le WebView au démarrage. + + + + + * **StatusBarBackgroundColor** (chaîne hexadécimale de couleur, par défaut, #000000). Sur iOS 7 et 5 Android, définir la couleur d'arrière-plan de la barre d'État par une chaîne hexadécimale (#RRGGBB) au démarrage. + + + + + * **StatusBarStyle** (style de barre de statut, par défaut, lightcontent). Sur iOS 7, définir le style de barre de statut. Par défaut les options disponibles, lightcontent, blacktranslucent, blackopaque. + + + + +### Quirks Android + +Les lignes directrices 5 + Android spécifient à l'aide d'une couleur différente pour la barre d'État à votre application principale couleur (contrairement à la couleur uniforme statusbar de nombreuses applications iOS 7 +), donc vous pouvez définir la couleur de la barre d'état lors de l'exécution au lieu de cela via `StatusBar.backgroundColorByHexString` ou `StatusBar.backgroundColorByName`. Une façon de le faire serait : + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## Cacher au démarrage + +Pendant l'exécution, vous pouvez utiliser la fonction StatusBar.hide en bas, mais si vous souhaitez que la barre d'État pour être caché au démarrage de l'application, vous devez modifier le fichier Info.plist de votre application. + +Ajouter/modifier ces deux attributs si n'est pas présent. **"Barre d'État est initialement masqué"** la valeur **"** Yes" et **"À l'apparence vue sur contrôleur statut bar"** la valeur **"Non"**. Si vous modifiez manuellement sans Xcode, les clés et les valeurs sont : + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Méthodes + +Ce plugin définit objet `StatusBar` global. + +Bien que dans la portée globale, il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## Propriétés + + * StatusBar.isVisible + +## Autorisations + +#### config.Xml + + + + + + +# StatusBar.overlaysWebView + +Sur iOS 7, faire la statusbar superposition ou pas superposer le WebView. + + StatusBar.overlaysWebView(true); + + +## Description + +Sur iOS 7, la valeur false pour afficher la barre d'État comme iOS 6. Définissez la couleur de style et d'arrière-plan en fonction de l'utilisation des autres fonctions. + +## Plates-formes supportées + + * iOS + +## Exemple court + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Utilisez la barre de statut par défaut (texte sombre, pour les arrière-plans lumineux). + + StatusBar.styleDefault(); + + +## Plates-formes supportées + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleLightContent + +Utilisez la barre d'État lightContent (texte clair, des arrière-plans sombres). + + StatusBar.styleLightContent(); + + +## Plates-formes supportées + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Utilisez la barre d'État blackTranslucent (texte clair, des arrière-plans sombres). + + StatusBar.styleBlackTranslucent(); + + +## Plates-formes supportées + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Utilisez la barre d'État blackOpaque (texte clair, des arrière-plans sombres). + + StatusBar.styleBlackOpaque(); + + +## Plates-formes supportées + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Sur iOS 7, lorsque vous définissez StatusBar.statusBarOverlaysWebView sur false, vous pouvez définir la couleur d'arrière-plan de la barre d'État par nom de couleur. + + StatusBar.backgroundColorByName("red"); + + +Les noms de couleurs prises en charge sont : + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Plates-formes supportées + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Définit la couleur d'arrière-plan de la barre d'État par une chaîne hexadécimale. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +Propriétés de raccourci CSS sont également pris en charge. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Sur iOS 7, lorsque vous définissez StatusBar.statusBarOverlaysWebView sur false, vous pouvez définir la couleur d'arrière-plan de la barre d'État par une chaîne hexadécimale (#RRGGBB). + +Sur WP7 et WP8, vous pouvez également spécifier des valeurs comme #AARRGGBB, où AA représente une valeur alpha + +## Plates-formes supportées + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.hide + +Masquer la barre d'État. + + StatusBar.hide(); + + +## Plates-formes supportées + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.show + +Affiche la barre d'État. + + StatusBar.show(); + + +## Plates-formes supportées + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.isVisible + +Lire cette propriété afin de voir si la barre d'État est visible ou non. + + if (StatusBar.isVisible) { + // do something + } + + +## Plates-formes supportées + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/fr/index.md b/plugins/cordova-plugin-statusbar/doc/fr/index.md new file mode 100644 index 0000000..816f3df --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/fr/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> Le `StatusBar` objet fournit quelques fonctions pour personnaliser les iOS et Android StatusBar. + +## Installation + + cordova plugin add cordova-plugin-statusbar + + +## Préférences + +#### config.xml + +* **StatusBarOverlaysWebView** (boolean, la valeur par défaut true). Sur iOS 7, faire la superposition de statusbar ou pas superposition le WebView au démarrage. + + + + +* **StatusBarBackgroundColor** (chaîne hexadécimale de couleur, par défaut, #000000). Sur iOS 7, définir la couleur d'arrière-plan de la barre d'État par une chaîne hexadécimale (#RRGGBB) au démarrage. + + + + +* **StatusBarStyle** (style de barre de statut, par défaut, lightcontent). Sur iOS 7, définir le style de barre de statut. Par défaut les options disponibles, lightcontent, blacktranslucent, blackopaque. + + + + +## Cacher au démarrage + +Pendant l'exécution, vous pouvez utiliser la fonction StatusBar.hide en bas, mais si vous souhaitez que la barre d'État pour être caché au démarrage de l'application, vous devez modifier le fichier Info.plist de votre application. + +Ajouter/modifier ces deux attributs si n'est pas présent. **"Barre d'État est initialement masqué"** la valeur **"** Yes" et **"À l'apparence vue sur contrôleur statut bar"** la valeur **"Non"**. Si vous modifiez manuellement sans Xcode, les clés et les valeurs sont : + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Méthodes + +Ce plugin définit objet `StatusBar` global. + +Bien que dans la portée globale, il n'est pas disponible jusqu'après la `deviceready` événement. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## Propriétés + +* StatusBar.isVisible + +## Autorisations + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +Sur iOS 7, faire la statusbar superposition ou pas superposer le WebView. + + StatusBar.overlaysWebView(true); + + +## Description + +Sur iOS 7, la valeur false pour afficher la barre d'État comme iOS 6. Définissez la couleur de style et d'arrière-plan en fonction de l'utilisation des autres fonctions. + +## Plates-formes supportées + +* iOS + +## Exemple court + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Utilisez la barre de statut par défaut (texte sombre, pour les arrière-plans lumineux). + + StatusBar.styleDefault(); + + +## Plates-formes prises en charge + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleLightContent + +Utilisez la barre d'État lightContent (texte clair, des arrière-plans sombres). + + StatusBar.styleLightContent(); + + +## Plates-formes prises en charge + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Utilisez la barre d'État blackTranslucent (texte clair, des arrière-plans sombres). + + StatusBar.styleBlackTranslucent(); + + +## Plates-formes prises en charge + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Utilisez la barre d'État blackOpaque (texte clair, des arrière-plans sombres). + + StatusBar.styleBlackOpaque(); + + +## Plates-formes prises en charge + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Sur iOS 7, lorsque vous définissez StatusBar.statusBarOverlaysWebView sur false, vous pouvez définir la couleur d'arrière-plan de la barre d'État par nom de couleur. + + StatusBar.backgroundColorByName("red"); + + +Les noms de couleurs prises en charge sont : + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Plates-formes prises en charge + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Définit la couleur d'arrière-plan de la barre d'État par une chaîne hexadécimale. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +Propriétés de raccourci CSS sont également pris en charge. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Sur iOS 7, lorsque vous définissez StatusBar.statusBarOverlaysWebView sur false, vous pouvez définir la couleur d'arrière-plan de la barre d'État par une chaîne hexadécimale (#RRGGBB). + +Sur WP7 et WP8, vous pouvez également spécifier des valeurs comme #AARRGGBB, où AA représente une valeur alpha + +## Plates-formes prises en charge + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.hide + +Masquer la barre d'État. + + StatusBar.hide(); + + +## Plates-formes prises en charge + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.show + +Affiche la barre d'État. + + StatusBar.show(); + + +## Plates-formes prises en charge + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.isVisible + +Lire cette propriété afin de voir si la barre d'État est visible ou non. + + if (StatusBar.isVisible) { + // do something + } + + +## Plates-formes supportées + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 diff --git a/plugins/cordova-plugin-statusbar/doc/it/README.md b/plugins/cordova-plugin-statusbar/doc/it/README.md new file mode 100644 index 0000000..cf3f844 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/it/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> Il `StatusBar` oggetto fornisce alcune funzioni per personalizzare l'iOS e Android StatusBar. + +## Installazione + + cordova plugin add cordova-plugin-statusbar + + +## Preferenze + +#### config. XML + + * **StatusBarOverlaysWebView** (boolean, default è true). IOS 7, rendono la statusbar sovrapposizione o la non sovrapposizione WebView all'avvio. + + + + + * **StatusBarBackgroundColor** (stringa esadecimale di colore, il valore predefinito è #000000). Su iOS 7 e 5 Android, è possibile impostare il colore di sfondo della barra di stato di una stringa esadecimale (#RRGGBB) all'avvio. + + + + + * **StatusBarStyle** (status bar in stile, default è lightcontent). IOS 7, impostare lo stile di barra di stato. Predefinita di opzioni disponibili, lightcontent, blacktranslucent, blackopaque. + + + + +### Stranezze Android + +Le linee 5 + Android Guida specificano utilizzando un colore diverso per la barra di stato che l'app principale di colore (a differenza di colore uniforme statusbar di molte applicazioni di iOS 7 +), quindi si consiglia di impostare il colore della barra di stato in fase di esecuzione invece tramite `StatusBar.backgroundColorByHexString` o `StatusBar.backgroundColorByName`. Un modo per farlo sarebbe: + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## Nascondendo all'avvio + +In fase di esecuzione è possibile utilizzare la funzione di StatusBar.hide qui sotto, ma se si desidera che la barra di stato venga nascosta all'avvio di app, è necessario modificare il file info. plist dell'app. + +Aggiungere o modificare questi due attributi, se non presente. Impostare la **"barra di stato è inizialmente nascosto"** a **"YES"** e **"Aspetto di vista basati su controller status bar"** a **"NO"**. Se si modifica manualmente senza Xcode, le chiavi e i valori sono: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Metodi + +Questo plugin definisce globale oggetto `StatusBar`. + +Anche se in ambito globale, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## Proprietà + + * StatusBar.isVisible + +## Autorizzazioni + +#### config. XML + + + + + + +# StatusBar.overlaysWebView + +IOS 7, rendono la statusbar sovrapposizione o non sovrapporre WebView. + + StatusBar.overlaysWebView(true); + + +## Descrizione + +IOS 7, impostato su false per rendere la barra di stato vengono visualizzati come iOS 6. Impostare il colore di sfondo e stile per soddisfare utilizzando altre funzioni. + +## Piattaforme supportate + + * iOS + +## Esempio rapido + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Utilizzare la barra di stato predefinito (testo scuro, per sfondi di luce). + + StatusBar.styleDefault(); + + +## Piattaforme supportate + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleLightContent + +Utilizzare la barra di stato lightContent (testo in chiaro, per sfondi scuri). + + StatusBar.styleLightContent(); + + +## Piattaforme supportate + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Utilizzare la barra di stato blackTranslucent (testo in chiaro, per sfondi scuri). + + StatusBar.styleBlackTranslucent(); + + +## Piattaforme supportate + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Utilizzare la barra di stato blackOpaque (testo in chiaro, per sfondi scuri). + + StatusBar.styleBlackOpaque(); + + +## Piattaforme supportate + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +IOS 7, quando StatusBar.statusBarOverlaysWebView è impostata su false, è possibile impostare il colore di sfondo della barra di stato con il nome di colore. + + StatusBar.backgroundColorByName("red"); + + +Nomi di colore supportati sono: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Piattaforme supportate + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Imposta il colore di sfondo della barra di stato di una stringa esadecimale. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +Proprietà di scrittura stenografica CSS sono supportati anche. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +IOS 7, quando StatusBar.statusBarOverlaysWebView è impostata su false, è possibile impostare il colore di sfondo della barra di stato di una stringa esadecimale (#RRGGBB). + +Su WP7 e WP8 è inoltre possibile specificare i valori come #AARRGGBB, dove AA è un valore alfa + +## Piattaforme supportate + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.hide + +Nascondere la barra di stato. + + StatusBar.hide(); + + +## Piattaforme supportate + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.show + +Mostra la barra di stato. + + StatusBar.show(); + + +## Piattaforme supportate + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.isVisible + +Leggere questa proprietà per vedere se la barra di stato è visibile o no. + + if (StatusBar.isVisible) { + // do something + } + + +## Piattaforme supportate + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/it/index.md b/plugins/cordova-plugin-statusbar/doc/it/index.md new file mode 100644 index 0000000..73ddcd4 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/it/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> Il `StatusBar` oggetto fornisce alcune funzioni per personalizzare l'iOS e Android StatusBar. + +## Installazione + + cordova plugin add cordova-plugin-statusbar + + +## Preferenze + +#### config.xml + +* **StatusBarOverlaysWebView** (boolean, default è true). IOS 7, rendono la statusbar sovrapposizione o la non sovrapposizione WebView all'avvio. + + + + +* **StatusBarBackgroundColor** (stringa esadecimale colore, predefinito è #000000). IOS 7, impostare il colore di sfondo della barra di stato di una stringa esadecimale (#RRGGBB) all'avvio. + + + + +* **StatusBarStyle** (status bar in stile, default è lightcontent). IOS 7, impostare lo stile di barra di stato. Predefinita di opzioni disponibili, lightcontent, blacktranslucent, blackopaque. + + + + +## Nascondendo all'avvio + +In fase di esecuzione è possibile utilizzare la funzione di StatusBar.hide qui sotto, ma se si desidera che la barra di stato venga nascosta all'avvio di app, è necessario modificare il file info. plist dell'app. + +Aggiungere o modificare questi due attributi, se non presente. Impostare la **"barra di stato è inizialmente nascosto"** a **"YES"** e **"Aspetto di vista basati su controller status bar"** a **"NO"**. Se si modifica manualmente senza Xcode, le chiavi e i valori sono: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Metodi + +Questo plugin definisce globale oggetto `StatusBar`. + +Anche se in ambito globale, non è disponibile fino a dopo l'evento `deviceready`. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## Proprietà + +* StatusBar.isVisible + +## Autorizzazioni + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +IOS 7, rendono la statusbar sovrapposizione o non sovrapporre WebView. + + StatusBar.overlaysWebView(true); + + +## Descrizione + +IOS 7, impostato su false per rendere la barra di stato vengono visualizzati come iOS 6. Impostare il colore di sfondo e stile per soddisfare utilizzando altre funzioni. + +## Piattaforme supportate + +* iOS + +## Esempio rapido + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Utilizzare la barra di stato predefinito (testo scuro, per sfondi di luce). + + StatusBar.styleDefault(); + + +## Piattaforme supportate + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleLightContent + +Utilizzare la barra di stato lightContent (testo in chiaro, per sfondi scuri). + + StatusBar.styleLightContent(); + + +## Piattaforme supportate + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Utilizzare la barra di stato blackTranslucent (testo in chiaro, per sfondi scuri). + + StatusBar.styleBlackTranslucent(); + + +## Piattaforme supportate + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Utilizzare la barra di stato blackOpaque (testo in chiaro, per sfondi scuri). + + StatusBar.styleBlackOpaque(); + + +## Piattaforme supportate + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +IOS 7, quando StatusBar.statusBarOverlaysWebView è impostata su false, è possibile impostare il colore di sfondo della barra di stato con il nome di colore. + + StatusBar.backgroundColorByName("red"); + + +Nomi di colore supportati sono: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Piattaforme supportate + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Imposta il colore di sfondo della barra di stato di una stringa esadecimale. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +Proprietà di scrittura stenografica CSS sono supportati anche. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +IOS 7, quando StatusBar.statusBarOverlaysWebView è impostata su false, è possibile impostare il colore di sfondo della barra di stato di una stringa esadecimale (#RRGGBB). + +Su WP7 e WP8 è inoltre possibile specificare i valori come #AARRGGBB, dove AA è un valore alfa + +## Piattaforme supportate + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.hide + +Nascondere la barra di stato. + + StatusBar.hide(); + + +## Piattaforme supportate + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.show + +Mostra la barra di stato. + + StatusBar.show(); + + +## Piattaforme supportate + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.isVisible + +Leggere questa proprietà per vedere se la barra di stato è visibile o no. + + if (StatusBar.isVisible) { + // do something + } + + +## Piattaforme supportate + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 diff --git a/plugins/cordova-plugin-statusbar/doc/ja/README.md b/plugins/cordova-plugin-statusbar/doc/ja/README.md new file mode 100644 index 0000000..fc8b59a --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/ja/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> `StatusBar`オブジェクトã¯ã€iOS 㨠Android ステータス ãƒãƒ¼ã‚’カスタマイズã™ã‚‹ã„ãã¤ã‹ã®æ©Ÿèƒ½ã‚’æä¾›ã—ã¾ã™ã€‚ + +## インストール + + cordova plugin add cordova-plugin-statusbar + + +## 基本設定 + +#### config.xml + + * **StatusBarOverlaysWebView**(ブール値ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã¯ true)。IOS 7ã€èµ·å‹•æ™‚ã«ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ オーãƒãƒ¼ãƒ¬ã‚¤ã¾ãŸã¯ãªã„オーãƒãƒ¼ãƒ¬ã‚¤ã€WebView を作る。 + + + + + * **StatusBarBackgroundColor**(カラー 16 進文字列ã€æ—¢å®šå€¤ã¯ #000000)。IOS 7 ã¨ã‚¢ãƒ³ãƒ‰ãƒ­ã‚¤ãƒ‰ 5ã€16 進文字列 (#RRGGBB) 起動時ã«ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã®èƒŒæ™¯è‰²ã‚’設定ã—ã¾ã™ã€‚ + + + + + * **StatusBarStyle**(ステータス ãƒãƒ¼ã®ã‚¹ã‚¿ã‚¤ãƒ«ã€æ—¢å®šå€¤ã¯ lightcontent)。Ios 7ã€ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ ãƒãƒ¼ã®ã‚¹ã‚¿ã‚¤ãƒ«ã‚’設定ã—ã¾ã™ã€‚使用å¯èƒ½ãªã‚ªãƒ—ションã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã€lightcontentã€blacktranslucentã€blackopaque。 + + + + +### Android ã®ç™– + +Android ã®ã‚¬ã‚¤ãƒ‰ãƒ©ã‚¤ãƒ³ 5 + 指定メイン アプリよりもステータスãƒãƒ¼ã®ç•°ãªã‚‹è‰²ã‚’使用ã—ã¦`StatusBar.backgroundColorByHexString`ã¾ãŸã¯`StatusBar.backgroundColorByName`経由ã§ä»£ã‚ã‚Šã«å®Ÿè¡Œæ™‚ã«ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ ãƒãƒ¼ã®è‰²ã‚’設定ã™ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã®ã§ (ã¨ã¯é•ã£ã¦åˆ¶æœã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼è‰²å¤šãã® iOS 7 + アプリã®) 色ã—ã¾ã™ã€‚ ãれを行ã†æ–¹æ³•ã® 1 ã¤ã«ãªã‚Šã¾ã™ã€‚ + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## 起動時ã«éžè¡¨ç¤º + +実行時ã«ä¸‹ã«ã€StatusBar.hide 関数を使用ã§ãã¾ã™ãŒã€StatusBar アプリ起動時ã«éžè¡¨ç¤ºã«ã™ã‚‹å ´åˆã¯ã€ã‚¢ãƒ—リ㮠Info.plist ファイルを変更ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + +ã“れら 2 ã¤ã®å±žæ€§ã®è¿½åŠ /編集存在ã—ãªã„å ´åˆã€‚ **「ステータス ãƒãƒ¼ãŒéžè¡¨ç¤ºæœ€åˆã€** **"YES"**を設定ã—〠**「ビュー コント ローラー ベースã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ ãƒãƒ¼ã®å¤–観ã€** **"NO"**ã«ã—ã¾ã™ã€‚ Xcode ã›ãšæ‰‹å‹•ã§ç·¨é›†ã™ã‚‹ã€ã‚­ãƒ¼ã¨å€¤ã¯ã€‚ + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## メソッド + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `StatusBar` オブジェクトを定義ã—ã¾ã™ã€‚ + +グローãƒãƒ« スコープã§ã¯ã‚ã‚‹ãŒãã‚ŒãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã§ã™ã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## プロパティ + + * StatusBar.isVisible + +## ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +IOS 7ã€statusbar オーãƒãƒ¼ãƒ¬ã‚¤ã¾ãŸã¯ãªã„ WebView をオーãƒãƒ¼ãƒ¬ã‚¤ã—ã¾ã™ã€‚ + + StatusBar.overlaysWebView(true); + + +## 解説 + +IOS 7ã€iOS ã® 6 ã®ã‚ˆã†ã«è¡¨ç¤ºã•ã‚Œã‚‹ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã‚’ false ã«è¨­å®šã—ã¾ã™ã€‚ä»–ã®é–¢æ•°ã®ä½¿ç”¨ã«åˆã‚ã›ã¦ã‚¹ã‚¿ã‚¤ãƒ«ã‚„背景色を設定ã—ã¾ã™ã€‚ + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + +## ç°¡å˜ãªä¾‹ + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +既定ステータス ãƒãƒ¼ (æš—ã„テキストã€æ·¡è‰²ã®èƒŒæ™¯) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleDefault(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleLightContent + +LightContent ステータスãƒãƒ¼ (æš—ã„背景ã®æ˜Žã‚‹ã„テキスト) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleLightContent(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +BlackTranslucent ステータスãƒãƒ¼ (æš—ã„背景ã®æ˜Žã‚‹ã„テキスト) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleBlackTranslucent(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +BlackOpaque ステータスãƒãƒ¼ (æš—ã„背景ã®æ˜Žã‚‹ã„テキスト) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleBlackOpaque(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Ios 7ã€StatusBar.statusBarOverlaysWebView ã‚’ false ã«è¨­å®šã™ã‚‹å ´åˆã¯ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã®èƒŒæ™¯è‰²ã®è‰²ã®åå‰ã«ã‚ˆã£ã¦è¨­å®šã§ãã¾ã™ã€‚ + + StatusBar.backgroundColorByName("red"); + + +サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„る色ã®åå‰ã¯æ¬¡ã®ã¨ãŠã‚Šã§ã™ã€‚ + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +16 進文字列をステータス ãƒãƒ¼ã®èƒŒæ™¯è‰²ã‚’設定ã—ã¾ã™ã€‚ + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +速記㮠CSS プロパティもサãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã™ã€‚ + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Ios 7ã€StatusBar.statusBarOverlaysWebView ã‚’ false ã«è¨­å®šã™ã‚‹å ´åˆã¯ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã®èƒŒæ™¯è‰²ã‚’ 16 進文字列 (#RRGGBB) ã§è¨­å®šã§ãã¾ã™ã€‚ + +WP7 㨠WP8 も指定ã§ãã¾ã™å€¤ #AARRGGBB, AA ã¯ã€ã‚¢ãƒ«ãƒ•ã‚¡å€¤ã¨ã—㦠+ +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.hide + +ステータスãƒãƒ¼ã‚’éš ã—ã¾ã™ã€‚ + + StatusBar.hide(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * アンドロイド + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.show + +ステータス ãƒãƒ¼ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + + StatusBar.show(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * アンドロイド + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.isVisible + +ã“ã®ãƒ—ロパティã€ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ãŒè¡¨ç¤ºã•ã‚Œã‚‹ã‹ã©ã†ã‹ã‚’ãŠèª­ã¿ãã ã•ã„。 + + if (StatusBar.isVisible) { + // do something + } + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + + * iOS + * アンドロイド + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/ja/index.md b/plugins/cordova-plugin-statusbar/doc/ja/index.md new file mode 100644 index 0000000..79705f2 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/ja/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> `StatusBar`オブジェクトã¯ã€iOS 㨠Android ステータス ãƒãƒ¼ã‚’カスタマイズã™ã‚‹ã„ãã¤ã‹ã®æ©Ÿèƒ½ã‚’æä¾›ã—ã¾ã™ã€‚ + +## インストール + + cordova plugin add cordova-plugin-statusbar + + +## 基本設定 + +#### config.xml + +* **StatusBarOverlaysWebView**(ブール値ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã¯ true)。IOS 7ã€èµ·å‹•æ™‚ã«ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ オーãƒãƒ¼ãƒ¬ã‚¤ã¾ãŸã¯ãªã„オーãƒãƒ¼ãƒ¬ã‚¤ã€WebView を作る。 + + + + +* **StatusBarBackgroundColor**(色 16 進文字列ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã¯ # 000000)。Ios 7ã€èµ·å‹•æ™‚ã« 16 進文字列 (#RRGGBB) ã§ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ ãƒãƒ¼ã®èƒŒæ™¯è‰²ã‚’設定ã—ã¾ã™ã€‚ + + + + +* **StatusBarStyle**(ステータス ãƒãƒ¼ã®ã‚¹ã‚¿ã‚¤ãƒ«ã€æ—¢å®šå€¤ã¯ lightcontent)。Ios 7ã€ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ ãƒãƒ¼ã®ã‚¹ã‚¿ã‚¤ãƒ«ã‚’設定ã—ã¾ã™ã€‚使用å¯èƒ½ãªã‚ªãƒ—ションã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã€lightcontentã€blacktranslucentã€blackopaque。 + + + + +## 起動時ã«éžè¡¨ç¤º + +実行時ã«ä¸‹ã«ã€StatusBar.hide 関数を使用ã§ãã¾ã™ãŒã€StatusBar アプリ起動時ã«éžè¡¨ç¤ºã«ã™ã‚‹å ´åˆã¯ã€ã‚¢ãƒ—リ㮠Info.plist ファイルを変更ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ + +ã“れら 2 ã¤ã®å±žæ€§ã®è¿½åŠ /編集存在ã—ãªã„å ´åˆã€‚ **「ステータス ãƒãƒ¼ãŒéžè¡¨ç¤ºæœ€åˆã€** **"YES"**を設定ã—〠**「ビュー コント ローラー ベースã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ ãƒãƒ¼ã®å¤–観ã€** **"NO"**ã«ã—ã¾ã™ã€‚ Xcode ã›ãšæ‰‹å‹•ã§ç·¨é›†ã™ã‚‹ã€ã‚­ãƒ¼ã¨å€¤ã¯ã€‚ + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## メソッド + +ã“ã®ãƒ—ラグインã§ã¯ã€ã‚°ãƒ­ãƒ¼ãƒãƒ« `StatusBar` オブジェクトを定義ã—ã¾ã™ã€‚ + +グローãƒãƒ« スコープã§ã¯ã‚ã‚‹ãŒãã‚ŒãŒãªã„ã¾ã§ `deviceready` イベントã®å¾Œã§ã™ã€‚ + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## プロパティ + +* StatusBar.isVisible + +## ã‚¢ã‚¯ã‚»ã‚¹è¨±å¯ + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +IOS 7ã€statusbar オーãƒãƒ¼ãƒ¬ã‚¤ã¾ãŸã¯ãªã„ WebView をオーãƒãƒ¼ãƒ¬ã‚¤ã—ã¾ã™ã€‚ + + StatusBar.overlaysWebView(true); + + +## 解説 + +IOS 7ã€iOS ã® 6 ã®ã‚ˆã†ã«è¡¨ç¤ºã•ã‚Œã‚‹ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã‚’ false ã«è¨­å®šã—ã¾ã™ã€‚ä»–ã®é–¢æ•°ã®ä½¿ç”¨ã«åˆã‚ã›ã¦ã‚¹ã‚¿ã‚¤ãƒ«ã‚„背景色を設定ã—ã¾ã™ã€‚ + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS + +## ç°¡å˜ãªä¾‹ + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +既定ステータス ãƒãƒ¼ (æš—ã„テキストã€æ·¡è‰²ã®èƒŒæ™¯) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleDefault(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleLightContent + +LightContent ステータスãƒãƒ¼ (æš—ã„背景ã®æ˜Žã‚‹ã„テキスト) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleLightContent(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +BlackTranslucent ステータスãƒãƒ¼ (æš—ã„背景ã®æ˜Žã‚‹ã„テキスト) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleBlackTranslucent(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +BlackOpaque ステータスãƒãƒ¼ (æš—ã„背景ã®æ˜Žã‚‹ã„テキスト) を使用ã—ã¾ã™ã€‚ + + StatusBar.styleBlackOpaque(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Ios 7ã€StatusBar.statusBarOverlaysWebView ã‚’ false ã«è¨­å®šã™ã‚‹å ´åˆã¯ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã®èƒŒæ™¯è‰²ã®è‰²ã®åå‰ã«ã‚ˆã£ã¦è¨­å®šã§ãã¾ã™ã€‚ + + StatusBar.backgroundColorByName("red"); + + +サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„る色ã®åå‰ã¯æ¬¡ã®ã¨ãŠã‚Šã§ã™ã€‚ + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +16 進文字列をステータス ãƒãƒ¼ã®èƒŒæ™¯è‰²ã‚’設定ã—ã¾ã™ã€‚ + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +速記㮠CSS プロパティもサãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã™ã€‚ + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Ios 7ã€StatusBar.statusBarOverlaysWebView ã‚’ false ã«è¨­å®šã™ã‚‹å ´åˆã¯ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã®èƒŒæ™¯è‰²ã‚’ 16 進文字列 (#RRGGBB) ã§è¨­å®šã§ãã¾ã™ã€‚ + +WP7 㨠WP8 も指定ã§ãã¾ã™å€¤ #AARRGGBB, AA ã¯ã€ã‚¢ãƒ«ãƒ•ã‚¡å€¤ã¨ã—㦠+ +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.hide + +ステータスãƒãƒ¼ã‚’éš ã—ã¾ã™ã€‚ + + StatusBar.hide(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* アンドロイド +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.show + +ステータス ãƒãƒ¼ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ + + StatusBar.show(); + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* アンドロイド +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.isVisible + +ã“ã®ãƒ—ロパティã€ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ãŒè¡¨ç¤ºã•ã‚Œã‚‹ã‹ã©ã†ã‹ã‚’ãŠèª­ã¿ãã ã•ã„。 + + if (StatusBar.isVisible) { + // do something + } + + +## サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るプラットフォーム + +* iOS +* アンドロイド +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 diff --git a/plugins/cordova-plugin-statusbar/doc/ko/README.md b/plugins/cordova-plugin-statusbar/doc/ko/README.md new file mode 100644 index 0000000..f76ac3e --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/ko/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> `StatusBar`개체 iOS와 안 ë“œ ë¡œì´ë“œ ìƒíƒœ í‘œì‹œì¤„ì„ ì‚¬ìš©ìž ì§€ì • 하려면 몇 가지 ê¸°ëŠ¥ì„ ì œê³µ 합니다. + +## 설치 + + cordova plugin add cordova-plugin-statusbar + + +## 환경 설정 + +#### config.xml + + * **StatusBarOverlaysWebView** (boolean, 기본값: true)입니다. IOS 7, 시작 ì‹œ ìƒíƒœ 표시줄 ì˜¤ë²„ë ˆì´ ë˜ëŠ” WebView 중첩 ë˜ì§€ í™•ì¸ í•©ë‹ˆë‹¤. + + + + + * **StatusBarBackgroundColor** (ìƒ‰ìƒ 16 진수 문ìžì—´ 기본값: #000000). IOSì—ì„œ 7ê³¼ 안 ë“œ ë¡œì´ë“œ 5 시작 ì‹œ 16 진수 문ìžì—´ (#RRGGBB) ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ì„ ì„¤ì • 합니다. + + + + + * **StatusBarStyle** (ìƒíƒœ 표시줄 스타ì¼, 기본값: lightcontent). Ios 7, ìƒíƒœ 표시줄 스타ì¼ì„ 설정 합니다. 사용 가능한 옵션 기본, lightcontent, blacktranslucent, blackopaque. + + + + +### 안 ë“œ ë¡œì´ë“œ ë‹¨ì  + +안 ë“œ ë¡œì´ë“œ 5 + 지침 보다 ê·€í•˜ì˜ ì£¼ìš” ì‘ìš© 프로그램 ìƒíƒœ í‘œì‹œì¤„ì— ëŒ€ í•œ 다른 ìƒ‰ì„ ì‚¬ìš© 하 ì—¬ 지정한 ìƒ‰ìƒ (와 달리 ê· ì¼ í•œ ìƒíƒœ í‘œì‹œì¤„ì˜ ìƒ‰ìƒ ë§Žì€ iOS 7 + 애플 리 ì¼€ì´ ì…˜), `StatusBar.backgroundColorByHexString` ë˜ëŠ” `StatusBar.backgroundColorByName`를 통해 대신 ëŸ°íƒ€ìž„ì— ìƒíƒœ 표시줄 ìƒ‰ì„ ì„¤ì • í•˜ê³ ìž í•  수 있습니다. í•œ 가지 ë°©ë²•ì€ ì¼ ê²ƒìž…ë‹ˆë‹¤. + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## 시작 ì‹œ 숨기기 + +런타임 ë™ì•ˆ ì•„ëž˜ì˜ StatusBar.hide 함수를 사용할 수 있습니다 하지만 ë‹¹ì‹ ì´ ì›í•˜ëŠ” ì‘ìš© 프로그램 시작 ì‹œ 숨겨진 ìƒíƒœ 표시줄, ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ Info.plist íŒŒì¼ ìˆ˜ì • 해야 합니다. + +추가 íŽ¸ì§‘ì´ ë‘ íŠ¹ì„±ì´ ì—†ëŠ” 경우. **"ìƒíƒœ 표시줄 ì²˜ìŒ ìˆ¨ê²¨ì§„"** **"YES"** ë¡œ 설정 하 ê³  **"ë·° 컨트롤러 기반 ìƒíƒœ 표시줄 모양"** **"NO"**ë¡œ 설정 합니다. Xcode, 열쇠 ì—†ì´ ìˆ˜ë™ìœ¼ë¡œ 편집 하는 경우 ê°’ì€: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## 메서드 + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `StatusBar` 개체를 ì •ì˜í•©ë‹ˆë‹¤. + +ì „ì—­ ë²”ìœ„ì— ìžˆì§€ë§Œ ê·¸ê²ƒì€ ë¶ˆê°€ëŠ¥ê¹Œì§€ `deviceready` ì´ë²¤íŠ¸ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## ì†ì„± + + * StatusBar.isVisible + +## 사용 권한 + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +IOS 7, ì˜¤ë²„ë ˆì´ ë˜ëŠ” 하지 WebView 중첩 ìƒíƒœ í‘œì‹œì¤„ì„ í™•ì¸ í•©ë‹ˆë‹¤. + + StatusBar.overlaysWebView(true); + + +## 설명 + +7 iOS, iOS 6 처럼 나타나는 ìƒíƒœ í‘œì‹œì¤„ì„ falseë¡œ 설정 합니다. 다른 함수를 사용 하 ì—¬ì— ë§žê²Œ 스타ì¼ê³¼ ë°°ê²½ 색ìƒì„ 설정 합니다. + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + +## 빠른 예제 + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +기본 ìƒíƒœ 표시줄 (ì–´ë‘ìš´ í…스트, ë°ì€ ë°°ê²½ì— ëŒ€ í•œ)를 사용 합니다. + + StatusBar.styleDefault(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleLightContent + +LightContent ìƒíƒœ 표시줄 (ì–´ë‘ìš´ ë°°ê²½ì— ëŒ€ í•œ 가벼운 í…스트)ì„ ì‚¬ìš© 합니다. + + StatusBar.styleLightContent(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +BlackTranslucent ìƒíƒœ 표시줄 (ì–´ë‘ìš´ ë°°ê²½ì— ëŒ€ í•œ 가벼운 í…스트)ì„ ì‚¬ìš© 합니다. + + StatusBar.styleBlackTranslucent(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +BlackOpaque ìƒíƒœ 표시줄 (ì–´ë‘ìš´ ë°°ê²½ì— ëŒ€ í•œ 가벼운 í…스트)ì„ ì‚¬ìš© 합니다. + + StatusBar.styleBlackOpaque(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Ios 7, StatusBar.statusBarOverlaysWebViewì„ falseë¡œ 설정 하면 설정할 수 있는 ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ ìƒ‰ìƒ ì´ë¦„으로. + + StatusBar.backgroundColorByName("red"); + + +ì§€ì› ë˜ëŠ” 색 ì´ë¦„입니다. + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +16 진수 문ìžì—´ ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ì„ ì„¤ì •í•©ë‹ˆë‹¤. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +CSS 대표 ì†ì„± ì§€ì› ë©ë‹ˆë‹¤. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Ios 7, StatusBar.statusBarOverlaysWebViewì„ falseë¡œ 설정 하면 설정할 수 있는 ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ 16 진수 문ìžì—´ (#RRGGBB)ì— ì˜í•´. + +WP7 ë° WP8ì— ë‹¹ì‹ ì€ ë˜í•œ #AARRGGBB, AA는 알파 값으로 ê°’ì„ ì§€ì •í•  수 있습니다. + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.hide + +숨기기 ìƒíƒœ 표시줄. + + StatusBar.hide(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * 안 ë“œ ë¡œì´ë“œ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.show + +ìƒíƒœ í‘œì‹œì¤„ì„ í‘œì‹œí•©ë‹ˆë‹¤. + + StatusBar.show(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * 안 ë“œ ë¡œì´ë“œ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.isVisible + +ì´ ì†ì„±ì„ ìƒíƒœ 표시줄 표시 ë˜ëŠ” 경우 ì½ê¸°. + + if (StatusBar.isVisible) { + // do something + } + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + + * iOS + * 안 ë“œ ë¡œì´ë“œ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/ko/index.md b/plugins/cordova-plugin-statusbar/doc/ko/index.md new file mode 100644 index 0000000..44de75b --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/ko/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> `StatusBar`개체 iOS와 안 ë“œ ë¡œì´ë“œ ìƒíƒœ í‘œì‹œì¤„ì„ ì‚¬ìš©ìž ì§€ì • 하려면 몇 가지 ê¸°ëŠ¥ì„ ì œê³µ 합니다. + +## 설치 + + cordova plugin add cordova-plugin-statusbar + + +## 환경 설정 + +#### config.xml + +* **StatusBarOverlaysWebView** (boolean, 기본값: true)입니다. IOS 7, 시작 ì‹œ ìƒíƒœ 표시줄 ì˜¤ë²„ë ˆì´ ë˜ëŠ” WebView 중첩 ë˜ì§€ í™•ì¸ í•©ë‹ˆë‹¤. + + + + +* **StatusBarBackgroundColor** (ìƒ‰ìƒ 16 진수 문ìžì—´ 기본값: #000000). Ios 7, 시작 ì‹œ 16 진수 문ìžì—´ (#RRGGBB) ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ì„ ì„¤ì • 합니다. + + + + +* **StatusBarStyle** (ìƒíƒœ 표시줄 스타ì¼, 기본값: lightcontent). Ios 7, ìƒíƒœ 표시줄 스타ì¼ì„ 설정 합니다. 사용 가능한 옵션 기본, lightcontent, blacktranslucent, blackopaque. + + + + +## 시작 ì‹œ 숨기기 + +런타임 ë™ì•ˆ ì•„ëž˜ì˜ StatusBar.hide 함수를 사용할 수 있습니다 하지만 ë‹¹ì‹ ì´ ì›í•˜ëŠ” ì‘ìš© 프로그램 시작 ì‹œ 숨겨진 ìƒíƒœ 표시줄, ì‘ìš© í”„ë¡œê·¸ëž¨ì˜ Info.plist íŒŒì¼ ìˆ˜ì • 해야 합니다. + +추가 íŽ¸ì§‘ì´ ë‘ íŠ¹ì„±ì´ ì—†ëŠ” 경우. **"ìƒíƒœ 표시줄 ì²˜ìŒ ìˆ¨ê²¨ì§„"** **"YES"** ë¡œ 설정 하 ê³  **"ë·° 컨트롤러 기반 ìƒíƒœ 표시줄 모양"** **"NO"**ë¡œ 설정 합니다. Xcode, 열쇠 ì—†ì´ ìˆ˜ë™ìœ¼ë¡œ 편집 하는 경우 ê°’ì€: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## 메서드 + +ì´ í”ŒëŸ¬ê·¸ì¸ ê¸€ë¡œë²Œ `StatusBar` 개체를 ì •ì˜í•©ë‹ˆë‹¤. + +ì „ì—­ ë²”ìœ„ì— ìžˆì§€ë§Œ ê·¸ê²ƒì€ ë¶ˆê°€ëŠ¥ê¹Œì§€ `deviceready` ì´ë²¤íŠ¸ 후. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## ì†ì„± + +* StatusBar.isVisible + +## 사용 권한 + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +IOS 7, ì˜¤ë²„ë ˆì´ ë˜ëŠ” 하지 WebView 중첩 ìƒíƒœ í‘œì‹œì¤„ì„ í™•ì¸ í•©ë‹ˆë‹¤. + + StatusBar.overlaysWebView(true); + + +## 설명 + +7 iOS, iOS 6 처럼 나타나는 ìƒíƒœ í‘œì‹œì¤„ì„ falseë¡œ 설정 합니다. 다른 함수를 사용 하 ì—¬ì— ë§žê²Œ 스타ì¼ê³¼ ë°°ê²½ 색ìƒì„ 설정 합니다. + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS + +## 빠른 예제 + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +기본 ìƒíƒœ 표시줄 (ì–´ë‘ìš´ í…스트, ë°ì€ ë°°ê²½ì— ëŒ€ í•œ)를 사용 합니다. + + StatusBar.styleDefault(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleLightContent + +LightContent ìƒíƒœ 표시줄 (ì–´ë‘ìš´ ë°°ê²½ì— ëŒ€ í•œ 가벼운 í…스트)ì„ ì‚¬ìš© 합니다. + + StatusBar.styleLightContent(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +BlackTranslucent ìƒíƒœ 표시줄 (ì–´ë‘ìš´ ë°°ê²½ì— ëŒ€ í•œ 가벼운 í…스트)ì„ ì‚¬ìš© 합니다. + + StatusBar.styleBlackTranslucent(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +BlackOpaque ìƒíƒœ 표시줄 (ì–´ë‘ìš´ ë°°ê²½ì— ëŒ€ í•œ 가벼운 í…스트)ì„ ì‚¬ìš© 합니다. + + StatusBar.styleBlackOpaque(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Ios 7, StatusBar.statusBarOverlaysWebViewì„ falseë¡œ 설정 하면 설정할 수 있는 ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ ìƒ‰ìƒ ì´ë¦„으로. + + StatusBar.backgroundColorByName("red"); + + +ì§€ì› ë˜ëŠ” 색 ì´ë¦„입니다. + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +16 진수 문ìžì—´ ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ì„ ì„¤ì •í•©ë‹ˆë‹¤. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +CSS 대표 ì†ì„± ì§€ì› ë©ë‹ˆë‹¤. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Ios 7, StatusBar.statusBarOverlaysWebViewì„ falseë¡œ 설정 하면 설정할 수 있는 ìƒíƒœ í‘œì‹œì¤„ì˜ ë°°ê²½ìƒ‰ 16 진수 문ìžì—´ (#RRGGBB)ì— ì˜í•´. + +WP7 ë° WP8ì— ë‹¹ì‹ ì€ ë˜í•œ #AARRGGBB, AA는 알파 값으로 ê°’ì„ ì§€ì •í•  수 있습니다. + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.hide + +숨기기 ìƒíƒœ 표시줄. + + StatusBar.hide(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* 안 ë“œ ë¡œì´ë“œ +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.show + +ìƒíƒœ í‘œì‹œì¤„ì„ í‘œì‹œí•©ë‹ˆë‹¤. + + StatusBar.show(); + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* 안 ë“œ ë¡œì´ë“œ +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.isVisible + +ì´ ì†ì„±ì„ ìƒíƒœ 표시줄 표시 ë˜ëŠ” 경우 ì½ê¸°. + + if (StatusBar.isVisible) { + // do something + } + + +## ì§€ì› ë˜ëŠ” í”Œëž«í¼ + +* iOS +* 안 ë“œ ë¡œì´ë“œ +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 diff --git a/plugins/cordova-plugin-statusbar/doc/pl/README.md b/plugins/cordova-plugin-statusbar/doc/pl/README.md new file mode 100644 index 0000000..1b116cc --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/pl/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> `StatusBar`Obiekt zawiera kilka funkcji, aby dostosować iOS i Android StatusBar. + +## Instalacja + + cordova plugin add cordova-plugin-statusbar + + +## Preferencje + +#### config.xml + + * **StatusBarOverlaysWebView** (boolean, domyÅ›lnie na wartość true). Na iOS 7 zrobić nakÅ‚adki stanu lub nie nakÅ‚adki widoku sieci Web podczas uruchamiania. + + + + + * **StatusBarBackgroundColor** (kolor ciÄ…g szesnastkowy, domyÅ›lnie #000000). Na iOS 7 i Android 5 kolor tÅ‚a stanu przez ciÄ…g szesnastkowy (#RRGGBB) przy starcie systemu. + + + + + * **StatusBarStyle** (stan styl paska, domyÅ›lnie lightcontent.) Na iOS 7 ustawić styl paska stanu. DostÄ™pne opcje domyÅ›lne, lightcontent, blacktranslucent, blackopaque. + + + + +### Dziwactwa Androida + +Android 5 + wytyczne okreÅ›lajÄ… przy użyciu różnych kolorów statusbar niż główne aplikacji kolor (w przeciwieÅ„stwie do stanu jednolitych kolorów wiele aplikacje iOS 7 +), wiÄ™c może chcesz ustawić kolor pasek stanu w czasie wykonywania zamiast za poÅ›rednictwem `StatusBar.backgroundColorByHexString` lub `StatusBar.backgroundColorByName`. Jednym sposobem na to byÅ‚oby: + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## Przy starcie + +Podczas uruchamiania można użyć funkcji StatusBar.hide poniżej, ale jeÅ›li chcesz StatusBar ukryty w uruchamiania aplikacji, należy zmodyfikować plik Info.plist Twojej aplikacji. + +Dodawanie/edycja tych dwóch atrybutów jeÅ›li nie obecny. Ustawianie **"pasek stanu jest poczÄ…tkowo ukryte"** na **"Tak"** i **"Oparte na kontroler stanu paska wyglÄ…d"** na **"Nie"**. JeÅ›li możesz go edytować rÄ™cznie bez Xcode, kluczy i wartoÅ›ci sÄ…: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Metody + +Ten plugin definiuje obiekt globalny `StatusBar`. + +Chociaż w globalnym zasiÄ™gu, to nie dostÄ™pne dopiero po `deviceready` imprezie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## WÅ‚aÅ›ciwoÅ›ci + + * StatusBar.isVisible + +## Uprawnienia + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +Na iOS 7 zrobić statusbar nakÅ‚adki lub nie nakÅ‚adka widoku sieci Web. + + StatusBar.overlaysWebView(true); + + +## Opis + +Na iOS 7 zestaw do false, aby na pasku stanu pojawia siÄ™ jak iOS 6. Ustaw kolor tÅ‚a i styl do korzystania z innych funkcji. + +## ObsÅ‚ugiwane platformy + + * iOS + +## Szybki przykÅ‚ad + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Użyj domyÅ›lnego stanu (ciemny tekst, teÅ‚ Å›wiatÅ‚a). + + StatusBar.styleDefault(); + + +## ObsÅ‚ugiwane platformy + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleLightContent + +Użyj lightContent stanu (Å›wiatÅ‚o tekst, ciemne tÅ‚o). + + StatusBar.styleLightContent(); + + +## ObsÅ‚ugiwane platformy + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Użyj blackTranslucent stanu (Å›wiatÅ‚o tekst, ciemne tÅ‚o). + + StatusBar.styleBlackTranslucent(); + + +## ObsÅ‚ugiwane platformy + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Użyj blackOpaque stanu (Å›wiatÅ‚o tekst, ciemne tÅ‚o). + + StatusBar.styleBlackOpaque(); + + +## ObsÅ‚ugiwane platformy + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Na iOS 7 gdy zostanie ustawiona wartość false, StatusBar.statusBarOverlaysWebView można ustawić kolor tÅ‚a stanu przez nazwÄ™ koloru. + + StatusBar.backgroundColorByName("red"); + + +Nazwy kolorów obsÅ‚ugiwane sÄ…: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## ObsÅ‚ugiwane platformy + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Ustawia kolor tÅ‚a stanu przez ciÄ…g szesnastkowy. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +ObsÅ‚ugiwane sÄ… również wÅ‚aÅ›ciwoÅ›ci CSS. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Na iOS 7 gdy zostanie ustawiona wartość false, StatusBar.statusBarOverlaysWebView można ustawić kolor tÅ‚a stanu przez ciÄ…g szesnastkowy (#RRGGBB). + +Na WP7 i WP8 można również okreÅ›lić wartoÅ›ci jako #AARRGGBB, gdzie AA jest wartoÅ›ciÄ… alfa + +## ObsÅ‚ugiwane platformy + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.hide + +Ukryj pasek stanu. + + StatusBar.hide(); + + +## ObsÅ‚ugiwane platformy + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.show + +Pokazuje pasek stanu. + + StatusBar.show(); + + +## ObsÅ‚ugiwane platformy + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.isVisible + +Czytać tej wÅ‚aÅ›ciwość, aby sprawdzić, czy stanu jest widoczne lub nie. + + if (StatusBar.isVisible) { + // do something + } + + +## ObsÅ‚ugiwane platformy + + * iOS + * Android + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/pl/index.md b/plugins/cordova-plugin-statusbar/doc/pl/index.md new file mode 100644 index 0000000..4f13a37 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/pl/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> `StatusBar`Obiekt zawiera kilka funkcji, aby dostosować iOS i Android StatusBar. + +## Instalacja + + cordova plugin add cordova-plugin-statusbar + + +## Preferencje + +#### config.xml + +* **StatusBarOverlaysWebView** (boolean, domyÅ›lnie na wartość true). Na iOS 7 zrobić nakÅ‚adki stanu lub nie nakÅ‚adki widoku sieci Web podczas uruchamiania. + + + + +* **StatusBarBackgroundColor** (kolor szesnastkowy ciÄ…g, domyÅ›lnie #000000). Na iOS 7 ustawić kolor tÅ‚a stanu przez ciÄ…g szesnastkowy (#RRGGBB) przy starcie systemu. + + + + +* **StatusBarStyle** (stan styl paska, domyÅ›lnie lightcontent.) Na iOS 7 ustawić styl paska stanu. DostÄ™pne opcje domyÅ›lne, lightcontent, blacktranslucent, blackopaque. + + + + +## Przy starcie + +Podczas uruchamiania można użyć funkcji StatusBar.hide poniżej, ale jeÅ›li chcesz StatusBar ukryty w uruchamiania aplikacji, należy zmodyfikować plik Info.plist Twojej aplikacji. + +Dodawanie/edycja tych dwóch atrybutów jeÅ›li nie obecny. Ustawianie **"pasek stanu jest poczÄ…tkowo ukryte"** na **"Tak"** i **"Oparte na kontroler stanu paska wyglÄ…d"** na **"Nie"**. JeÅ›li możesz go edytować rÄ™cznie bez Xcode, kluczy i wartoÅ›ci sÄ…: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Metody + +Ten plugin definiuje obiekt globalny `StatusBar`. + +Chociaż w globalnym zasiÄ™gu, to nie dostÄ™pne dopiero po `deviceready` imprezie. + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## WÅ‚aÅ›ciwoÅ›ci + +* StatusBar.isVisible + +## Uprawnienia + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +Na iOS 7 zrobić statusbar nakÅ‚adki lub nie nakÅ‚adka widoku sieci Web. + + StatusBar.overlaysWebView(true); + + +## Opis + +Na iOS 7 zestaw do false, aby na pasku stanu pojawia siÄ™ jak iOS 6. Ustaw kolor tÅ‚a i styl do korzystania z innych funkcji. + +## ObsÅ‚ugiwane platformy + +* iOS + +## Szybki przykÅ‚ad + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +Użyj domyÅ›lnego stanu (ciemny tekst, teÅ‚ Å›wiatÅ‚a). + + StatusBar.styleDefault(); + + +## ObsÅ‚ugiwane platformy + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleLightContent + +Użyj lightContent stanu (Å›wiatÅ‚o tekst, ciemne tÅ‚o). + + StatusBar.styleLightContent(); + + +## ObsÅ‚ugiwane platformy + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +Użyj blackTranslucent stanu (Å›wiatÅ‚o tekst, ciemne tÅ‚o). + + StatusBar.styleBlackTranslucent(); + + +## ObsÅ‚ugiwane platformy + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +Użyj blackOpaque stanu (Å›wiatÅ‚o tekst, ciemne tÅ‚o). + + StatusBar.styleBlackOpaque(); + + +## ObsÅ‚ugiwane platformy + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +Na iOS 7 gdy zostanie ustawiona wartość false, StatusBar.statusBarOverlaysWebView można ustawić kolor tÅ‚a stanu przez nazwÄ™ koloru. + + StatusBar.backgroundColorByName("red"); + + +Nazwy kolorów obsÅ‚ugiwane sÄ…: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## ObsÅ‚ugiwane platformy + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +Ustawia kolor tÅ‚a stanu przez ciÄ…g szesnastkowy. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +ObsÅ‚ugiwane sÄ… również wÅ‚aÅ›ciwoÅ›ci CSS. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Na iOS 7 gdy zostanie ustawiona wartość false, StatusBar.statusBarOverlaysWebView można ustawić kolor tÅ‚a stanu przez ciÄ…g szesnastkowy (#RRGGBB). + +Na WP7 i WP8 można również okreÅ›lić wartoÅ›ci jako #AARRGGBB, gdzie AA jest wartoÅ›ciÄ… alfa + +## ObsÅ‚ugiwane platformy + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.hide + +Ukryj pasek stanu. + + StatusBar.hide(); + + +## ObsÅ‚ugiwane platformy + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.show + +Pokazuje pasek stanu. + + StatusBar.show(); + + +## ObsÅ‚ugiwane platformy + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.isVisible + +Czytać tej wÅ‚aÅ›ciwość, aby sprawdzić, czy stanu jest widoczne lub nie. + + if (StatusBar.isVisible) { + // do something + } + + +## ObsÅ‚ugiwane platformy + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 diff --git a/plugins/cordova-plugin-statusbar/doc/ru/index.md b/plugins/cordova-plugin-statusbar/doc/ru/index.md new file mode 100644 index 0000000..fdb95ee --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/ru/index.md @@ -0,0 +1,238 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> Объект `StatusBar` предоÑтавлÑет некоторые функции Ð´Ð»Ñ Ð½Ð°Ñтройки ÑтатуÑной панели на iOS и Android. + +## ÐаÑтройки + +#### config.xml + +* **StatusBarOverlaysWebView** (логичеÑкое значение, по умолчанию true). Ð’ iOS 7 определÑет необходимо ли Ñделать наложение ÑтатуÑной панели на WebView при запуÑке или нет. + + + + +* **StatusBarBackgroundColor** (шеÑÑ‚Ð½Ð°Ð´Ñ†Ð°Ñ‚ÐµÑ€Ð¸Ñ‡Ð½Ð°Ñ Ñтрока цвета, Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию #000000). Ðа iOS 7 уÑтановит цвет фона ÑтатуÑной панели при запуÑке, на оÑновании шеÑтнадцатеричной Ñтроки цвета (#RRGGBB). + + + + +* **StatusBarStyle** (ÑÑ‚Ð°Ñ‚ÑƒÑ Ð±Ð°Ñ€ Ñтиль, по умолчанию lightcontent). Ðа iOS 7 уÑтановите Ñтиль Ñтроки ÑоÑтоÑниÑ. ДоÑтупные параметры по умолчанию, lightcontent, blacktranslucent, blackopaque. + + + + +## Скрытие при запуÑке + +Во Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð¶Ð½Ð¾ иÑпользовать функцию StatusBar.hide ниже, но еÑли вы хотите StatusBar быть Ñкрыты при запуÑке приложениÑ, необходимо изменить файл Info.plist вашего приложениÑ. + +Добавьте/измените Ñти два атрибута, еÑли они не приÑутÑтвуют или отличаютÑÑ Ð¾Ñ‚ нижеуказанных значений. УÑтановите значение **«Status bar is initially hidden»** равное **«YES»** и уÑтановите значение **«View controller-based status bar appearance»** на **«NO»**. ЕÑли вы измените его вручную без Xcode, ключи и Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвлÑÑŽÑ‚ÑÑ Ñледующими: + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## Методы + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## Параметры + +* StatusBar.isVisible + +## Ð Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +Ðа iOS 7 Сделайте statusbar overlay или не поверх WebView. + + StatusBar.overlaysWebView(true); + + +## ОпиÑание + +Ðа iOS 7 УÑтановите значение false чтобы Ñделать statusbar поÑвлÑÑŽÑ‚ÑÑ ÐºÐ°Ðº iOS 6. Задайте Ñтиль и цвет фона в ÑоответÑтвии Ñ Ð¸Ñпользованием других функций. + +## Поддерживаемые платформы + +* iOS + +## Краткий пример + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +ИÑпользуйте по умолчанию statusbar (темный текÑÑ‚, Ð´Ð»Ñ Ð»ÐµÐ³ÐºÐ¸Ñ… Ñтола). + + StatusBar.styleDefault(); + + +## Поддерживаемые платформы + +* iOS +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.styleLightContent + +ИÑпользуйте lightContent statusbar (Ñветлый текÑÑ‚, на темном фоне). + + StatusBar.styleLightContent(); + + +## Поддерживаемые платформы + +* iOS +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.styleBlackTranslucent + +ИÑпользуйте blackTranslucent statusbar (Ñветлый текÑÑ‚, на темном фоне). + + StatusBar.styleBlackTranslucent(); + + +## Поддерживаемые платформы + +* iOS +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.styleBlackOpaque + +ИÑпользуйте blackOpaque statusbar (Ñветлый текÑÑ‚, на темном фоне). + + StatusBar.styleBlackOpaque(); + + +## Поддерживаемые платформы + +* iOS +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.backgroundColorByName + +Ðа iOS 7 когда StatusBar.statusBarOverlaysWebView приÑвоено значение false, можно задать цвет фона Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° statusbar по имени цвета. + + StatusBar.backgroundColorByName("red"); + + +Имена поддерживаемых цветов ÑвлÑÑŽÑ‚ÑÑ: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## Поддерживаемые платформы + +* iOS +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.backgroundColorByHexString + +Задает цвет фона Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° statusbar, шеÑÑ‚Ð½Ð°Ð´Ñ†Ð°Ñ‚ÐµÑ€Ð¸Ñ‡Ð½Ð°Ñ Ñтрока. + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +Также поддерживаютÑÑ ÑвойÑтва CSS Ñтенографию. + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +Ðа iOS 7 когда StatusBar.statusBarOverlaysWebView приÑвоено значение false, можно задать цвет фона Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° statusbar, шеÑÑ‚Ð½Ð°Ð´Ñ†Ð°Ñ‚ÐµÑ€Ð¸Ñ‡Ð½Ð°Ñ Ñтрока (#RRGGBB). + +Ðа WP7 и WP8 также можно указать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÐºÐ°Ðº #AARRGGBB, где AA — Ñто альфа-значение + +## Поддерживаемые платформы + +* iOS +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.hide + +Скройте Ñтроку ÑоÑтоÑÐ½Ð¸Ñ statusbar. + + StatusBar.hide(); + + +## Поддерживаемые платформы + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.show + +Показывает Ñтроку ÑоÑтоÑÐ½Ð¸Ñ statusbar. + + StatusBar.show(); + + +## Поддерживаемые платформы + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 + +# StatusBar.isVisible + +Чтение Ñто ÑвойÑтво, чтобы увидеть, еÑли statusbar ÑвлÑетÑÑ Ð²Ð¸Ð´Ð¸Ð¼Ñ‹Ð¼ или нет. + + if (StatusBar.isVisible) { + // do something + } + + +## Поддерживаемые платформы + +* iOS +* Android +* Windows Phone 7 +* Windows Phone 8 diff --git a/plugins/cordova-plugin-statusbar/doc/zh/README.md b/plugins/cordova-plugin-statusbar/doc/zh/README.md new file mode 100644 index 0000000..8a63699 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/zh/README.md @@ -0,0 +1,276 @@ + + +# cordova-plugin-statusbar + +[![Build Status](https://travis-ci.org/apache/cordova-plugin-statusbar.svg)](https://travis-ci.org/apache/cordova-plugin-statusbar) + +# StatusBar + +> `StatusBar`物件æ供了一些功能,自訂的 iOS å’Œ Android 狀態列。 + +## å®‰è£ + + cordova plugin add cordova-plugin-statusbar + + +## 首é¸é … + +#### config.xml + + * **StatusBarOverlaysWebView**(布林值,é è¨­å€¼ç‚º true)。在 iOS 7,使狀態列覆蓋或ä¸è¦†è“‹ web 視圖在啟動時。 + + + + + * **StatusBarBackgroundColor**(é¡è‰²å六進ä½å­—串,é è¨­å€¼ç‚º #000000)。IOS 7 å’Œ Android 5,由å六進ä½å­—串 (#RRGGBB) 在啟動時設置狀態列的背景色。 + + + + + * **狀態列**(狀態列樣å¼ï¼Œé è¨­å€¼ç‚º lightcontent)。在 iOS 7,設置的狀態橫æ¢åœ–樣å¼ã€‚å¯ç”¨çš„é¸é …é è¨­ï¼Œlightcontent,blacktranslucent,blackopaque。 + + + + +### Android 的怪癖 + +Android çš„ 5 + 準則指定使用ä¸åŒçš„é¡è‰²æ¯”您主è¦çš„應用程å¼ç‹€æ…‹æ¬„é¡è‰² (ä¸åƒå¾ˆå¤š iOS 7 + 應用程å¼çš„統一狀態列é¡è‰²),所以你å¯èƒ½æƒ³è¦è¨­ç½®åœ¨é‹è¡Œæ™‚顯示狀態列é¡è‰²è€Œä¸æ˜¯é€šéŽ`StatusBar.backgroundColorByHexString`或`StatusBar.backgroundColorByName`。 一個的方å¼åšåˆ°é€™ä¸€é»žå°‡æ˜¯: + +```js +if (cordova.platformId == 'android') { + StatusBar.backgroundColorByHexString("#333"); +} +``` + +## åœ¨å•Ÿå‹•æ™‚éš±è— + +在é‹è¡Œæ™‚期間,你å¯ä»¥ä½¿ç”¨ StatusBar.hide 函數下é¢ï¼Œä½†å¦‚果你想è¦é¡¯ç¤ºç‹€æ…‹åˆ—éš±è—在應用程å¼å•Ÿå‹•æ™‚,你必須修改你的應用程å¼çš„ Info.plist 檔。 + +添加編輯這兩個屬性,如果ä¸å­˜åœ¨ã€‚ å°‡**"狀態列最åˆéš±è—"**設置為**"YES"**å’Œ**"視圖基於控制器的狀態列外觀"**設置為**"å¦"**。 如果您手動編輯它沒有 Xcode,éµå’Œå€¼æ˜¯ï¼š + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## 方法 + +這個外掛程å¼å®šç¾©å…¨åŸŸ `StatusBar` 物件。 + +雖然在全çƒç¯„åœå…§ï¼Œå®ƒä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + + * StatusBar.overlaysWebView + * StatusBar.styleDefault + * StatusBar.styleLightContent + * StatusBar.styleBlackTranslucent + * StatusBar.styleBlackOpaque + * StatusBar.backgroundColorByName + * StatusBar.backgroundColorByHexString + * StatusBar.hide + * StatusBar.show + +## 屬性 + + * StatusBar.isVisible + +## 許å¯æ¬Š + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +在 iOS 7,使狀態列覆蓋或ä¸è¦†è“‹ web 視圖。 + + StatusBar.overlaysWebView(true); + + +## 說明 + +在 iOS 7,設置為 false,使狀態列出ç¾åƒ iOS 6。設置樣å¼å’ŒèƒŒæ™¯é¡è‰²ï¼Œé©åˆä½¿ç”¨å…¶ä»–函數。 + +## 支æ´çš„平臺 + + * iOS + +## 快速的示例 + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +使用é è¨­ç‹€æ…‹åˆ— (淺色背景深色文本)。 + + StatusBar.styleDefault(); + + +## 支æ´çš„平臺 + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleLightContent + +使用 lightContent 狀態列 (深色背景光文本)。 + + StatusBar.styleLightContent(); + + +## 支æ´çš„平臺 + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +使用 blackTranslucent 狀態列 (深色背景光文本)。 + + StatusBar.styleBlackTranslucent(); + + +## 支æ´çš„平臺 + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +使用 blackOpaque 狀態列 (深色背景光文本)。 + + StatusBar.styleBlackOpaque(); + + +## 支æ´çš„平臺 + + * iOS + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +在 iOS 7,當您將 StatusBar.statusBarOverlaysWebView 設置為 false,你å¯ä»¥è¨­ç½®ç‹€æ…‹åˆ—的背景色的é¡è‰²å稱。 + + StatusBar.backgroundColorByName("red"); + + +支æ´çš„é¡è‰²å稱是: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## 支æ´çš„平臺 + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +ç”±å六進ä½å­—串設置狀態列的背景色。 + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +æ­¤å¤–æ”¯æ´ CSS 速記屬性。 + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +在 iOS 7,當將 StatusBar.statusBarOverlaysWebView 設置為 false,您å¯ä»¥è¨­ç½®ç‹€æ…‹åˆ—的背景色由å六進ä½å­—串 (#RRGGBB)。 + +WP7 å’Œ WP8 您還å¯ä»¥æŒ‡å®šå€¼ç‚º #AARRGGBB,其中 AA 是 Alpha 值 + +## 支æ´çš„平臺 + + * iOS + * Android 5+ + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.hide + +éš±è—狀態列。 + + StatusBar.hide(); + + +## 支æ´çš„平臺 + + * iOS + * Android 系統 + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.show + +顯示狀態列。 + + StatusBar.show(); + + +## 支æ´çš„平臺 + + * iOS + * Android 系統 + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 + +# StatusBar.isVisible + +讀å–此屬性,以查看狀態列是å¦å¯è¦‹ã€‚ + + if (StatusBar.isVisible) { + // do something + } + + +## 支æ´çš„平臺 + + * iOS + * Android 系統 + * Windows Phone 7 + * Windows Phone 8 + * Windows Phone 8.1 \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/doc/zh/index.md b/plugins/cordova-plugin-statusbar/doc/zh/index.md new file mode 100644 index 0000000..a8805da --- /dev/null +++ b/plugins/cordova-plugin-statusbar/doc/zh/index.md @@ -0,0 +1,262 @@ + + +# cordova-plugin-statusbar + +# StatusBar + +> `StatusBar`物件æ供了一些功能,自訂的 iOS å’Œ Android 狀態列。 + +## å®‰è£ + + cordova plugin add cordova-plugin-statusbar + + +## 首é¸é … + +#### config.xml + +* **StatusBarOverlaysWebView**(布林值,é è¨­å€¼ç‚º true)。在 iOS 7,使狀態列覆蓋或ä¸è¦†è“‹ web 視圖在啟動時。 + + + + +* **StatusBarBackgroundColor**(é¡è‰²å六進ä½å­—串,é è¨­å€¼ç‚º #000000)。在 iOS 7,通éŽä¸€å€‹å六進ä½å­—串 (#RRGGBB) 在啟動時設置狀態列的背景色。 + + + + +* **狀態列**(狀態列樣å¼ï¼Œé è¨­å€¼ç‚º lightcontent)。在 iOS 7,設置的狀態橫æ¢åœ–樣å¼ã€‚å¯ç”¨çš„é¸é …é è¨­ï¼Œlightcontent,blacktranslucent,blackopaque。 + + + + +## åœ¨å•Ÿå‹•æ™‚éš±è— + +在é‹è¡Œæ™‚期間,你å¯ä»¥ä½¿ç”¨ StatusBar.hide 函數下é¢ï¼Œä½†å¦‚果你想è¦é¡¯ç¤ºç‹€æ…‹åˆ—éš±è—在應用程å¼å•Ÿå‹•æ™‚,你必須修改你的應用程å¼çš„ Info.plist 檔。 + +添加編輯這兩個屬性,如果ä¸å­˜åœ¨ã€‚ å°‡**"狀態列最åˆéš±è—"**設置為**"YES"**å’Œ**"視圖基於控制器的狀態列外觀"**設置為**"å¦"**。 如果您手動編輯它沒有 Xcode,éµå’Œå€¼æ˜¯ï¼š + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + +## 方法 + +這個外掛程å¼å®šç¾©å…¨åŸŸ `StatusBar` 物件。 + +雖然在全çƒç¯„åœå…§ï¼Œå®ƒä¸å¯ç”¨ç›´åˆ° `deviceready` 事件之後。 + + document.addEventListener("deviceready", onDeviceReady, false); + function onDeviceReady() { + console.log(StatusBar); + } + + +* StatusBar.overlaysWebView +* StatusBar.styleDefault +* StatusBar.styleLightContent +* StatusBar.styleBlackTranslucent +* StatusBar.styleBlackOpaque +* StatusBar.backgroundColorByName +* StatusBar.backgroundColorByHexString +* StatusBar.hide +* StatusBar.show + +## 屬性 + +* StatusBar.isVisible + +## 許å¯æ¬Š + +#### config.xml + + + + + + +# StatusBar.overlaysWebView + +在 iOS 7,使狀態列覆蓋或ä¸è¦†è“‹ web 視圖。 + + StatusBar.overlaysWebView(true); + + +## 說明 + +在 iOS 7,設置為 false,使狀態列出ç¾åƒ iOS 6。設置樣å¼å’ŒèƒŒæ™¯é¡è‰²ï¼Œé©åˆä½¿ç”¨å…¶ä»–函數。 + +## 支æ´çš„平臺 + +* iOS + +## 快速的示例 + + StatusBar.overlaysWebView(true); + StatusBar.overlaysWebView(false); + + +# StatusBar.styleDefault + +使用é è¨­ç‹€æ…‹åˆ— (淺色背景深色文本)。 + + StatusBar.styleDefault(); + + +## 支æ´çš„平臺 + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleLightContent + +使用 lightContent 狀態列 (深色背景光文本)。 + + StatusBar.styleLightContent(); + + +## 支æ´çš„平臺 + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackTranslucent + +使用 blackTranslucent 狀態列 (深色背景光文本)。 + + StatusBar.styleBlackTranslucent(); + + +## 支æ´çš„平臺 + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.styleBlackOpaque + +使用 blackOpaque 狀態列 (深色背景光文本)。 + + StatusBar.styleBlackOpaque(); + + +## 支æ´çš„平臺 + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByName + +在 iOS 7,當您將 StatusBar.statusBarOverlaysWebView 設置為 false,你å¯ä»¥è¨­ç½®ç‹€æ…‹åˆ—的背景色的é¡è‰²å稱。 + + StatusBar.backgroundColorByName("red"); + + +支æ´çš„é¡è‰²å稱是: + + black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + + +## 支æ´çš„平臺 + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.backgroundColorByHexString + +ç”±å六進ä½å­—串設置狀態列的背景色。 + + StatusBar.backgroundColorByHexString("#C0C0C0"); + + +æ­¤å¤–æ”¯æ´ CSS 速記屬性。 + + StatusBar.backgroundColorByHexString("#333"); // => #333333 + StatusBar.backgroundColorByHexString("#FAB"); // => #FFAABB + + +在 iOS 7,當將 StatusBar.statusBarOverlaysWebView 設置為 false,您å¯ä»¥è¨­ç½®ç‹€æ…‹åˆ—的背景色由å六進ä½å­—串 (#RRGGBB)。 + +WP7 å’Œ WP8 您還å¯ä»¥æŒ‡å®šå€¼ç‚º #AARRGGBB,其中 AA 是 Alpha 值 + +## 支æ´çš„平臺 + +* iOS +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.hide + +éš±è—狀態列。 + + StatusBar.hide(); + + +## 支æ´çš„平臺 + +* iOS +* 安å“系統 +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.show + +顯示狀態列。 + + StatusBar.show(); + + +## 支æ´çš„平臺 + +* iOS +* 安å“系統 +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 + +# StatusBar.isVisible + +讀å–此屬性,以查看狀態列是å¦å¯è¦‹ã€‚ + + if (StatusBar.isVisible) { + // do something + } + + +## 支æ´çš„平臺 + +* iOS +* 安å“系統 +* Windows Phone 7 +* Windows Phone 8 +* Windows Phone 8.1 diff --git a/plugins/cordova-plugin-statusbar/package.json b/plugins/cordova-plugin-statusbar/package.json new file mode 100644 index 0000000..29db0f0 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/package.json @@ -0,0 +1,81 @@ +{ + "_from": "cordova-plugin-statusbar@^2.4.2", + "_id": "cordova-plugin-statusbar@2.4.2", + "_inBundle": false, + "_integrity": "sha1-/B+9wNjXAzp+jh8ff/FnrJvU+vY=", + "_location": "/cordova-plugin-statusbar", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "cordova-plugin-statusbar@^2.4.2", + "name": "cordova-plugin-statusbar", + "escapedName": "cordova-plugin-statusbar", + "rawSpec": "^2.4.2", + "saveSpec": null, + "fetchSpec": "^2.4.2" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/cordova-plugin-statusbar/-/cordova-plugin-statusbar-2.4.2.tgz", + "_shasum": "fc1fbdc0d8d7033a7e8e1f1f7ff167ac9bd4faf6", + "_spec": "cordova-plugin-statusbar@^2.4.2", + "_where": "/home/thrrgilag/workspace/cordova/Goober", + "author": { + "name": "Apache Software Foundation" + }, + "bugs": { + "url": "https://issues.apache.org/jira/browse/CB" + }, + "bundleDependencies": false, + "cordova": { + "id": "cordova-plugin-statusbar", + "platforms": [ + "android", + "ios", + "wp7", + "wp8", + "windows" + ] + }, + "deprecated": false, + "description": "Cordova StatusBar Plugin", + "devDependencies": { + "jshint": "^2.6.0" + }, + "engines": { + "cordovaDependencies": { + "0.1.0": { + "cordova": ">=3.0.0" + }, + "3.0.0": { + "cordova": ">100" + } + } + }, + "homepage": "https://github.com/apache/cordova-plugin-statusbar#readme", + "keywords": [ + "cordova", + "statusbar", + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "cordova-wp7", + "cordova-wp8", + "cordova-windows" + ], + "license": "Apache-2.0", + "name": "cordova-plugin-statusbar", + "repository": { + "type": "git", + "url": "git+https://github.com/apache/cordova-plugin-statusbar.git" + }, + "scripts": { + "jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests", + "test": "npm run jshint" + }, + "types": "./types/index.d.ts", + "version": "2.4.2" +} diff --git a/plugins/cordova-plugin-statusbar/plugin.xml b/plugins/cordova-plugin-statusbar/plugin.xml new file mode 100644 index 0000000..04d6c18 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/plugin.xml @@ -0,0 +1,99 @@ + + + + + StatusBar + Cordova StatusBar Plugin + Apache 2.0 + cordova,statusbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/cordova-plugin-statusbar/src/android/StatusBar.java b/plugins/cordova-plugin-statusbar/src/android/StatusBar.java new file mode 100644 index 0000000..714c30e --- /dev/null +++ b/plugins/cordova-plugin-statusbar/src/android/StatusBar.java @@ -0,0 +1,276 @@ +/* + * 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.statusbar; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Build; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaArgs; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginResult; +import org.json.JSONException; +import java.util.Arrays; + +public class StatusBar extends CordovaPlugin { + private static final String TAG = "StatusBar"; + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The CordovaWebView Cordova is running in. + */ + @Override + public void initialize(final CordovaInterface cordova, CordovaWebView webView) { + LOG.v(TAG, "StatusBar: initialization"); + super.initialize(cordova, webView); + + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Clear flag FLAG_FORCE_NOT_FULLSCREEN which is set initially + // by the Cordova. + Window window = cordova.getActivity().getWindow(); + window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + + // Read 'StatusBarBackgroundColor' from config.xml, default is #000000. + setStatusBarBackgroundColor(preferences.getString("StatusBarBackgroundColor", "#000000")); + + // Read 'StatusBarStyle' from config.xml, default is 'lightcontent'. + setStatusBarStyle(preferences.getString("StatusBarStyle", "lightcontent")); + } + }); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return True if the action was valid, false otherwise. + */ + @Override + public boolean execute(final String action, final CordovaArgs args, final CallbackContext callbackContext) throws JSONException { + LOG.v(TAG, "Executing action: " + action); + final Activity activity = this.cordova.getActivity(); + final Window window = activity.getWindow(); + + if ("_ready".equals(action)) { + boolean statusBarVisible = (window.getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0; + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, statusBarVisible)); + return true; + } + + if ("show".equals(action)) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // SYSTEM_UI_FLAG_FULLSCREEN is available since JellyBean, but we + // use KitKat here to be aligned with "Fullscreen" preference + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + int uiOptions = window.getDecorView().getSystemUiVisibility(); + uiOptions &= ~View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + uiOptions &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + + window.getDecorView().setSystemUiVisibility(uiOptions); + } + + // CB-11197 We still need to update LayoutParams to force status bar + // to be hidden when entering e.g. text fields + window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + }); + return true; + } + + if ("hide".equals(action)) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // SYSTEM_UI_FLAG_FULLSCREEN is available since JellyBean, but we + // use KitKat here to be aligned with "Fullscreen" preference + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + int uiOptions = window.getDecorView().getSystemUiVisibility() + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_FULLSCREEN; + + window.getDecorView().setSystemUiVisibility(uiOptions); + } + + // CB-11197 We still need to update LayoutParams to force status bar + // to be hidden when entering e.g. text fields + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + }); + return true; + } + + if ("backgroundColorByHexString".equals(action)) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + setStatusBarBackgroundColor(args.getString(0)); + } catch (JSONException ignore) { + LOG.e(TAG, "Invalid hexString argument, use f.i. '#777777'"); + } + } + }); + return true; + } + + if ("overlaysWebView".equals(action)) { + if (Build.VERSION.SDK_INT >= 21) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + setStatusBarTransparent(args.getBoolean(0)); + } catch (JSONException ignore) { + LOG.e(TAG, "Invalid boolean argument"); + } + } + }); + return true; + } + else return args.getBoolean(0) == false; + } + + if ("styleDefault".equals(action)) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + setStatusBarStyle("default"); + } + }); + return true; + } + + if ("styleLightContent".equals(action)) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + setStatusBarStyle("lightcontent"); + } + }); + return true; + } + + if ("styleBlackTranslucent".equals(action)) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + setStatusBarStyle("blacktranslucent"); + } + }); + return true; + } + + if ("styleBlackOpaque".equals(action)) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + setStatusBarStyle("blackopaque"); + } + }); + return true; + } + + return false; + } + + private void setStatusBarBackgroundColor(final String colorPref) { + if (Build.VERSION.SDK_INT >= 21) { + if (colorPref != null && !colorPref.isEmpty()) { + final Window window = cordova.getActivity().getWindow(); + // Method and constants not available on all SDKs but we want to be able to compile this code with any SDK + window.clearFlags(0x04000000); // SDK 19: WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.addFlags(0x80000000); // SDK 21: WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + try { + // Using reflection makes sure any 5.0+ device will work without having to compile with SDK level 21 + window.getClass().getMethod("setStatusBarColor", int.class).invoke(window, Color.parseColor(colorPref)); + } catch (IllegalArgumentException ignore) { + LOG.e(TAG, "Invalid hexString argument, use f.i. '#999999'"); + } catch (Exception ignore) { + // this should not happen, only in case Android removes this method in a version > 21 + LOG.w(TAG, "Method window.setStatusBarColor not found for SDK level " + Build.VERSION.SDK_INT); + } + } + } + } + + private void setStatusBarTransparent(final boolean transparent) { + if (Build.VERSION.SDK_INT >= 21) { + final Window window = cordova.getActivity().getWindow(); + if (transparent) { + window.getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + window.setStatusBarColor(Color.TRANSPARENT); + } + else { + window.getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_VISIBLE); + } + } + } + + private void setStatusBarStyle(final String style) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (style != null && !style.isEmpty()) { + View decorView = cordova.getActivity().getWindow().getDecorView(); + int uiOptions = decorView.getSystemUiVisibility(); + + String[] darkContentStyles = { + "default", + }; + + String[] lightContentStyles = { + "lightcontent", + "blacktranslucent", + "blackopaque", + }; + + if (Arrays.asList(darkContentStyles).contains(style.toLowerCase())) { + decorView.setSystemUiVisibility(uiOptions | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + return; + } + + if (Arrays.asList(lightContentStyles).contains(style.toLowerCase())) { + decorView.setSystemUiVisibility(uiOptions & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + return; + } + + LOG.e(TAG, "Invalid style, must be either 'default', 'lightcontent' or the deprecated 'blacktranslucent' and 'blackopaque'"); + } + } + } +} diff --git a/plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js b/plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js new file mode 100644 index 0000000..3290d58 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/src/browser/StatusBarProxy.js @@ -0,0 +1,50 @@ +/* + * 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. + * + */ + +function notSupported(win,fail) { + // + console.log('StatusBar is not supported'); + setTimeout(function(){ + if (win) { + win(); + } + // note that while it is not explicitly supported, it does not fail + // this is really just here to allow developers to test their code in the browser + // and if we fail, then their app might as well. -jm + },0); +} + +module.exports = { + isVisible: false, + styleBlackTranslucent:notSupported, + styleDefault:notSupported, + styleLightContent:notSupported, + styleBlackOpaque:notSupported, + overlaysWebView:notSupported, + styleLightContect: notSupported, + backgroundColorByName: notSupported, + backgroundColorByHexString: notSupported, + hide: notSupported, + show: notSupported, + _ready:notSupported +}; + +require("cordova/exec/proxy").add("StatusBar", module.exports); + diff --git a/plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.h b/plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.h new file mode 100644 index 0000000..0be08cc --- /dev/null +++ b/plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.h @@ -0,0 +1,50 @@ +/* + 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 +#import + +@interface CDVStatusBar : CDVPlugin { + @protected + BOOL _statusBarOverlaysWebView; + UIView* _statusBarBackgroundView; + BOOL _uiviewControllerBasedStatusBarAppearance; + UIColor* _statusBarBackgroundColor; + NSString* _eventsCallbackId; +} + +@property (atomic, assign) BOOL statusBarOverlaysWebView; +@property (atomic, assign) BOOL statusBarVisible; + +- (void) overlaysWebView:(CDVInvokedUrlCommand*)command; + +- (void) styleDefault:(CDVInvokedUrlCommand*)command; +- (void) styleLightContent:(CDVInvokedUrlCommand*)command; +- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command; +- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command; + +- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command; +- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command; + +- (void) hide:(CDVInvokedUrlCommand*)command; +- (void) show:(CDVInvokedUrlCommand*)command; + +- (void) _ready:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.m b/plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.m new file mode 100644 index 0000000..c67f137 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/src/ios/CDVStatusBar.m @@ -0,0 +1,479 @@ +/* + 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. + */ + +/* + NOTE: plugman/cordova cli should have already installed this, + but you need the value UIViewControllerBasedStatusBarAppearance + in your Info.plist as well to set the styles in iOS 7 + */ + +#import "CDVStatusBar.h" +#import +#import + +static const void *kHideStatusBar = &kHideStatusBar; +static const void *kStatusBarStyle = &kStatusBarStyle; + +@interface CDVViewController (StatusBar) + +@property (nonatomic, retain) id sb_hideStatusBar; +@property (nonatomic, retain) id sb_statusBarStyle; + +@end + +@implementation CDVViewController (StatusBar) + +@dynamic sb_hideStatusBar; +@dynamic sb_statusBarStyle; + +- (id)sb_hideStatusBar { + return objc_getAssociatedObject(self, kHideStatusBar); +} + +- (void)setSb_hideStatusBar:(id)newHideStatusBar { + objc_setAssociatedObject(self, kHideStatusBar, newHideStatusBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (id)sb_statusBarStyle { + return objc_getAssociatedObject(self, kStatusBarStyle); +} + +- (void)setSb_statusBarStyle:(id)newStatusBarStyle { + objc_setAssociatedObject(self, kStatusBarStyle, newStatusBarStyle, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL) prefersStatusBarHidden { + return [self.sb_hideStatusBar boolValue]; +} + +- (UIStatusBarStyle)preferredStatusBarStyle +{ + return (UIStatusBarStyle)[self.sb_statusBarStyle intValue]; +} + +@end + + +@interface CDVStatusBar () +- (void)fireTappedEvent; +- (void)updateIsVisible:(BOOL)visible; +@end + +@implementation CDVStatusBar + +- (id)settingForKey:(NSString*)key +{ + return [self.commandDelegate.settings objectForKey:[key lowercaseString]]; +} + +- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context +{ + if ([keyPath isEqual:@"statusBarHidden"]) { + NSNumber* newValue = [change objectForKey:NSKeyValueChangeNewKey]; + [self updateIsVisible:![newValue boolValue]]; + } +} + +-(void)cordovaViewWillAppear:(NSNotification*)notification +{ + [self resizeWebView]; +} + +-(void)statusBarDidChangeFrame:(NSNotification*)notification +{ + //add a small delay ( 0.1 seconds ) or statusbar size will be wrong + __weak CDVStatusBar* weakSelf = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [weakSelf resizeStatusBarBackgroundView]; + [weakSelf resizeWebView]; + }); +} + +- (void)pluginInitialize +{ + // init + NSNumber* uiviewControllerBasedStatusBarAppearance = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]; + _uiviewControllerBasedStatusBarAppearance = (uiviewControllerBasedStatusBarAppearance == nil || [uiviewControllerBasedStatusBarAppearance boolValue]); + + // observe the statusBarHidden property + [[UIApplication sharedApplication] addObserver:self forKeyPath:@"statusBarHidden" options:NSKeyValueObservingOptionNew context:NULL]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarDidChangeFrame:) name: UIApplicationDidChangeStatusBarFrameNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cordovaViewWillAppear:) name: @"CDVViewWillAppearNotification" object:nil]; + + _statusBarOverlaysWebView = YES; // default + + [self initializeStatusBarBackgroundView]; + + self.viewController.view.autoresizesSubviews = YES; + + NSString* setting; + + setting = @"StatusBarBackgroundColor"; + if ([self settingForKey:setting]) { + [self _backgroundColorByHexString:[self settingForKey:setting]]; + } + + setting = @"StatusBarStyle"; + if ([self settingForKey:setting]) { + [self setStatusBarStyle:[self settingForKey:setting]]; + } + + setting = @"StatusBarDefaultScrollToTop"; + if ([self settingForKey:setting]) { + self.webView.scrollView.scrollsToTop = [(NSNumber*)[self settingForKey:setting] boolValue]; + } else { + self.webView.scrollView.scrollsToTop = NO; + } + + // blank scroll view to intercept status bar taps + UIScrollView *fakeScrollView = [[UIScrollView alloc] initWithFrame:UIScreen.mainScreen.bounds]; + fakeScrollView.delegate = self; + fakeScrollView.scrollsToTop = YES; + [self.viewController.view addSubview:fakeScrollView]; // Add scrollview to the view heirarchy so that it will begin accepting status bar taps + [self.viewController.view sendSubviewToBack:fakeScrollView]; // Send it to the very back of the view heirarchy + fakeScrollView.contentSize = CGSizeMake(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height * 2.0f); // Make the scroll view longer than the screen itself + fakeScrollView.contentOffset = CGPointMake(0.0f, UIScreen.mainScreen.bounds.size.height); // Scroll down so a tap will take scroll view back to the top + + _statusBarVisible = ![UIApplication sharedApplication].isStatusBarHidden; +} + +- (void)onReset { + _eventsCallbackId = nil; +} + +- (void)fireTappedEvent { + if (_eventsCallbackId == nil) { + return; + } + NSDictionary* payload = @{@"type": @"tap"}; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:payload]; + [result setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId]; +} + +- (void)updateIsVisible:(BOOL)visible { + if (_eventsCallbackId == nil) { + return; + } + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:visible]; + [result setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId]; +} + +- (void) _ready:(CDVInvokedUrlCommand*)command +{ + _eventsCallbackId = command.callbackId; + [self updateIsVisible:![UIApplication sharedApplication].statusBarHidden]; + NSString* setting = @"StatusBarOverlaysWebView"; + if ([self settingForKey:setting]) { + self.statusBarOverlaysWebView = [(NSNumber*)[self settingForKey:setting] boolValue]; + if (self.statusBarOverlaysWebView) { + [self resizeWebView]; + } + } +} + +- (void) initializeStatusBarBackgroundView +{ + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + + if ([[UIApplication sharedApplication]statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown && + statusBarFrame.size.height + statusBarFrame.origin.y == [self.viewController.view.window bounds].size.height) { + + // When started in upside-down orientation on iOS 7, status bar will be bound to lower edge of the + // screen (statusBarFrame.origin.y will be somewhere around screen height). In this case we need to + // correct frame's coordinates + statusBarFrame.origin.y = 0; + } + + _statusBarBackgroundView = [[UIView alloc] initWithFrame:statusBarFrame]; + _statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor; + _statusBarBackgroundView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin); + _statusBarBackgroundView.autoresizesSubviews = YES; +} + +- (void) setStatusBarOverlaysWebView:(BOOL)statusBarOverlaysWebView +{ + // we only care about the latest iOS version or a change in setting + if (statusBarOverlaysWebView == _statusBarOverlaysWebView) { + return; + } + + _statusBarOverlaysWebView = statusBarOverlaysWebView; + + [self resizeWebView]; + + if (statusBarOverlaysWebView) { + + [_statusBarBackgroundView removeFromSuperview]; + + } else { + + [self initializeStatusBarBackgroundView]; + [self.webView.superview addSubview:_statusBarBackgroundView]; + + } + +} + +- (BOOL) statusBarOverlaysWebView +{ + return _statusBarOverlaysWebView; +} + +- (void) overlaysWebView:(CDVInvokedUrlCommand*)command +{ + id value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSNumber class]])) { + value = [NSNumber numberWithBool:YES]; + } + + self.statusBarOverlaysWebView = [value boolValue]; +} + +- (void) refreshStatusBarAppearance +{ + SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate"); + if ([self.viewController respondsToSelector:sel]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [self.viewController performSelector:sel withObject:nil]; +#pragma clang diagnostic pop + } +} + +- (void) setStyleForStatusBar:(UIStatusBarStyle)style +{ + if (_uiviewControllerBasedStatusBarAppearance) { + CDVViewController* vc = (CDVViewController*)self.viewController; + vc.sb_statusBarStyle = [NSNumber numberWithInt:style]; + [self refreshStatusBarAppearance]; + + } else { + [[UIApplication sharedApplication] setStatusBarStyle:style]; + } +} + +- (void) setStatusBarStyle:(NSString*)statusBarStyle +{ + // default, lightContent, blackTranslucent, blackOpaque + NSString* lcStatusBarStyle = [statusBarStyle lowercaseString]; + + if ([lcStatusBarStyle isEqualToString:@"default"]) { + [self styleDefault:nil]; + } else if ([lcStatusBarStyle isEqualToString:@"lightcontent"]) { + [self styleLightContent:nil]; + } else if ([lcStatusBarStyle isEqualToString:@"blacktranslucent"]) { + [self styleBlackTranslucent:nil]; + } else if ([lcStatusBarStyle isEqualToString:@"blackopaque"]) { + [self styleBlackOpaque:nil]; + } +} + +- (void) styleDefault:(CDVInvokedUrlCommand*)command +{ + [self setStyleForStatusBar:UIStatusBarStyleDefault]; +} + +- (void) styleLightContent:(CDVInvokedUrlCommand*)command +{ + [self setStyleForStatusBar:UIStatusBarStyleLightContent]; +} + +- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command +{ + [self setStyleForStatusBar:UIStatusBarStyleLightContent]; +} + +- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command +{ + [self setStyleForStatusBar:UIStatusBarStyleLightContent]; +} + +- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command +{ + id value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSString class]])) { + value = @"black"; + } + + SEL selector = NSSelectorFromString([value stringByAppendingString:@"Color"]); + if ([UIColor respondsToSelector:selector]) { + _statusBarBackgroundView.backgroundColor = [UIColor performSelector:selector]; + } +} + +- (void) _backgroundColorByHexString:(NSString*)hexString +{ + unsigned int rgbValue = 0; + NSScanner* scanner = [NSScanner scannerWithString:hexString]; + [scanner setScanLocation:1]; + [scanner scanHexInt:&rgbValue]; + + _statusBarBackgroundColor = [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; + _statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor; +} + +- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command +{ + NSString* value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSString class]])) { + value = @"#000000"; + } + + if (![value hasPrefix:@"#"] || [value length] < 7) { + return; + } + + [self _backgroundColorByHexString:value]; +} + +- (void) hideStatusBar +{ + if (_uiviewControllerBasedStatusBarAppearance) { + CDVViewController* vc = (CDVViewController*)self.viewController; + vc.sb_hideStatusBar = [NSNumber numberWithBool:YES]; + [self refreshStatusBarAppearance]; + + } else { + UIApplication* app = [UIApplication sharedApplication]; + [app setStatusBarHidden:YES]; + } +} + +- (void) hide:(CDVInvokedUrlCommand*)command +{ + _statusBarVisible = NO; + UIApplication* app = [UIApplication sharedApplication]; + + if (!app.isStatusBarHidden) + { + + [self hideStatusBar]; + + [_statusBarBackgroundView removeFromSuperview]; + + [self resizeWebView]; + + _statusBarBackgroundView.hidden = YES; + } +} + +- (void) showStatusBar +{ + if (_uiviewControllerBasedStatusBarAppearance) { + CDVViewController* vc = (CDVViewController*)self.viewController; + vc.sb_hideStatusBar = [NSNumber numberWithBool:NO]; + [self refreshStatusBarAppearance]; + + } else { + UIApplication* app = [UIApplication sharedApplication]; + [app setStatusBarHidden:NO]; + } +} + +- (void) show:(CDVInvokedUrlCommand*)command +{ + _statusBarVisible = YES; + UIApplication* app = [UIApplication sharedApplication]; + + if (app.isStatusBarHidden) + { + [self showStatusBar]; + [self resizeWebView]; + + if (!self.statusBarOverlaysWebView) { + + // there is a possibility that when the statusbar was hidden, it was in a different orientation + // from the current one. Therefore we need to expand the statusBarBackgroundView as well to the + // statusBar's current size + [self resizeStatusBarBackgroundView]; + [self.webView.superview addSubview:_statusBarBackgroundView]; + + } + + _statusBarBackgroundView.hidden = NO; + } +} + +-(void)resizeStatusBarBackgroundView { + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + CGRect sbBgFrame = _statusBarBackgroundView.frame; + sbBgFrame.size = statusBarFrame.size; + _statusBarBackgroundView.frame = sbBgFrame; +} + +-(void)resizeWebView +{ + BOOL isIOS11 = (IsAtLeastiOSVersion(@"11.0")); + + CGRect bounds = [self.viewController.view.window bounds]; + if (CGRectEqualToRect(bounds, CGRectZero)) { + bounds = [[UIScreen mainScreen] bounds]; + } + + self.viewController.view.frame = bounds; + + self.webView.frame = bounds; + + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + CGRect frame = self.webView.frame; + CGFloat height = statusBarFrame.size.height; + + if (!self.statusBarOverlaysWebView) { + frame.origin.y = height; + } else { + frame.origin.y = height >= 20 ? height - 20 : 0; + if (isIOS11) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if (@available(iOS 11.0, *)) { + float safeAreaTop = self.webView.safeAreaInsets.top; + if (height >= safeAreaTop && safeAreaTop >0) { + // Sometimes when in-call/recording/hotspot larger status bar is present, the safeAreaTop is 40 but we want frame.origin.y to be 20 + frame.origin.y = safeAreaTop == 40 ? 20 : height - safeAreaTop; + } else { + frame.origin.y = 0; + } + } +#endif + } + } + frame.size.height -= frame.origin.y; + self.webView.frame = frame; + +} + +- (void) dealloc +{ + [[UIApplication sharedApplication] removeObserver:self forKeyPath:@"statusBarHidden"]; + [[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +} + + +#pragma mark - UIScrollViewDelegate + +- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView +{ + [self fireTappedEvent]; + return NO; +} + +@end diff --git a/plugins/cordova-plugin-statusbar/src/windows/StatusBarProxy.js b/plugins/cordova-plugin-statusbar/src/windows/StatusBarProxy.js new file mode 100644 index 0000000..3929ff0 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/src/windows/StatusBarProxy.js @@ -0,0 +1,114 @@ +/* + * 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. + * + */ + +/* global Windows */ + +var _supported = null; // set to null so we can check first time + +function isSupported() { + // if not checked before, run check + if (_supported === null) { + var viewMan = Windows.UI.ViewManagement; + _supported = (viewMan.StatusBar && viewMan.StatusBar.getForCurrentView); + } + return _supported; +} + +function getViewStatusBar() { + if (!isSupported()) { + throw new Error("Status bar is not supported"); + } + return Windows.UI.ViewManagement.StatusBar.getForCurrentView(); +} + +function hexToRgb(hex) { + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function (m, r, g, b) { + return r + r + g + g + b + b; + }); + + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; +} + +module.exports = { + _ready: function(win, fail) { + if(isSupported()) { + var statusBar = getViewStatusBar(); + win(statusBar.occludedRect.height !== 0); + } + }, + overlaysWebView: function () { + // not supported + }, + + styleDefault: function () { + // dark text ( to be used on a light background ) + if (isSupported()) { + getViewStatusBar().foregroundColor = { a: 0, r: 0, g: 0, b: 0 }; + } + }, + + styleLightContent: function () { + // light text ( to be used on a dark background ) + if (isSupported()) { + getViewStatusBar().foregroundColor = { a: 0, r: 255, g: 255, b: 255 }; + } + }, + + styleBlackTranslucent: function () { + // #88000000 ? Apple says to use lightContent instead + return module.exports.styleLightContent(); + }, + + styleBlackOpaque: function () { + // #FF000000 ? Apple says to use lightContent instead + return module.exports.styleLightContent(); + }, + + backgroundColorByHexString: function (win, fail, args) { + var rgb = hexToRgb(args[0]); + if(isSupported()) { + var statusBar = getViewStatusBar(); + statusBar.backgroundColor = { a: 0, r: rgb.r, g: rgb.g, b: rgb.b }; + statusBar.backgroundOpacity = 1; + } + }, + + show: function (win, fail) { + // added support check so no error thrown, when calling this method + if (isSupported()) { + getViewStatusBar().showAsync().done(win, fail); + } + }, + + hide: function (win, fail) { + // added support check so no error thrown, when calling this method + if (isSupported()) { + getViewStatusBar().hideAsync().done(win, fail); + } + } +}; +require("cordova/exec/proxy").add("StatusBar", module.exports); \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/src/wp/StatusBar.cs b/plugins/cordova-plugin-statusbar/src/wp/StatusBar.cs new file mode 100644 index 0000000..ec83ca8 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/src/wp/StatusBar.cs @@ -0,0 +1,141 @@ +/* + 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. +*/ + + +using Microsoft.Phone.Shell; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Threading; +using System.Windows; +using System.Windows.Media; +using System.Windows.Threading; + + +/* + * http://www.idev101.com/code/User_Interface/StatusBar.html + * https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/Bars.html + * https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html#//apple_ref/c/econst/UIStatusBarStyleDefault + * */ + + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class StatusBar : BaseCommand + { + + // returns an argb value, if the hex is only rgb, it will be full opacity + protected Color ColorFromHex(string hexString) + { + string cleanHex = hexString.Replace("#", "").Replace("0x", ""); + // turn #FFF into #FFFFFF + if (cleanHex.Length == 3) + { + cleanHex = "" + cleanHex[0] + cleanHex[0] + cleanHex[1] + cleanHex[1] + cleanHex[2] + cleanHex[2]; + } + // add an alpha 100% if it is missing + if (cleanHex.Length == 6) + { + cleanHex = "FF" + cleanHex; + } + int argb = Int32.Parse(cleanHex, NumberStyles.HexNumber); + Color clr = Color.FromArgb((byte)((argb & 0xff000000) >> 0x18), + (byte)((argb & 0xff0000) >> 0x10), + (byte)((argb & 0xff00) >> 8), + (byte)(argb & 0xff)); + return clr; + } + + public void _ready(string options) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + bool isVis = SystemTray.IsVisible; + // TODO: pass this to JS + //Debug.WriteLine("Result::" + res); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isVis)); + }); + } + + public void overlaysWebView(string options) + { //exec(null, null, "StatusBar", "overlaysWebView", [doOverlay]); + // string arg = JSON.JsonHelper.Deserialize(options)[0]; + } + + public void styleDefault(string options) + { //exec(null, null, "StatusBar", "styleDefault", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.ForegroundColor = Colors.Black; + }); + } + + public void styleLightContent(string options) + { //exec(null, null, "StatusBar", "styleLightContent", []); + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.ForegroundColor = Colors.White; + }); + } + + public void styleBlackTranslucent(string options) + { //exec(null, null, "StatusBar", "styleBlackTranslucent", []); + styleLightContent(options); + } + + public void styleBlackOpaque(string options) + { //exec(null, null, "StatusBar", "styleBlackOpaque", []); + styleLightContent(options); + } + + public void backgroundColorByName(string options) + { //exec(null, null, "StatusBar", "backgroundColorByName", [colorname]); + // this should NOT be called, js should now be using/converting color names to hex + } + + public void backgroundColorByHexString(string options) + { //exec(null, null, "StatusBar", "backgroundColorByHexString", [hexString]); + string argb = JSON.JsonHelper.Deserialize(options)[0]; + + Color clr = ColorFromHex(argb); + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.Opacity = clr.A / 255.0d; + SystemTray.BackgroundColor = clr; + + }); + } + + public void hide(string options) + { //exec(null, null, "StatusBar", "hide", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.IsVisible = false; + }); + + } + + public void show(string options) + { //exec(null, null, "StatusBar", "show", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.IsVisible = true; + }); + } + } +} \ No newline at end of file diff --git a/plugins/cordova-plugin-statusbar/tests/package.json b/plugins/cordova-plugin-statusbar/tests/package.json new file mode 100644 index 0000000..5e2ba47 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/tests/package.json @@ -0,0 +1,14 @@ +{ + "name": "cordova-plugin-statusbar-tests", + "version": "2.2.3-dev", + "description": "", + "cordova": { + "id": "cordova-plugin-statusbar-tests", + "platforms": [] + }, + "keywords": [ + "ecosystem:cordova" + ], + "author": "", + "license": "Apache 2.0" +} diff --git a/plugins/cordova-plugin-statusbar/tests/plugin.xml b/plugins/cordova-plugin-statusbar/tests/plugin.xml new file mode 100644 index 0000000..af142ae --- /dev/null +++ b/plugins/cordova-plugin-statusbar/tests/plugin.xml @@ -0,0 +1,31 @@ + + + + + Cordova StatusBar Plugin Tests + Apache 2.0 + + + + diff --git a/plugins/cordova-plugin-statusbar/tests/tests.js b/plugins/cordova-plugin-statusbar/tests/tests.js new file mode 100644 index 0000000..5a8fe39 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/tests/tests.js @@ -0,0 +1,151 @@ +/* + * + * 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. + * +*/ + +/* jshint jasmine: true */ +/* global StatusBar */ + +exports.defineAutoTests = function () { + describe("StatusBar", function () { + it("statusbar.spec.1 should exist", function() { + expect(window.StatusBar).toBeDefined(); + }); + + it("statusbar.spec.2 should have show|hide methods", function() { + expect(window.StatusBar.show).toBeDefined(); + expect(typeof window.StatusBar.show).toBe("function"); + + expect(window.StatusBar.hide).toBeDefined(); + expect(typeof window.StatusBar.hide).toBe("function"); + }); + + it("statusbar.spec.3 should have set backgroundColor methods", function() { + expect(window.StatusBar.backgroundColorByName).toBeDefined(); + expect(typeof window.StatusBar.backgroundColorByName).toBe("function"); + + expect(window.StatusBar.backgroundColorByHexString).toBeDefined(); + expect(typeof window.StatusBar.backgroundColorByHexString).toBe("function"); + }); + + it("statusbar.spec.4 should have set style methods", function() { + expect(window.StatusBar.styleBlackTranslucent).toBeDefined(); + expect(typeof window.StatusBar.styleBlackTranslucent).toBe("function"); + + expect(window.StatusBar.styleDefault).toBeDefined(); + expect(typeof window.StatusBar.styleDefault).toBe("function"); + + expect(window.StatusBar.styleLightContent).toBeDefined(); + expect(typeof window.StatusBar.styleLightContent).toBe("function"); + + expect(window.StatusBar.styleBlackOpaque).toBeDefined(); + expect(typeof window.StatusBar.styleBlackOpaque).toBe("function"); + + expect(window.StatusBar.overlaysWebView).toBeDefined(); + expect(typeof window.StatusBar.overlaysWebView).toBe("function"); + }); + }); +}; + +exports.defineManualTests = function (contentEl, createActionButton) { + function log(msg) { + var el = document.getElementById("info"); + var logLine = document.createElement('div'); + logLine.innerHTML = msg; + el.appendChild(logLine); + } + + function doShow() { + StatusBar.show(); + log('StatusBar.isVisible=' + StatusBar.isVisible); + } + + function doHide() { + StatusBar.hide(); + log('StatusBar.isVisible=' + StatusBar.isVisible); + } + + function doColor1() { + log('set color=red'); + StatusBar.backgroundColorByName('red'); + } + + function doColor2() { + log('set style=translucent black'); + StatusBar.styleBlackTranslucent(); + } + + function doColor3() { + log('set style=default'); + StatusBar.styleDefault(); + } + + var showOverlay = true; + function doOverlay() { + showOverlay = !showOverlay; + StatusBar.overlaysWebView(showOverlay); + log('Set overlay=' + showOverlay); + } + + /******************************************************************************/ + + contentEl.innerHTML = '
' + + 'Also: tapping bar on iOS should emit a log.' + + '
' + + 'Expected result: Status bar will be visible' + + '

' + + 'Expected result: Status bar will be hidden' + + '

' + + 'Expected result: Status bar text will be a light (white) color' + + '

' + + 'Expected result: Status bar text will be a dark (black) color' + + '

' + + 'Expected result:
Overlay true = status bar will lay on top of web view content
Overlay false = status bar will be separate from web view and will not cover content' + + '

' + + 'Expected result: If overlay false, background color for status bar will be red'; + + log('StatusBar.isVisible=' + StatusBar.isVisible); + window.addEventListener('statusTap', function () { + log('tap!'); + }, false); + + createActionButton("Show", function () { + doShow(); + }, 'action-show'); + + createActionButton("Hide", function () { + doHide(); + }, 'action-hide'); + + createActionButton("Style=red (background)", function () { + doColor1(); + }, 'action-color1'); + + createActionButton("Style=translucent black", function () { + doColor2(); + }, 'action-color2'); + + createActionButton("Style=default", function () { + doColor3(); + }, 'action-color3'); + + createActionButton("Toggle Overlays", function () { + doOverlay(); + }, 'action-overlays'); +}; diff --git a/plugins/cordova-plugin-statusbar/types/index.d.ts b/plugins/cordova-plugin-statusbar/types/index.d.ts new file mode 100644 index 0000000..87df2e7 --- /dev/null +++ b/plugins/cordova-plugin-statusbar/types/index.d.ts @@ -0,0 +1,77 @@ +// Type definitions for Apache Cordova StatusBar plugin +// Project: https://github.com/apache/cordova-plugin-statusbar +// Definitions by: Xinkai Chen +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/** +* Global object StatusBar. +*/ +interface Window { + StatusBar: StatusBar; +} + + +/** +* The StatusBar object provides some functions to customize the iOS and Android StatusBar. +*/ +interface StatusBar { + /** + * On iOS 7, make the statusbar overlay or not overlay the WebView. + * @param isOverlay On iOS 7, set to false to make the statusbar appear like iOS 6. + * Set the style and background color to suit using the other functions. + */ + overlaysWebView: (isOverlay: boolean) => void; + + /** + * Use the default statusbar (dark text, for light backgrounds). + */ + styleDefault: () => void; + + /** + * Use the lightContent statusbar (light text, for dark backgrounds). + */ + styleLightContent: () => void; + + /** + * Use the blackTranslucent statusbar (light text, for dark backgrounds). + */ + styleBlackTranslucent: () => void; + + /** + * Use the blackOpaque statusbar (light text, for dark backgrounds). + */ + styleBlackOpaque: () => void; + + /** + * On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false, + * you can set the background color of the statusbar by color name. + * @param color Supported color names are: + * black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + */ + backgroundColorByName: (color: string) => void; + + /** + * Sets the background color of the statusbar by a hex string. + * @param color CSS shorthand properties are also supported. + * On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false, you can set the background color of the statusbar by a hex string (#RRGGBB). + * On WP7 and WP8 you can also specify values as #AARRGGBB, where AA is an alpha value + */ + backgroundColorByHexString: (color: string) => void; + + /** + * Hide the statusbar. + */ + hide: () => void; + + /** + * Show the statusbar. + */ + show: () => void; + + /** + * Read this property to see if the statusbar is visible or not. + */ + isVisible: boolean; +} + +declare var StatusBar: StatusBar; \ No newline at end of file diff --git a/plugins/cordova-plugin-whitelist/CONTRIBUTING.md b/plugins/cordova-plugin-whitelist/CONTRIBUTING.md new file mode 100644 index 0000000..7de4c64 --- /dev/null +++ b/plugins/cordova-plugin-whitelist/CONTRIBUTING.md @@ -0,0 +1,37 @@ + + +# Contributing to Apache Cordova + +Anyone can contribute to Cordova. And we need your contributions. + +There are multiple ways to contribute: report bugs, improve the docs, and +contribute code. + +For instructions on this, start with the +[contribution overview](http://cordova.apache.org/contribute/). + +The details are explained there, but the important items are: + - Sign and submit an Apache ICLA (Contributor License Agreement). + - Have a Jira issue open that corresponds to your contribution. + - Run the tests so your patch doesn't break existing functionality. + +We look forward to your contributions! diff --git a/plugins/cordova-plugin-whitelist/LICENSE b/plugins/cordova-plugin-whitelist/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/cordova-plugin-whitelist/LICENSE @@ -0,0 +1,202 @@ + + 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. \ No newline at end of file diff --git a/plugins/cordova-plugin-whitelist/NOTICE b/plugins/cordova-plugin-whitelist/NOTICE new file mode 100644 index 0000000..8ec56a5 --- /dev/null +++ b/plugins/cordova-plugin-whitelist/NOTICE @@ -0,0 +1,5 @@ +Apache Cordova +Copyright 2012 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/cordova-plugin-whitelist/README.md b/plugins/cordova-plugin-whitelist/README.md new file mode 100644 index 0000000..e19d230 --- /dev/null +++ b/plugins/cordova-plugin-whitelist/README.md @@ -0,0 +1,163 @@ +--- +title: Whitelist +description: Whitelist external content accessible by your app. +--- + + +# cordova-plugin-whitelist + +This plugin implements a whitelist policy for navigating the application webview on Cordova 4.0 + +:warning: Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Whitelist%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) + +## Installation + +You can install whitelist plugin with Cordova CLI, from npm: + +``` +$ cordova plugin add cordova-plugin-whitelist +$ cordova prepare +``` + +## Supported Cordova Platforms + +* Android 4.0.0 or above + +## Navigation Whitelist +Controls which URLs the WebView itself can be navigated to. Applies to +top-level navigations only. + +Quirks: on Android it also applies to iframes for non-http(s) schemes. + +By default, navigations only to `file://` URLs, are allowed. To allow others URLs, you must add `` tags to your `config.xml`: + + + + + + + + + + + + + + + +## Intent Whitelist +Controls which URLs the app is allowed to ask the system to open. +By default, no external URLs are allowed. + +On Android, this equates to sending an intent of type BROWSEABLE. + +This whitelist does not apply to plugins, only hyperlinks and calls to `window.open()`. + +In `config.xml`, add `` tags, like this: + + + + + + + + + + + + + + + + + + + + + + + +## Network Request Whitelist +Controls which network requests (images, XHRs, etc) are allowed to be made (via cordova native hooks). + +Note: We suggest you use a Content Security Policy (see below), which is more secure. This whitelist is mostly historical for webviews which do not support CSP. + +In `config.xml`, add `` tags, like this: + + + + + + + + + + + + + + + + + +Without any `` tags, only requests to `file://` URLs are allowed. However, the default Cordova application includes `` by default. + + +Note: Whitelist cannot block network redirects from a whitelisted remote website (i.e. http or https) to a non-whitelisted website. Use CSP rules to mitigate redirects to non-whitelisted websites for webviews that support CSP. + +Quirk: Android also allows requests to https://ssl.gstatic.com/accessibility/javascript/android/ by default, since this is required for TalkBack to function properly. + +### Content Security Policy +Controls which network requests (images, XHRs, etc) are allowed to be made (via webview directly). + +On Android and iOS, the network request whitelist (see above) is not able to filter all types of requests (e.g. `