diff options
60 files changed, 1351 insertions, 770 deletions
diff --git a/build_android.sh b/build_android.sh index d084d8c4..0eb723c5 100755 --- a/build_android.sh +++ b/build_android.sh @@ -13,8 +13,8 @@ if [ ! -f "$NINJAKEYSTORE" ]; then exit fi +rm -f release_files 2>/dev/null mkdir release_files -rm -f release_files/* # no arguments - build both # 1 == build crosswalk only @@ -30,45 +30,8 @@ if [ "$1" = "2" ]; then echo "only building native view (5+)" fi -############ Crosswalk build #################################### -if [ "$BUILD_MODE" = "xwalk" ] || [ "$BUILD_MODE" = "all" ]; then - - echo "${ver_pre5}: Building Release mode for Xwalk android..." - echo "--------------------------------------------" - echo "Removing android and re-adding..." - cordova platform remove android - cordova platform add android@6.3.0 - cordova plugin remove cordova-plugin-crosswalk-webview --nosave - echo "Adding crosswalk..." - #cordova plugin add cordova-plugin-crosswalk-webview - cordova plugin add cordova-plugin-crosswalk-webview@2.2.0 --variable XWALK_MODE="lite" --variable "XWALK_VERSION"="17.46.459.1" --nosave - #cordova plugin add cordova-plugin-crosswalk-webview --variable XWALK_VERSION="22+" - #ionic plugin add cordova-plugin-crosswalk-webview - # crosswalk handles SSL certificate handling in a different way - # need to switch plugins - echo "Adding crosswalk cert plugin..." - cordova plugin remove cordova-plugin-certificates --nosave - cordova plugin add cordova-plugin-crosswalk-certificate-pp-fork --nosave - cp "$NINJAKEYSTORE" platforms/android/ - echo "Building XWALK ..." - #cordova build android --release -- --targetSdkVersion=23 - cordova build android --release -- --gradleArg=-PcdvVersionCode=${ver_pre5} - - # copy builds to my release directory - cp platforms/android/build/outputs/apk/android-x86-release-unsigned.apk release_files/ - cp platforms/android/build/outputs/apk/android-armv7-release-unsigned.apk release_files/ - echo "Copied files to release_files" - - # sign them - cd release_files/ - jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../platforms/android/zmNinja.keystore android-armv7-release-unsigned.apk zmNinja - jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../platforms/android/zmNinja.keystore android-x86-release-unsigned.apk zmNinja - ~/Library/Android/sdk/build-tools/25.0.2/zipalign -v 4 android-x86-release-unsigned.apk zmNinja-x86-pre5.apk - ~/Library/Android/sdk/build-tools/25.0.2/zipalign -v 4 android-armv7-release-unsigned.apk zmNinja-arm-pre5.apk - rm -f android-x86-release-unsigned.apk android-armv7-release-unsigned.apk - cd .. -fi - +echo "----------> Only building native. Not building crosswalk anymore due to compatibility issues <----------------------" +BUILD_MODE="native" ############ Native web view build ############################### if [ "$BUILD_MODE" = "native" ] || [ "$BUILD_MODE" = "all" ]; then @@ -76,24 +39,25 @@ if [ "$BUILD_MODE" = "native" ] || [ "$BUILD_MODE" = "all" ]; then echo "${ver}: Building Release mode for android 5+..." echo "--------------------------------------------" - echo "Removing android and re-adding..." - cordova platform remove android - cordova platform add android@6.3.0 +# No longger needed as we are not supporting Xwalk +# echo "Removing android and re-adding..." +# cordova platform remove android +# cordova platform add android@6.4.0 #clean up past build stuff - echo "Adding default browser..." - cordova plugin remove cordova-plugin-crosswalk-webview +# echo "Adding default browser..." +# cordova plugin remove cordova-plugin-crosswalk-webview # use the right plugin for SSL certificate mgmt - cordova plugin remove cordova-plugin-crosswalk-certificate-pp-fork - cordova plugin add cordova-plugin-certificates +# cordova plugin remove cordova-plugin-crosswalk-certificate-pp-fork +# cordova plugin add cordova-plugin-certificates cp "$NINJAKEYSTORE" platforms/android/ # Make sure native builds are only deployed in devices >= Android 5 cordova build android --release -- --minSdkVersion=21 --versionCode=${ver} # copy build to release folder and sign - cp platforms/android/build/outputs/apk/android-release-unsigned.apk release_files/ + cp platforms/android/build/outputs/apk/release/android-release-unsigned.apk release_files/ echo "Copied files to release_files" cd release_files/ diff --git a/build_ios.sh b/build_ios.sh index 0e87eca7..cbd66b34 100755 --- a/build_ios.sh +++ b/build_ios.sh @@ -1,4 +1,11 @@ echo "*** Using old build system due to XCode 10 issues ** " echo "see https://forum.ionicframework.com/t/how-to-build-ionic-cordova-with-xcode-10/142044" +#ionic cordova plugin add cordova-plugin-ionic-webview@latest + +#echo "--- readding certificate plugin to make sure... ---" +#ionic cordova plugin remove cordova-plugin-certificates +#ionic cordova plugin add cordova-plugin-certificates + +echo "-- building --" ionic cordova build ios --release --buildConfig="./build-auto.json" @@ -1,5 +1,5 @@ <?xml version='1.0' encoding='utf-8'?> -<widget android-packageName="com.pliablepixels.zmninja_pro" id="com.pliablepixels.zmninja_pro" ios-CFBundleIdentifier="com.pliablepixels.zmninja-pro" version="1.3.025" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> +<widget android-packageName="com.pliablepixels.zmninja_pro" id="com.pliablepixels.zmninja_pro" ios-CFBundleIdentifier="com.pliablepixels.zmninja-pro" version="1.3.026" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <name>zmNinja</name> <description> High performance ZoneMinder client @@ -8,10 +8,14 @@ Pliable Pixels </author> <content src="index.html" /> + <allow-navigation href="localhost:8080/*" /> + <allow-navigation href="gap://ready" /> <access launch-external="yes" origin="mailto:*" /> <access allows-arbitrary-loads-for-media="true" allows-arbitrary-loads-in-web-content="true" allows-local-networking="true" origin="*" /> <allow-intent href="mailto:*" /> - <allow-navigation href="*" /> + <allow-intent href="http://*/*" /> + <allow-intent href="https://*/*" /> + <preference name="KeyboardResizeMode" value="native" /> <preference name="xwalkMultipleApk" value="true" /> <preference name="AndroidPersistentFileLocation" value="Compatibility" /> <preference name="iosPersistentFileLocation" value="Library" /> @@ -25,7 +29,7 @@ <preference name="AutoHideSplashScreen" value="false" /> <preference name="ShowSplashScreenSpinner" value="false" /> <preference name="SplashScreen" value="screen" /> - <preference name="deployment-target" value="8.0" /> + <preference name="deployment-target" value="10.0" /> <preference name="SplashScreenDelay" value="300" /> <preference name="SplashMaintainAspectRatio" value="true" /> <preference name="FadeSplashScreen" value="false" /> @@ -36,7 +40,17 @@ <param name="ios-package" onload="true" value="CDVStatusBar" /> </feature> <platform name="ios"> + <feature name="CDVWKWebViewEngine"> + <param name="ios-package" value="CDVWKWebViewEngine" /> + </feature> + <preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" /> <resource-file src="GoogleService-Info.plist" /> + <config-file parent="NSAppTransportSecurity" target="*-Info.plist"> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true /> + </dict> + </config-file> <config-file parent="NSPhotoLibraryAddUsageDescription" platform="ios" target="*-Info.plist"> <string>Store photos of events and live feeds</string> </config-file> @@ -82,6 +96,9 @@ <icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" /> </platform> <platform name="android"> + <config-file parent="/*" target="AndroidManifest.xml"> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + </config-file> <resource-file src="build-extras.gradle" target="build-extras.gradle" /> <resource-file src="google-services.json" target="google-services.json" /> <preference name="android-manifest/@android:installLocation" value="auto" /> @@ -108,7 +125,6 @@ <icon src="resources/android/icon/drawable-xhdpi-icon.png" /> <preference name="SplashShowOnlyFirstTime" value="false" /> <plugin name="cordova-plugin-add-swift-support" spec="^1.7.2" /> - <plugin name="com.telerik.plugins.nativepagetransitions" spec="^0.6.5" /> <plugin name="cordova-plugin-android-permissions" spec="^1.0.0" /> <plugin name="cordova-plugin-app-version" spec="^0.1.9" /> <plugin name="cordova-plugin-customurlscheme" spec="^4.3.0"> @@ -118,9 +134,6 @@ <plugin name="cordova-plugin-globalization" spec="^1.0.7" /> <plugin name="cordova-plugin-inappbrowser" spec="^1.7.1" /> <plugin name="cordova-plugin-insomnia" spec="^4.3.0" /> - <plugin name="cordova-plugin-photo-library" spec="^1.2.2"> - <variable name="PHOTO_LIBRARY_USAGE_DESCRIPTION" value="images to gallery" /> - </plugin> <plugin name="cordova-plugin-pin-dialog" spec="^0.1.3" /> <plugin name="cordova-plugin-settings-hook" spec="^0.2.7" /> <plugin name="cordova-plugin-splashscreen" spec="^4.0.3" /> @@ -133,23 +146,27 @@ <plugin name="cordova-plugin-network-information" spec="^2.0.1" /> <plugin name="cordova-plugin-device" spec="^2.0.1" /> <plugin name="cordova-plugin-file" spec="^6.0.1" /> - <plugin name="cordova-plugin-canvas2image-pp-fork" spec="^0.6.0"> - <variable name="PHOTOLIBRARY_USAGE_DESCRIPTION" value=" " /> - </plugin> <plugin name="cordova-plugin-media-pp-fork" spec="^1.0.2-dev" /> <plugin name="cordova-plugin-email" spec="^1.2.7" /> <plugin name="cordova-plugin-statusbar" spec="^2.4.2" /> <plugin name="cordova-library-helper-pp-fork" spec="^1.0.1" /> <plugin name="cordova-plugin-multi-window" spec="0.0.3" /> <plugin name="cordova-plugin-ignore-lint-translation" spec="0.0.1" /> - <plugin name="cordova-plugin-ionic-keyboard" spec="^2.1.2" /> <plugin name="cordova-plugin-cloud-settings" spec="^1.0.4"> <variable name="ANDROID_BACKUP_SERVICE_KEY" value="AEdPqrEAAAAIqF-OaHdwIzZhx2L1WOfAGTagBxm5a1R4wBW_Uw" /> </plugin> - <plugin name="phonegap-plugin-push" spec="^2.1.3"> + <plugin name="cordova-plugin-ionic-keyboard" spec="^2.1.2" /> + <plugin name="cordova-plugin-photo-library" spec="git+https://github.com/pliablepixels/cordova-plugin-photo-library.git"> + <variable name="PHOTO_LIBRARY_USAGE_DESCRIPTION" value="Save image alarms todisk" /> + </plugin> + <plugin name="cordova-plugin-advanced-http" spec="^2.0.1" /> + <plugin name="phonegap-plugin-push" spec="~2.1.3"> <variable name="FCM_VERSION" value="11.6.2" /> </plugin> - <plugin name="cordova-plugin-certificates" spec="^0.6.4" /> - <engine name="android" spec="^6.3.0" /> + <plugin name="cordova-plugin-ionic-webview" spec="https://github.com/pliablepixels/cordova-plugin-ionic-webview.git"> + <variable name="ANDROID_SUPPORT_ANNOTATIONS_VERSION" value="27.+" /> + </plugin> + <plugin name="cordova-plugin-advanced-websocket" spec="^1.1.3" /> + <engine name="android" spec="^6.4.0" /> <engine name="ios" spec="~4.5.5" /> </widget> diff --git a/jsconfig.json b/jsconfig.json index bccb75f0..fb1eda40 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,11 +1,18 @@ { + + "include": [ - "www/**/*" + "www/js/**/*", + "www/css/**/*", + "www/templates/**/*" ], "exclude": [ - "node_modules/**/*", - "dist/**/*", - "platforms/**/*" + "node_modules", + "**/node_modules/*", + "dist", + "platforms", + ".vscode", + ] }
\ No newline at end of file diff --git a/package.json b/package.json index 0a31483d..95216464 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "zmninjapro", "description": "Home security mobile app for ZoneMinder", - "version":"1.3.022", + "version": "1.3.026", "displayName": "zmNinja", "author": "Pliable Pixels", "license": "custom see LICENSE.md", @@ -12,7 +12,6 @@ ], "plugins": { "cordova-plugin-add-swift-support": {}, - "com.telerik.plugins.nativepagetransitions": {}, "cordova-plugin-android-permissions": {}, "cordova-plugin-app-version": {}, "cordova-plugin-customurlscheme": { @@ -22,9 +21,6 @@ "cordova-plugin-globalization": {}, "cordova-plugin-inappbrowser": {}, "cordova-plugin-insomnia": {}, - "cordova-plugin-photo-library": { - "PHOTO_LIBRARY_USAGE_DESCRIPTION": "images to gallery" - }, "cordova-plugin-pin-dialog": {}, "cordova-plugin-settings-hook": {}, "cordova-plugin-splashscreen": {}, @@ -37,37 +33,40 @@ "cordova-plugin-network-information": {}, "cordova-plugin-device": {}, "cordova-plugin-file": {}, - "cordova-plugin-canvas2image-pp-fork": { - "PHOTOLIBRARY_USAGE_DESCRIPTION": " " - }, "cordova-plugin-media-pp-fork": {}, "cordova-plugin-email": {}, "cordova-plugin-statusbar": {}, "cordova-library-helper-pp-fork": {}, "cordova-plugin-multi-window": {}, "cordova-plugin-ignore-lint-translation": {}, - "cordova-plugin-ionic-keyboard": {}, "cordova-plugin-cloud-settings": { "ANDROID_BACKUP_SERVICE_KEY": "AEdPqrEAAAAIqF-OaHdwIzZhx2L1WOfAGTagBxm5a1R4wBW_Uw" }, + "cordova-plugin-ionic-keyboard": {}, + "cordova-plugin-photo-library": { + "PHOTO_LIBRARY_USAGE_DESCRIPTION": "Save image alarms todisk" + }, + "cordova-plugin-advanced-http": {}, "phonegap-plugin-push": { "FCM_VERSION": "11.6.2" }, - "cordova-plugin-certificates": {} + "cordova-plugin-ionic-webview": { + "ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+" + }, + "cordova-plugin-advanced-websocket": {} } }, "dependencies": { "clivas": "^0.2.0", - "com.telerik.plugins.nativepagetransitions": "^0.6.5", - "cordova-android": "^6.3.0", + "cordova-android": "^6.4.0", "cordova-ios": "~4.5.5", "cordova-library-helper-pp-fork": "^1.0.1", "cordova-plugin-add-swift-support": "^1.7.2", + "cordova-plugin-advanced-http": "^2.0.1", + "cordova-plugin-advanced-websocket": "^1.1.3", "cordova-plugin-android-fingerprint-auth": "^1.4.0", "cordova-plugin-android-permissions": "^1.0.0", "cordova-plugin-app-version": "^0.1.9", - "cordova-plugin-canvas2image-pp-fork": "^0.6.0", - "cordova-plugin-certificates": "^0.6.4", "cordova-plugin-cloud-settings": "^1.0.4", "cordova-plugin-customurlscheme": "^4.3.0", "cordova-plugin-device": "^2.0.1", @@ -79,10 +78,11 @@ "cordova-plugin-inappbrowser": "^1.7.1", "cordova-plugin-insomnia": "^4.3.0", "cordova-plugin-ionic-keyboard": "^2.1.2", + "cordova-plugin-ionic-webview": "git+https://github.com/pliablepixels/cordova-plugin-ionic-webview.git", "cordova-plugin-media-pp-fork": "^1.0.2-dev", "cordova-plugin-multi-window": "0.0.3", "cordova-plugin-network-information": "^2.0.1", - "cordova-plugin-photo-library": "^1.2.2", + "cordova-plugin-photo-library": "git+https://github.com/pliablepixels/cordova-plugin-photo-library.git", "cordova-plugin-pin-dialog": "^0.1.3", "cordova-plugin-settings-hook": "^0.2.7", "cordova-plugin-splashscreen": "^4.0.3", @@ -99,7 +99,7 @@ "minimist": "^1.2.0", "mkdirp": "^0.5.1", "phonegap-plugin-mobile-accessibility": "^1.0.5", - "phonegap-plugin-push": "^2.1.3" + "phonegap-plugin-push": "~2.1.3" }, "devDependencies": { "bower": "^1.8.4", diff --git a/prepare_desktop.sh b/prepare_desktop.sh new file mode 100755 index 00000000..86d28945 --- /dev/null +++ b/prepare_desktop.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +REL="v2.0.3" +#REL="v1.4.3" +WGET='wget' +WGET_ARGS='-q --show-progress' +UNZIP='unzip' +UNZIP_ARGS='-d' + +exe() { echo "\$ $@" ; "$@" ; } + +echo ---------------------------------------------------- +echo Pliable Pixels Desktop preparation process +echo Use this to download electron images +echo You really need to do this one time +echo ---------------------------------------------------- +echo +echo This will delete all files in desktop/ and also remove icon associations +read -p "Press a key to continue or Ctrl-C to break..." + + +iswget=`which ${WGET}` +if [ $? -ne 0 ]; then + echo "**ERROR** You need ${WGET} installed in your path to use this tool." + exit +fi +rm -rf desktop +mkdir -p desktop 2>/dev/null +cd desktop + + +declare -a release_names=("darwin-x64" "win32-x64" "win32-ia32" "linux-arm" "linux-x64" "linux-ia32") +declare -a release_renames=("zmNinja-mac.app" "zmNinja-win64bit" "zmNinja-win32bit" "zmNinja-linuxarm" "zmNinja-linux64bit" "zmNinja-linux32bit") + +for i in "${!release_names[@]}" +do + RELEASE="https://github.com/electron/electron/releases/download/${REL}/electron-${REL}-${release_names[$i]}.zip" + echo + echo "Working on ${RELEASE}..." + #echo "Rename to ${release_renames[$i]}" + echo "---------------------------------------------" + + echo "Downloading ${release_names[i]} ..." + exe ${WGET} ${RELEASE} ${WGET_ARGS} + + + echo "Decompressing image..." + if [ "${release_names[$i]}" != "darwin-x64" ]; then + exe mkdir electron-${REL}-${release_names[$i]} >/dev/null 2>&1 + exe rm -fr electron-${REL}-${release_names[$i]}/* >/dev/null 2>&1 + exe ${UNZIP} electron-${REL}-${release_names[$i]}.zip ${UNZIP_ARGS} electron-${REL}-${release_names[$i]} 2>/dev/null + exe mv electron-${REL}-${release_names[$i]} ${release_renames[$i]} >/dev/null 2>&1 + mv ${release_renames[$i]}/electron.exe ${release_renames[$i]}/zmNinja.exe >/dev/null 2>&1 + mv ${release_renames[$i]}/electron ${release_renames[$i]}/zmNinja >/dev/null 2>&1 + + else # OSX + exe ${UNZIP} electron-${REL}-${release_names[$i]}.zip 2>/dev/null + exe mv Electron.app ${release_renames[$i]} + + fi + + rm LICENSE* >/dev/null 2>&1 + rm version >/dev/null 2>&1 +done + +rm *.zip >/dev/null 2>&1 + +echo +echo ========================================================= +echo All done. Use ./make_desktop now +echo You need to associate icons for OSX and windows +echo for OSX +echo ========================================================= +echo + + diff --git a/resources/ios/icon/icon-1024.png b/resources/ios/icon/icon-1024.png Binary files differindex a8863a65..8ce1f586 100644 --- a/resources/ios/icon/icon-1024.png +++ b/resources/ios/icon/icon-1024.png diff --git a/resources/ios/icon/icon-40.png b/resources/ios/icon/icon-40.png Binary files differindex e7f34dfe..992e4347 100644 --- a/resources/ios/icon/icon-40.png +++ b/resources/ios/icon/icon-40.png diff --git a/resources/ios/icon/icon-40@2x.png b/resources/ios/icon/icon-40@2x.png Binary files differindex 86427b97..88e9093f 100644 --- a/resources/ios/icon/icon-40@2x.png +++ b/resources/ios/icon/icon-40@2x.png diff --git a/resources/ios/icon/icon-40@3x.png b/resources/ios/icon/icon-40@3x.png Binary files differindex f374d227..0eb2d14f 100644 --- a/resources/ios/icon/icon-40@3x.png +++ b/resources/ios/icon/icon-40@3x.png diff --git a/resources/ios/icon/icon-50.png b/resources/ios/icon/icon-50.png Binary files differindex 28625a52..cb772e83 100644 --- a/resources/ios/icon/icon-50.png +++ b/resources/ios/icon/icon-50.png diff --git a/resources/ios/icon/icon-50@2x.png b/resources/ios/icon/icon-50@2x.png Binary files differindex 89dbcbe9..03ee7ed4 100644 --- a/resources/ios/icon/icon-50@2x.png +++ b/resources/ios/icon/icon-50@2x.png diff --git a/resources/ios/icon/icon-60.png b/resources/ios/icon/icon-60.png Binary files differindex 1b43dde7..74f726ca 100644 --- a/resources/ios/icon/icon-60.png +++ b/resources/ios/icon/icon-60.png diff --git a/resources/ios/icon/icon-60@2x.png b/resources/ios/icon/icon-60@2x.png Binary files differindex f374d227..0eb2d14f 100644 --- a/resources/ios/icon/icon-60@2x.png +++ b/resources/ios/icon/icon-60@2x.png diff --git a/resources/ios/icon/icon-60@3x.png b/resources/ios/icon/icon-60@3x.png Binary files differindex 0ea98f88..7574996d 100644 --- a/resources/ios/icon/icon-60@3x.png +++ b/resources/ios/icon/icon-60@3x.png diff --git a/resources/ios/icon/icon-72.png b/resources/ios/icon/icon-72.png Binary files differindex 6dbbc4cf..a2d820df 100644 --- a/resources/ios/icon/icon-72.png +++ b/resources/ios/icon/icon-72.png diff --git a/resources/ios/icon/icon-72@2x.png b/resources/ios/icon/icon-72@2x.png Binary files differindex aebe82bc..3338ca7f 100644 --- a/resources/ios/icon/icon-72@2x.png +++ b/resources/ios/icon/icon-72@2x.png diff --git a/resources/ios/icon/icon-76.png b/resources/ios/icon/icon-76.png Binary files differindex 9b7b13ca..133f22f2 100644 --- a/resources/ios/icon/icon-76.png +++ b/resources/ios/icon/icon-76.png diff --git a/resources/ios/icon/icon-76@2x.png b/resources/ios/icon/icon-76@2x.png Binary files differindex e31278cc..cc8edd0a 100644 --- a/resources/ios/icon/icon-76@2x.png +++ b/resources/ios/icon/icon-76@2x.png diff --git a/resources/ios/icon/icon-83.5@2x.png b/resources/ios/icon/icon-83.5@2x.png Binary files differindex 923975fd..fb75f8f9 100644 --- a/resources/ios/icon/icon-83.5@2x.png +++ b/resources/ios/icon/icon-83.5@2x.png diff --git a/resources/ios/icon/icon-small.png b/resources/ios/icon/icon-small.png Binary files differindex eb650703..79cd46a1 100644 --- a/resources/ios/icon/icon-small.png +++ b/resources/ios/icon/icon-small.png diff --git a/resources/ios/icon/icon-small@2x.png b/resources/ios/icon/icon-small@2x.png Binary files differindex 6800f2b6..8251efd0 100644 --- a/resources/ios/icon/icon-small@2x.png +++ b/resources/ios/icon/icon-small@2x.png diff --git a/resources/ios/icon/icon-small@3x.png b/resources/ios/icon/icon-small@3x.png Binary files differindex a5edb37f..46c34cc5 100644 --- a/resources/ios/icon/icon-small@3x.png +++ b/resources/ios/icon/icon-small@3x.png diff --git a/resources/ios/icon/icon.png b/resources/ios/icon/icon.png Binary files differindex 1430cc4c..742116e2 100644 --- a/resources/ios/icon/icon.png +++ b/resources/ios/icon/icon.png diff --git a/resources/ios/icon/icon@2x.png b/resources/ios/icon/icon@2x.png Binary files differindex e326f4ca..6eb4742d 100644 --- a/resources/ios/icon/icon@2x.png +++ b/resources/ios/icon/icon@2x.png diff --git a/resources/ios/splash/Default-568h@2x~iphone.png b/resources/ios/splash/Default-568h@2x~iphone.png Binary files differindex 6294c251..b987bfaa 100644 --- a/resources/ios/splash/Default-568h@2x~iphone.png +++ b/resources/ios/splash/Default-568h@2x~iphone.png diff --git a/resources/ios/splash/Default-667h.png b/resources/ios/splash/Default-667h.png Binary files differindex dcf6739c..ba037c77 100644 --- a/resources/ios/splash/Default-667h.png +++ b/resources/ios/splash/Default-667h.png diff --git a/resources/ios/splash/Default-736h.png b/resources/ios/splash/Default-736h.png Binary files differindex b74244b2..456327ee 100644 --- a/resources/ios/splash/Default-736h.png +++ b/resources/ios/splash/Default-736h.png diff --git a/resources/ios/splash/Default-Landscape-736h.png b/resources/ios/splash/Default-Landscape-736h.png Binary files differindex e24c2e99..ba79a715 100644 --- a/resources/ios/splash/Default-Landscape-736h.png +++ b/resources/ios/splash/Default-Landscape-736h.png diff --git a/resources/ios/splash/Default-Landscape@2x~ipad.png b/resources/ios/splash/Default-Landscape@2x~ipad.png Binary files differindex 263d27d8..9000638f 100644 --- a/resources/ios/splash/Default-Landscape@2x~ipad.png +++ b/resources/ios/splash/Default-Landscape@2x~ipad.png diff --git a/resources/ios/splash/Default-Landscape@~ipadpro.png b/resources/ios/splash/Default-Landscape@~ipadpro.png Binary files differindex 9ba935ac..5ed46f67 100644 --- a/resources/ios/splash/Default-Landscape@~ipadpro.png +++ b/resources/ios/splash/Default-Landscape@~ipadpro.png diff --git a/resources/ios/splash/Default-Landscape~ipad.png b/resources/ios/splash/Default-Landscape~ipad.png Binary files differindex f31bd7b7..bc6d1502 100644 --- a/resources/ios/splash/Default-Landscape~ipad.png +++ b/resources/ios/splash/Default-Landscape~ipad.png diff --git a/resources/ios/splash/Default-Portrait@2x~ipad.png b/resources/ios/splash/Default-Portrait@2x~ipad.png Binary files differindex 9932c9c2..39f7f9de 100644 --- a/resources/ios/splash/Default-Portrait@2x~ipad.png +++ b/resources/ios/splash/Default-Portrait@2x~ipad.png diff --git a/resources/ios/splash/Default-Portrait@~ipadpro.png b/resources/ios/splash/Default-Portrait@~ipadpro.png Binary files differindex 4b850386..e0685076 100644 --- a/resources/ios/splash/Default-Portrait@~ipadpro.png +++ b/resources/ios/splash/Default-Portrait@~ipadpro.png diff --git a/resources/ios/splash/Default-Portrait~ipad.png b/resources/ios/splash/Default-Portrait~ipad.png Binary files differindex cf6d37ef..df205911 100644 --- a/resources/ios/splash/Default-Portrait~ipad.png +++ b/resources/ios/splash/Default-Portrait~ipad.png diff --git a/resources/ios/splash/Default@2x~iphone.png b/resources/ios/splash/Default@2x~iphone.png Binary files differindex 63aa416c..5fde545a 100644 --- a/resources/ios/splash/Default@2x~iphone.png +++ b/resources/ios/splash/Default@2x~iphone.png diff --git a/resources/ios/splash/Default@2x~universal~anyany.png b/resources/ios/splash/Default@2x~universal~anyany.png Binary files differindex cee7a645..77108330 100644 --- a/resources/ios/splash/Default@2x~universal~anyany.png +++ b/resources/ios/splash/Default@2x~universal~anyany.png diff --git a/resources/ios/splash/Default~iphone.png b/resources/ios/splash/Default~iphone.png Binary files differindex 41c5fed8..0a72e96b 100644 --- a/resources/ios/splash/Default~iphone.png +++ b/resources/ios/splash/Default~iphone.png diff --git a/www/css/style.css b/www/css/style.css index 175a8748..2d8650d7 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -1843,4 +1843,15 @@ body { .platform-ios.platform-cordova:not(.fullscreen) .has-subheader { top: 88px !important; -}
\ No newline at end of file +} + + + + /* @media screen and (-webkit-min-device-pixel-ratio:0) { + select, + textarea, + input { + font-size: 16px; + } + }*/ +
\ No newline at end of file diff --git a/www/index.html b/www/index.html index 4d8d2332..7aa77593 100644 --- a/www/index.html +++ b/www/index.html @@ -5,7 +5,7 @@ <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0, viewport-fit=cover"> <meta http-equiv="Content-Security-Policy" content="img-src * blob: android-webview-video-poster: cdvphotolibrary: 'self' data: ws: wss://*; default-src * blob: 'self' gap: wss: ws: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src * http: https: ws: wss:;"> @@ -13,6 +13,7 @@ <!--<meta http-equiv="Content-Security-Policy" content="img-src * blob: android-webview-video-poster: cdvphotolibrary: 'self' data:; default-src * blob: 'self' gap: wss: ws: ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval';connect-src * http: https: ws: wss:;">--> + <title></title> <meta name="format-detection" content="telephone=no"> <link rel="stylesheet" href="css/animate.min.css"> diff --git a/www/js/DataModel.js b/www/js/DataModel.js index 575f369c..3b465400 100644 --- a/www/js/DataModel.js +++ b/www/js/DataModel.js @@ -9,10 +9,10 @@ angular.module('zmApp.controllers') - .service('NVRDataModel', ['$ionicPlatform', '$http', '$q', '$ionicLoading', '$ionicBackdrop', '$fileLogger', 'zm', '$rootScope', '$ionicContentBanner', '$timeout', '$cordovaPinDialog', '$ionicPopup', '$localstorage', '$state', '$ionicNativeTransitions', '$translate', '$cordovaSQLite', + .service('NVRDataModel', ['$ionicPlatform', '$http', '$q', '$ionicLoading', '$ionicBackdrop', '$fileLogger', 'zm', '$rootScope', '$ionicContentBanner', '$timeout', '$cordovaPinDialog', '$ionicPopup', '$localstorage', '$state', '$translate', '$cordovaSQLite', function ($ionicPlatform, $http, $q, $ionicLoading, $ionicBackdrop, $fileLogger, zm, $rootScope, $ionicContentBanner, $timeout, $cordovaPinDialog, - $ionicPopup, $localstorage, $state, $ionicNativeTransitions, $translate) { + $ionicPopup, $localstorage, $state, $translate) { var currentServerMultiPortSupported = false; @@ -20,7 +20,7 @@ angular.module('zmApp.controllers') DO NOT TOUCH zmAppVersion It is changed by sync_version.sh */ - var zmAppVersion = "1.3.022"; + var zmAppVersion = "1.3.026"; var isBackground = false; var justResumed = false; var timeSinceResumed = -1; @@ -28,7 +28,7 @@ angular.module('zmApp.controllers') var monitors = []; var multiservers = []; - var oldevents = []; + var migrationComplete = false; var tz = ""; @@ -202,18 +202,27 @@ angular.module('zmApp.controllers') * * @returns */ - function setSSLCerts() { - if (!window.cordova) return; + function setCordovaHttpOptions() { + + if (loginData.isUseBasicAuth) { + debug ("Cordova HTTP: configuring basic auth"); + cordova.plugin.http.useBasicAuth(loginData.basicAuthUser, loginData.basicAuthPassword); + } + if (!loginData.enableStrictSSL) { //alert("Enabling insecure SSL"); log(">>>> Disabling strict SSL checking (turn off in Dev Options if you can't connect)"); - cordova.plugins.certificates.trustUnsecureCerts(true); + cordova.plugin.http.setSSLCertMode('nocheck', function() { + debug('--> SSL is permissive, will allow any certs. Use at your own risk.'); + }, function() { + console.log('-->Error setting SSL permissive'); + }); } else { log(">>>> Enabling strict SSL checking (turn off in Dev Options if you can't connect)"); - cordova.plugins.certificates.trustUnsecureCerts(false); + } } @@ -301,6 +310,7 @@ angular.module('zmApp.controllers') + function getZmsMultiPortSupport(forceReload) { var d = $q.defer(); if (configParams.ZM_MIN_STREAMING_PORT == -1 || forceReload) { @@ -308,7 +318,8 @@ angular.module('zmApp.controllers') var apiurl = loginData.apiurl; var myurl = apiurl + '/configs/viewByName/ZM_MIN_STREAMING_PORT.json'; $http.get(myurl) - .success(function (data) { + .then(function (data) { + data = data.data; //console.log ("GOT " + JSON.stringify(data)); if (data.config && data.config.Value) { @@ -324,8 +335,8 @@ angular.module('zmApp.controllers') d.resolve(configParams.ZM_MIN_STREAMING_PORT); return (d.promise); - }) - .error(function (err) { + }, + function (err) { configParams.ZM_MIN_STREAMING_PORT = 0; log("ZM_MIN_STREAMING_PORT not supported"); setCurrentServerMultiPortSupported(false); @@ -694,7 +705,8 @@ angular.module('zmApp.controllers') if ($rootScope.platformOS == 'desktop') $state.go(state, p1, p2); else - $ionicNativeTransitions.stateGo(state, p1, p2); + $state.go(state, p1, p2); + // $ionicNativeTransitions.stateGo(state, p1, p2); }, // used when an empty server profile is created @@ -1130,13 +1142,13 @@ angular.module('zmApp.controllers') } - console.log("INIT SIMUL=" + loginData.disableSimulStreaming); - console.log("INIT PLATFORM IS=" + $rootScope.platformOS); + //console.log("INIT SIMUL=" + loginData.disableSimulStreaming); + //console.log("INIT PLATFORM IS=" + $rootScope.platformOS); if (typeof loginData.disableSimulStreaming == 'undefined') { loginData.disableSimulStreaming = ($rootScope.platformOS == 'ios') ? true : false; - console.log("INIT DISABLING SIMUL:" + loginData.disableSimulStreaming); + //console.log("INIT DISABLING SIMUL:" + loginData.disableSimulStreaming); } @@ -1442,10 +1454,9 @@ angular.module('zmApp.controllers') log("defaultServer configuration NOT found. Keeping login at defaults"); } - //console.log ("LOGS="+JSON.stringify(loginData.enableLogs)); - // now set up SSL - need to do it after data return + // from local forage - setSSLCerts(); + if (window.cordova) setCordovaHttpOptions(); // FIXME: HACK: This is the latest entry point into dataModel init, so start portal login after this @@ -1812,6 +1823,13 @@ angular.module('zmApp.controllers') // This function returns the numdigits for padding capture images //----------------------------------------------------------------------------- + + getAuthHashLogin: function () { + + return $http.get(loginData.apiurl + '/configs/viewByName/ZM_AUTH_HASH_LOGINS.json'); + + }, + getKeyConfigParams: function (forceReload) { var d = $q.defer(); @@ -1821,14 +1839,14 @@ angular.module('zmApp.controllers') var myurl = apiurl + '/configs/viewByName/ZM_EVENT_IMAGE_DIGITS.json'; debug("Config URL for digits is:" + myurl); $http.get(myurl) - .success(function (data) { + .then(function (data) { + data = data.data; log("ZM_EVENT_IMAGE_DIGITS is " + data.config.Value); configParams.ZM_EVENT_IMAGE_DIGITS = data.config.Value; d.resolve(configParams.ZM_EVENT_IMAGE_DIGITS); return (d.promise); - }) - .error(function (err) { + },function (err) { log("Error retrieving ZM_EVENT_IMAGE_DIGITS" + JSON.stringify(err), "error"); log("Taking a guess, setting ZM_EVENT_IMAGE_DIGITS to 5"); // FIXME: take a plunge and keep it at 5? @@ -1852,16 +1870,21 @@ angular.module('zmApp.controllers') //-------------------------------------------------------------------------- getPathZms: function () { var d = $q.defer(); + + var apiurl = loginData.apiurl; var myurl = apiurl + '/configs/viewByName/ZM_PATH_ZMS.json'; debug("Config URL for ZMS PATH is:" + myurl); $http.get(myurl) - .success(function (data) { + .then(function (data) { + + data = data.data; + //console.log (">>>> GOT: "+JSON.stringify(data)); configParams.ZM_PATH_ZMS = data.config.Value; d.resolve(configParams.ZM_PATH_ZMS); return (d.promise); - }) - .error(function (error) { + }, + function (error) { log("Can't retrieving ZM_PATH_ZMS: " + JSON.stringify(error)); d.resolve(""); return (d.promise); @@ -2025,7 +2048,7 @@ angular.module('zmApp.controllers') $ionicLoading.show({ template: $translate.instant('kLoadingMonitors'), animation: 'fade-in', - showBackdrop: true, + showBackdrop: false, duration: zm.loadingTimeout, maxWidth: 200, showDelay: 0 @@ -2048,8 +2071,9 @@ angular.module('zmApp.controllers') debug("ZMS Multiport reported: " + zmsPort); debug("Monitor URL to fetch is:" + myurl); $http.get(myurl /*,{timeout:15000}*/ ) - .success(function (data) { + .then(function (data) { // console.log("HTTP success got " + JSON.stringify(data.monitors)); + data = data.data; monitors = data.monitors; @@ -2068,7 +2092,8 @@ angular.module('zmApp.controllers') debug("Inside getMonitors, will also regen connkeys"); debug("Now trying to get multi-server data, if present"); $http.get(apiurl + "/servers.json") - .success(function (data) { + .then(function (data) { + data = data.data; // We found a server list API, so lets make sure // we get the hostname as it will be needed for playback log("multi server list loaded" + JSON.stringify(data)); @@ -2240,8 +2265,8 @@ angular.module('zmApp.controllers') // now get packery hide if applicable reloadMonitorDisplayStatus(); d.resolve(monitors); - }) - .error(function (err) { + }, + function (err) { log("multi server list loading error"); multiservers = []; @@ -2284,8 +2309,8 @@ angular.module('zmApp.controllers') $ionicLoading.hide(); log("Monitor load was successful, loaded " + monitors.length + " monitors"); - }) - .error(function (err) { + }, + function (err) { //console.log("HTTP Error " + err); log("Monitor load failed " + JSON.stringify(err), "error"); // To keep it simple for now, I'm translating an error @@ -2321,7 +2346,8 @@ angular.module('zmApp.controllers') $http({ url: myurl, method: 'GET', - transformResponse: undefined + transformResponse: undefined, + responseType:'text', }) // $http.get(myurl) .then(function (textsucc) { @@ -2493,6 +2519,11 @@ angular.module('zmApp.controllers') getEventsPages: function (monitorId, startTime, endTime) { //console.log("********** INSIDE EVENTS PAGES "); + + var d = $q.defer(); + + + var apiurl = loginData.apiurl; var myurl = apiurl + "/events/index"; @@ -2511,23 +2542,24 @@ angular.module('zmApp.controllers') $ionicLoading.show({ template: $translate.instant('kCalcEventSize') + '...', animation: 'fade-in', - showBackdrop: true, + showBackdrop: false, duration: zm.loadingTimeout, maxWidth: 200, showDelay: 0 }); //var myurl = (monitorId == 0) ? apiurl + "/events.json?page=1" : apiurl + "/events/index/MonitorId:" + monitorId + ".json?page=1"; - var d = $q.defer(); + $http.get(myurl) - .success(function (data) { + .then(function (data) { + data = data.data; $ionicLoading.hide(); //console.log ("**** EVENTS PAGES I GOT "+JSON.stringify(data)); //console.log("**** PAGE COUNT IS " + data.pagination.pageCount); d.resolve(data.pagination); return d.promise; - }) - .error(function (error) { + }, + function (error) { $ionicLoading.hide(); // console.log("*** ERROR GETTING TOTAL PAGES ***"); log("Error retrieving page count of events " + JSON.stringify(error), "error"); @@ -2548,9 +2580,13 @@ angular.module('zmApp.controllers') //----------------------------------------------------------------------------- // new reminder - // https://zm/api/events.json?&sort=StartTime&direction=desc + // + //https:///zm/api/events.json?&sort=StartTime&direction=desc&page=1 getEvents: function (monitorId, pageId, loadingStr, startTime, endTime) { + + + if (!pageId) pageId = 1; //console.log("ZMData getEvents called with ID=" + monitorId + "and Page=" + pageId); if (!loadingStr) { @@ -2562,7 +2598,7 @@ angular.module('zmApp.controllers') $ionicLoading.show({ template: loadingStr, animation: 'fade-in', - showBackdrop: true, + showBackdrop: false, maxWidth: 200, showDelay: 0, duration: zm.loadingTimeout, //specifically for Android - http seems to get stuck at times @@ -2582,14 +2618,11 @@ angular.module('zmApp.controllers') myurl = myurl + "/EndTime <=:" + endTime; myurl = myurl + "/AlarmFrames >=:" + (loginData.enableAlarmCount ? loginData.minAlarmCount : 0); - myurl = myurl + ".json"; - if (pageId) { - myurl = myurl + "?page=" + pageId; - } else { - //console.log("**** PAGE WAS " + pageId); - } + myurl = myurl + ".json?&sort=StartTime&direction=desc&page="+pageId; + + debug ("getEvents:"+myurl); // Simulated data // myurl = "https://api.myjson.com/bins/4jx44.json"; @@ -2597,20 +2630,18 @@ angular.module('zmApp.controllers') //console.log (">>>>>Constructed URL " + myurl); $http.get(myurl /*,{timeout:15000}*/ ) - .success(function (data) { + .then(function (data) { + data = data.data; if (loadingStr != 'none') $ionicLoading.hide(); //myevents = data.events; - myevents = data.events.reverse(); - if (monitorId == 0) { - oldevents = myevents; - } - //console.log (JSON.stringify(data)); - // console.log("DataModel Returning " + myevents.length + "events for page" + pageId); + myevents = data; + + d.resolve(myevents); return d.promise; - }) - .error(function (err) { + }, + function (err) { if (loadingStr != 'none') $ionicLoading.hide(); displayBanner('error', ['error retrieving event list', 'please try again']); //console.log("HTTP Events error " + err); @@ -2622,10 +2653,7 @@ angular.module('zmApp.controllers') d.reject(myevents); - // FIXME: Check what pagination does to this logic - if (monitorId == 0) { - oldevents = []; - } + return d.promise; }); return d.promise; @@ -2862,7 +2890,8 @@ angular.module('zmApp.controllers') debug("Logging out using API method"); $http.get(loginData.apiurl + '/host/logout.json', { timeout: 7000, - transformResponse: undefined + transformResponse: undefined, + // responseType:'text', }) .then(function (s) { debug("Logout returned... "); diff --git a/www/js/EventCtrl.js b/www/js/EventCtrl.js index 0d825eb4..32367682 100644 --- a/www/js/EventCtrl.js +++ b/www/js/EventCtrl.js @@ -45,7 +45,8 @@ angular.module('zmApp.controllers') var oldEvent; var scrollbynumber; var eventImageDigits = 5; // failsafe - var eventsPage; + var currEventsPage = 1; + var maxEventsPage = 1; var moreEvents; var pageLoaded; var enableLoadMore; @@ -236,7 +237,7 @@ angular.module('zmApp.controllers') }); $scope.showSearch = false; - eventsPage = 1; + moreEvents = true; $scope.viewTitle = { title: "" @@ -257,8 +258,8 @@ angular.module('zmApp.controllers') var apiurl = NVRDataModel.getLogin().apiurl + '/events/' + eid + '.json'; $http.get(apiurl) - .success(function (data) {}) - .error(function (err) {}); + .then(function (data) {}, + function (err) {}); } @@ -342,6 +343,8 @@ angular.module('zmApp.controllers') $scope.monitors = NVRDataModel.applyMontageMonitorPrefs(tempMon, 2)[0]; } else*/ $scope.monitors = message; + currEventsPage = 1; + maxEventsPage = 1; if ($scope.monitors.length == 0) { var pTitle = $translate.instant('kNoMonitors'); @@ -374,14 +377,8 @@ angular.module('zmApp.controllers') nolangTo = moment($rootScope.toString).locale('en').format("YYYY-MM-DD HH:mm:ss"); //NVRDataModel.debug ("GETTING EVENTS USING "+$scope.id+" "+nolangFrom+" "+ nolangTo); - NVRDataModel.getEventsPages($scope.id, nolangFrom, nolangTo) - .then(function (data) { - // console.log ("WE GOT PAGES="+JSON.stringify(data)); - eventsPage = data.pageCount || 1; - NVRDataModel.debug("EventCtrl: found " + eventsPage + " pages of events"); - pageLoaded = true; - $scope.viewTitle.title = data.count; + NVRDataModel.debug("EventCtrl: grabbing events for: id=" + $scope.id + " Date/Time:" + $rootScope.fromString + "-" + $rootScope.toString); nolangFrom = ""; @@ -391,11 +388,20 @@ angular.module('zmApp.controllers') if ($rootScope.toString) nolangTo = moment($rootScope.toString).locale('en').format("YYYY-MM-DD HH:mm:ss"); - NVRDataModel.getEvents($scope.id, eventsPage, "", nolangFrom, nolangTo) + NVRDataModel.getEvents($scope.id, currEventsPage, "", nolangFrom, nolangTo) .then(function (data) { + pageLoaded = true; + //$scope.viewTitle.title = data.pagination.count; + + console.log (JSON.stringify(data.pagination)); + if (data.pagination && data.pagination.pageCount) + maxEventsPage = data.pagination.pageCount; + + NVRDataModel.debug ("We have a total of "+maxEventsPage+" and are at page="+currEventsPage); + // console.log ("WE GOT EVENTS="+JSON.stringify(data)); - var myevents = data; + var myevents = data.events; NVRDataModel.debug("EventCtrl: success, got " + myevents.length + " events"); var loginData = NVRDataModel.getLogin(); @@ -493,7 +499,6 @@ angular.module('zmApp.controllers') } }); - }); } //------------------------------------------------------- @@ -522,6 +527,11 @@ angular.module('zmApp.controllers') function saveNow(imgsrc, r, f) { + var fname = "zmninja.jpg"; + var fn = "cordova.plugins.photoLibrary.saveImage"; + var loginData = NVRDataModel.getLogin(); + + $ionicLoading.show({ template: $translate.instant('kSavingSnapshot') + "...", noBackdrop: true, @@ -530,52 +540,106 @@ angular.module('zmApp.controllers') var url = imgsrc; NVRDataModel.log("saveNow: File path to grab is " + url); - var img = new Image(); - img.onload = function () { - // console.log("********* ONLOAD"); - var canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - var context = canvas.getContext('2d'); - context.drawImage(img, 0, 0); - - var imageDataUrl = canvas.toDataURL('image/jpeg', 1.0); - var imageData = imageDataUrl.replace(/data:image\/jpeg;base64,/, ''); - - if ($rootScope.platformOS != "desktop") { - try { - - cordova.exec( - SaveSuccess, - SaveError, - 'Canvas2ImagePlugin', - 'saveImageDataToLibrary', [imageData] - ); - // carouselUtils.setStop(curState); - } catch (e) { - - SaveError(e.message); - // carouselUtils.setStop(curState); + if ($rootScope.platformOS != 'desktop') { + + var album = 'zmNinja'; + NVRDataModel.debug("Trying to save image to album: " + album); + cordova.plugins.photoLibrary.requestAuthorization( + function () { + //url = "https://picsum.photos/200/300/?random"; + + var fileTransfer = new FileTransfer(); + var urle = encodeURI(url); + + + fileTransfer.onprogress = function (progressEvent) { + if (progressEvent.lengthComputable) { + + $timeout(function () { + var perc = Math.floor(progressEvent.loaded / progressEvent.total * 100); + $ionicLoading.show({ + template: $translate.instant('kPleaseWait') + "... (" + perc + "%)", + noBackdrop: true, + //duration: zm.httpTimeout + }); + }); + + + } + }; + + fileTransfer.download(urle, cordova.file.dataDirectory + fname, + function (entry) { + NVRDataModel.debug("local download complete: " + entry.toURL()); + NVRDataModel.debug("Now trying to move it to album"); + var pluginName = (fname == "zmNinja.mp4" ? "saveVideo" : "saveImage"); + + + cordova.plugins.photoLibrary[pluginName](entry.toURL(), album, + function (cameraRollAssetId) { + SaveSuccess(); + $cordovaFile.removeFile(cordova.file.dataDirectory, fname) + .then( + function () { + NVRDataModel.debug("file removed from data directory"); + }, + function (e) { + NVRDataModel.debug("could not delete temp file: " + JSON.stringify(e)); + } + ); + + + }, + function (err) { + NVRDataModel.debug("Saving error:" + JSON.stringify(err)); + SaveError(); + + }); + + + + + }, + function (err) { + NVRDataModel.log("error downloading:" + JSON.stringify(err)); + SaveError(); + }, !loginData.enableStrictSSL, {}); + + + + + // User gave us permission to his library, retry reading it! + }, + function (err) { + // User denied the access + NVRDataModel.debug("Permission not granted"); + SaveError(); + }, // if options not provided, defaults to {read: true}. + + { + read: true, + write: true } - } else { - - var fname = r + f + ".png"; - fname = fname.replace(/\//, "-"); - fname = fname.replace(/\.jpg/, ''); - - canvas.toBlob(function (blob) { - saveAs(blob, fname); - SaveSuccess(); - }); - } - }; - try { - img.src = url; - // console.log ("SAVING IMAGE SOURCE"); - } catch (e) { - SaveError(e.message); + ); + + } else { + //desktop + + $ionicLoading.hide(); + + $rootScope.zmPopup = SecuredPopups.show('alert', { + title: $translate.instant('kNote'), + template: $translate.instant('kDownloadVideoImage')+"<br/><br/><center><a href='" + url + "' class='button button-assertive icon ion-android-download' download>"+" "+$translate.instant('kDownload')+"</a></center>", + okText: $translate.instant('kDismiss'), + okType:'button-stable' + }); + + + } + + } @@ -1655,7 +1719,8 @@ angular.module('zmApp.controllers') return $http.delete(apiDelete) - .success(function (data) { + .then(function (data) { + data = data.data; $ionicLoading.hide(); NVRDataModel.debug("delete output: " + JSON.stringify(data)); @@ -1685,8 +1750,8 @@ angular.module('zmApp.controllers') //doRefresh(); - }) - .error(function (data) { + }, + function (data) { $ionicLoading.hide(); NVRDataModel.debug("delete error: " + JSON.stringify(data)); NVRDataModel.displayBanner('error', [$translate.instant('kDeleteEventError1'), $translate.instant('kDeleteEventError2')]); @@ -1760,11 +1825,12 @@ angular.module('zmApp.controllers') var af = "/AlarmFrames >=:" + (ld.enableAlarmCount ? ld.minAlarmCount : 0); - var apiurl = ld.apiurl + "/events/consoleEvents/1%20hour" + af + ".json"; + var apiurl = ld.apiurl + "/events/consoleEvents/1 hour" + af + ".json"; NVRDataModel.debug("consoleEvents API:" + apiurl); $http.get(apiurl) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug(JSON.stringify(data)); $scope.hours = []; var p = data.results; @@ -1796,10 +1862,11 @@ angular.module('zmApp.controllers') } }); - apiurl = ld.apiurl + "/events/consoleEvents/1%20day" + af + ".json"; + apiurl = ld.apiurl + "/events/consoleEvents/1 day" + af + ".json"; NVRDataModel.debug("consoleEvents API:" + apiurl); $http.get(apiurl) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug(JSON.stringify(data)); $scope.days = []; var p = data.results; @@ -1828,10 +1895,11 @@ angular.module('zmApp.controllers') } }); - apiurl = ld.apiurl + "/events/consoleEvents/1%20week" + af + ".json"; + apiurl = ld.apiurl + "/events/consoleEvents/1 week" + af + ".json"; NVRDataModel.debug("consoleEvents API:" + apiurl); $http.get(apiurl) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug(JSON.stringify(data)); $scope.weeks = []; var p = data.results; @@ -1861,10 +1929,11 @@ angular.module('zmApp.controllers') } }); - apiurl = ld.apiurl + "/events/consoleEvents/1%20month" + af + ".json"; + apiurl = ld.apiurl + "/events/consoleEvents/1 month" + af + ".json"; NVRDataModel.debug("consoleEvents API:" + apiurl); $http.get(apiurl) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug(JSON.stringify(data)); $scope.months = []; var p = data.results; @@ -2035,7 +2104,8 @@ angular.module('zmApp.controllers') var myurl = loginData.apiurl + '/events/' + event.Event.Id + ".json"; NVRDataModel.log("API for event details" + myurl); $http.get(myurl) - .success(function (data) { + .then(function (data) { + data = data.data; $scope.FrameArray = data.event.Frame; // $scope.slider_options.scale=[]; @@ -2070,8 +2140,8 @@ angular.module('zmApp.controllers') oldEvent = event; //console.log (JSON.stringify(data)); - }) - .error(function (err) { + }, + function (err) { NVRDataModel.log("Error retrieving detailed frame API " + JSON.stringify(err)); NVRDataModel.displayBanner('error', ['could not retrieve frame details', 'please try again']); }); @@ -2147,7 +2217,8 @@ angular.module('zmApp.controllers') var myurl_frames = loginData.apiurl + '/events/' + event.Event.Id + ".json"; NVRDataModel.log("API for event details" + myurl_frames); $http.get(myurl_frames) - .success(function (data) { + .then(function (data) { + data = data.data; $scope.FrameArray = data.event.Frame; // $scope.slider_options.scale=[]; @@ -2166,8 +2237,8 @@ angular.module('zmApp.controllers') } //console.log (JSON.stringify(data)); - }) - .error(function (err) { + }, + function (err) { NVRDataModel.log("Error retrieving detailed frame API " + JSON.stringify(err)); NVRDataModel.displayBanner('error', [$translate.instant('kErrorFrameBanner'), $translate.instant('kErrorPleaseTryAgain')]); }); @@ -2208,7 +2279,8 @@ angular.module('zmApp.controllers') var myurl2 = loginData.apiurl + '/events/' + event.Event.Id + ".json"; NVRDataModel.log("API for event details" + myurl2); $http.get(myurl2) - .success(function (data) { + .then(function (data) { + data = data.data; $scope.FrameArray = data.event.Frame; // $scope.slider_options.scale=[]; $scope.slider_options.scale = []; @@ -2229,8 +2301,8 @@ angular.module('zmApp.controllers') } //console.log (JSON.stringify(data)); - }) - .error(function (err) { + }, + function (err) { NVRDataModel.log("Error retrieving detailed frame API " + JSON.stringify(err)); NVRDataModel.displayBanner('error', [$translate.instant('kErrorFrameBanner'), $translate.instant('kErrorPleaseTryAgain')]); }); @@ -2650,7 +2722,7 @@ angular.module('zmApp.controllers') $ionicLoading.show({ template: $translate.instant('kSearchCancelled'), animation: 'fade-in', - showBackdrop: true, + showBackdrop: false, duration: 2000, maxWidth: 200, showDelay: 0 @@ -2666,14 +2738,17 @@ angular.module('zmApp.controllers') // the events API does not return an error for anything // except greater page limits than reported - // console.log("***** LOADING MORE INFINITE SCROLL ****"); - eventsPage--; - if ((eventsPage <= 0) && (pageLoaded)) { + console.log("***** LOADING MORE INFINITE SCROLL ****"); + + if ((currEventsPage >= maxEventsPage) && (pageLoaded)) { moreEvents = false; - //console.log("*** At Page " + eventsPage + ", not proceeding"); + NVRDataModel.debug ("No more - We have a total of "+maxEventsPage+" and are at page="+currEventsPage); + + console.log("*** At Page " + currEventsPage + " of "+maxEventsPage+", not proceeding"); return; } + currEventsPage++; if (!enableLoadMore) { moreEvents = false; // Don't ion-scroll till enableLoadMore is true; $scope.$broadcast('scroll.infiniteScrollComplete'); @@ -2684,7 +2759,7 @@ angular.module('zmApp.controllers') var loadingStr = ""; if ($scope.search.text != "") { - var toastStr = $translate.instant('kToastSearchingPage') + eventsPage; + var toastStr = $translate.instant('kToastSearchingPage') + currEventsPage; $ionicLoading.show({ maxwidth: 100, scope: $scope, @@ -2701,11 +2776,12 @@ angular.module('zmApp.controllers') if ($rootScope.toString) nolangTo = moment($rootScope.toString).locale('en').format("YYYY-MM-DD HH:mm:ss"); - NVRDataModel.getEvents($scope.id, eventsPage, loadingStr, nolangFrom, nolangTo) + NVRDataModel.getEvents($scope.id, currEventsPage, loadingStr, nolangFrom, nolangTo) .then(function (data) { var loginData = NVRDataModel.getLogin(); // console.log("Got new page of events with Page=" + eventsPage); - var myevents = data; + var myevents = data.events; + for (var i = 0; i < myevents.length; i++) { @@ -2855,7 +2931,7 @@ angular.module('zmApp.controllers') $scope.constructThumbnail = function (event) { var stream = ""; stream = event.Event.baseURL + - "/index.php?view=image&show=capture&fid=" + + "/index.php?view=image&show=capture&fid=" + (event.Event.MaxScoreFrameId ? event.Event.MaxScoreFrameId : "1&eid=" + event.Event.Id) + "&width=" + event.Event.thumbWidth * 2 + "&height=" + event.Event.thumbHeight * 2; @@ -2997,6 +3073,8 @@ angular.module('zmApp.controllers') // console.log("***Pull to Refresh"); NVRDataModel.debug("Reloading monitors"); + maxEventsPage = 1; + currEventsPage = 1; var refresh = NVRDataModel.getMonitors(1); refresh.then(function (data) { $scope.monitors = data; diff --git a/www/js/EventModalCtrl.js b/www/js/EventModalCtrl.js index cf77aa3d..0e6d6be7 100644 --- a/www/js/EventModalCtrl.js +++ b/www/js/EventModalCtrl.js @@ -3,7 +3,7 @@ /* jslint browser: true*/ /* global saveAs, cordova,StatusBar,angular,console,ionic, moment, Chart */ -angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', '$filter', 'SecuredPopups', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate, $filter, SecuredPopups) { +angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', '$filter', 'SecuredPopups', '$cordovaFile', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate, $filter, SecuredPopups, $cordovaFile) { var playerReady = false; @@ -86,13 +86,13 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro NVRDataModel.debug("Setting playback to " + $scope.streamMode); - if ($rootScope.platformOS == 'desktop') { + if ($rootScope.platformOS == 'desktop') { window.addEventListener('keydown', keyboardHandler, true); } - // Keyboard handler for desktop versions - function keyboardHandler(evt) { + // Keyboard handler for desktop versions + function keyboardHandler(evt) { var handled = false; var keyCodes = { @@ -103,36 +103,31 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro ESC: 27, FITFILL_F: 70, - PLAY_SELECT:13 - + PLAY_SELECT: 13 + }; - $timeout (function () { + $timeout(function () { var keyCode = evt.keyCode; - - console.log (keyCode + " PRESSED"); + + console.log(keyCode + " PRESSED"); if (keyCode == keyCodes.ESC) { $scope.closeModal(); - } - else if (keyCode == keyCodes.LEFT) { - - $scope.jumpToEvent($scope.prevId, -1); - } - else if (keyCode == keyCodes.RIGHT) { + } else if (keyCode == keyCodes.LEFT) { + + $scope.jumpToEvent($scope.prevId, -1); + } else if (keyCode == keyCodes.RIGHT) { $scope.jumpToEvent($scope.nextId, 1); - } - else if (keyCode == keyCodes.FITFILL_F) { + } else if (keyCode == keyCodes.FITFILL_F) { $scope.scaleImage(); - } - else if (keyCode == keyCodes.PLAY_SELECT) { + } else if (keyCode == keyCodes.PLAY_SELECT) { if ($scope.isSnapShot() && !$scope.liveFeedMid) { $scope.convertSnapShotToStream(); - } - else { - NVRDataModel.debug ("Not in snapshot mode, ignoring"); + } else { + NVRDataModel.debug("Not in snapshot mode, ignoring"); } } @@ -407,13 +402,14 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro // pass: loginData.password } }) - .success(function (resp) { - // NVRDataModel.debug ("processEvent success:"+JSON.stringify(resp)); + .then(function (resp) { + //NVRDataModel.debug ("processEvent success:"+JSON.stringify(resp)); + resp = resp.data; if (resp.result == "Ok") { if (resp.status) $scope.currentProgress.progress = resp.status.progress; - if (resp.status) $scope.eventId = resp.status.event; + if (resp.status && resp.status.event) $scope.eventId = resp.status.event; $scope.d_eventId = $scope.eventId; if (resp.status) $scope.currentRate = resp.status.rate; @@ -444,8 +440,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro NVRDataModel.debug("so I'm regenerating Connkey to " + $scope.connKey);*/ } - }) - .error(function (resp) { + }, + function (resp) { NVRDataModel.debug("processEvent error:" + JSON.stringify(resp)); //eventQueryHandle = $timeout (function(){checkEvent();}, zm.eventPlaybackQuery); @@ -509,7 +505,7 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro $ionicLoading.show({ template: $translate.instant('kDone'), noBackdrop: true, - duration: 1000 + duration: 2000 }); NVRDataModel.debug("ModalCtrl:Photo saved successfuly"); } @@ -518,9 +514,9 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro $ionicLoading.show({ template: $translate.instant('kErrorSave'), noBackdrop: true, - duration: 2000 + duration: 3000 }); - NVRDataModel.log("Error saving image: " + e.message); + //NVRDataModel.log("Error saving image: " + e.message); //console.log("***ERROR"); } @@ -528,6 +524,36 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro // streamReq.send( streamParms+"&command="+CMD_SEEK+"&offset="+offset ); }; + + $scope.saveEventVideoToPhoneWithPerms = function () { + + if ($rootScope.platformOS != 'android') { + saveNow(); + return; + } + + NVRDataModel.debug("EventModalCtrl: Permission checking for write"); + var permissions = cordova.plugins.permissions; + permissions.hasPermission(permissions.WRITE_EXTERNAL_STORAGE, checkPermissionCallback, null); + + function checkPermissionCallback(status) { + if (!status.hasPermission) { + SaveError("No permission to write to external storage"); + } + permissions.requestPermission(permissions.WRITE_EXTERNAL_STORAGE, succ, err); + } + + function succ(s) { + saveNow(); + } + + function err(e) { + SaveError("Error in requestPermission"); + } + + }; + + //----------------------------------------------------------------------- // Saves a snapshot of the monitor image to phone storage //----------------------------------------------------------------------- @@ -562,8 +588,21 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro function processSaveEventImageToPhone(onlyAlarms) { + + + if ($scope.isSnapShot()) { + + + $scope.selectEventUrl = $scope.constructStream(); + NVRDataModel.debug("just saving current snapshot:" + $scope.selectEventUrl); + saveNow("image"); + return; + + } + if ($scope.loginData.useNphZmsForEvents) { NVRDataModel.log("Use ZMS stream to save to phone"); + saveEventImageToPhoneZms(onlyAlarms); } else { @@ -587,13 +626,13 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro sendCommand('1', $scope.connKey). then(function (resp) { - console.log ("PAUSE ANSWER IS " + JSON.stringify(resp)); + console.log("PAUSE ANSWER IS " + JSON.stringify(resp)); if (resp && resp.data && resp.data.status) $scope.currentProgress.progress = resp.data.status.progress; - else - $scope.currentProgress.progress = 100; - + else + $scope.currentProgress.progress = 100; + // console.log ("STEP 0 progress is " + $scope.currentProgress.progress); $scope.slides = []; @@ -706,24 +745,33 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro // console.log ("________________UNUSED?_______________________"); var curState = carouselUtils.getStop(); carouselUtils.setStop(true); + var url; //console.log("Your index is " + $scope.mycarousel.index); //console.log("Associated image is " + $scope.slides[$scope.mycarousel.index].img); + + + NVRDataModel.debug("ModalCtrl: SaveEventImageToPhone called"); var canvas, context, imageDataUrl, imageData; var loginData = NVRDataModel.getLogin(); // for alarms only - if (onlyAlarms) $scope.mycarousel.index = 0; - var url; + if (onlyAlarms || ($scope.defaultVideo !== undefined && $scope.defaultVideo != '')) + $scope.mycarousel.index = 1; + if ($scope.event.Event.imageMode == 'path') { url = $scope.playbackURL + '/index.php?view=image&rand=' + $rootScope.rand + "&path=" + $scope.relativePath + $scope.slides[$scope.mycarousel.index].img; } else { + + console.log("SLIDES " + JSON.stringify($scope.slides)); + console.log("CAROUSEL " + JSON.stringify($scope.mycarousel)); + url = $scope.playbackURL + '/index.php?view=image&rand=' + $rootScope.rand + "&eid=" + $scope.eventId + - "&fid=" + $scope.slides[$scope.mycarousel.index].id; + "&fid=" + $scope.slides[$scope.mycarousel.index - 1].id; } if ($rootScope.authSession != 'undefined') { @@ -867,68 +915,141 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro text: '', type: 'button-positive button-small ion-checkmark-round', onTap: function (e) { - saveNow(); + saveNow("image"); } } ] }); - function saveNow() { - $ionicLoading.show({ - template: $translate.instant('kSavingSnapshot') + "...", - noBackdrop: true, - duration: zm.httpTimeout - }); - var url = $scope.selectEventUrl; - NVRDataModel.log("saveNow: File path to grab is " + url); - - var img = new Image(); - img.onload = function () { - // console.log("********* ONLOAD"); - canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - context = canvas.getContext('2d'); - context.drawImage(img, 0, 0); - - imageDataUrl = canvas.toDataURL('image/jpeg', 1.0); - imageData = imageDataUrl.replace(/data:image\/jpeg;base64,/, ''); - - if ($rootScope.platformOS != "desktop") { - try { - - cordova.exec( - SaveSuccess, - SaveError, - 'Canvas2ImagePlugin', - 'saveImageDataToLibrary', [imageData] - ); - // carouselUtils.setStop(curState); - } catch (e) { - - SaveError(e.message); - // carouselUtils.setStop(curState); - } - } else { - var fname = $scope.relativePath + $scope.slides[$scope.slideIndex].img + ".png"; - fname = fname.replace(/\//, "-"); - fname = fname.replace(/\.jpg/, ''); - canvas.toBlob(function (blob) { - saveAs(blob, fname); - SaveSuccess(); - }); + } + + function saveNow(t) { + + var fname = "zmninja.jpg"; + var fn = "cordova.plugins.photoLibrary.saveImage"; + var loginData = NVRDataModel.getLogin(); + + $ionicLoading.show({ + template: $translate.instant('kPleaseWait') + "...", + noBackdrop: true, + duration: zm.httpTimeout + }); + + if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '' && t != "image") { + $scope.selectEventUrl = $scope.video_url; + fname = "zmNinja.mp4"; + fn = "cordova.plugins.photoLibrary.saveVideo"; + + + } + + NVRDataModel.debug("-->Going to try and download " + $scope.selectEventUrl); + var url = $scope.selectEventUrl; + + + NVRDataModel.log(">>saveNow: File path to grab is " + url); + + if ($rootScope.platformOS != 'desktop') { + + var album = 'zmNinja'; + NVRDataModel.debug("Trying to save image to album: " + album); + cordova.plugins.photoLibrary.requestAuthorization( + function () { + //url = "https://picsum.photos/200/300/?random"; + + var fileTransfer = new FileTransfer(); + var urle = encodeURI(url); + + + fileTransfer.onprogress = function (progressEvent) { + if (progressEvent.lengthComputable) { + + $timeout(function () { + var perc = Math.floor(progressEvent.loaded / progressEvent.total * 100); + $ionicLoading.show({ + template: $translate.instant('kPleaseWait') + "... (" + perc + "%)", + noBackdrop: true, + //duration: zm.httpTimeout + }); + }); + + + } + }; + + fileTransfer.download(urle, cordova.file.dataDirectory + fname, + function (entry) { + NVRDataModel.debug("local download complete: " + entry.toURL()); + NVRDataModel.debug("Now trying to move it to album"); + var pluginName = (fname == "zmNinja.mp4" ? "saveVideo" : "saveImage"); + + + cordova.plugins.photoLibrary[pluginName](entry.toURL(), album, + function (cameraRollAssetId) { + SaveSuccess(); + $cordovaFile.removeFile(cordova.file.dataDirectory, fname) + .then( + function () { + NVRDataModel.debug("file removed from data directory"); + }, + function (e) { + NVRDataModel.debug("could not delete temp file: " + JSON.stringify(e)); + } + ); + + + }, + function (err) { + NVRDataModel.debug("Saving error:" + JSON.stringify(err)); + SaveError(); + + }); + + + + + }, + function (err) { + NVRDataModel.log("error downloading:" + JSON.stringify(err)); + SaveError(); + }, !loginData.enableStrictSSL, {}); + + + + + // User gave us permission to his library, retry reading it! + }, + function (err) { + // User denied the access + NVRDataModel.debug("Permission not granted"); + SaveError(); + }, // if options not provided, defaults to {read: true}. + + { + read: true, + write: true } - }; - try { - img.src = url; - // console.log ("SAVING IMAGE SOURCE"); - } catch (e) { - SaveError(e.message); - } + ); + + } else { + //desktop + + $ionicLoading.hide(); + + $rootScope.zmPopup = SecuredPopups.show('alert', { + title: $translate.instant('kNote'), + template: $translate.instant('kDownloadVideoImage')+"<br/><br/><center><a href='" + url + "' class='button button-assertive icon ion-android-download' download>"+" "+$translate.instant('kDownload')+"</a></center>", + okText: $translate.instant('kDismiss'), + okType:'button-stable' + }); + + + } + } $scope.reloadView = function () { @@ -946,8 +1067,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro $scope.constructStream = function (monitor) { + //console.log ("STREAMSTATE ="+currentStreamState); if ($scope.animationInProgress) return ""; - var stream = ""; // eventId gets populated when prepareModal completes if (currentStreamState == streamState.STOPPED || !$scope.eventId) { @@ -971,7 +1092,7 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro } //console.log ($scope.connKey ); - // console.log ("STREAM="+stream); + //console.log ("STREAM="+stream); //console.log ("EID="+$scope.eventId); if ($rootScope.basicAuthToken && stream) stream += "&basicauth=" + $rootScope.basicAuthToken; return stream; @@ -1166,7 +1287,7 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro $scope.videoTime = function (s, c) { - // console.log ("VIDEO TIME WITH "+s+ " and "+c); + // console.log ("VIDEO TIME WITH "+s+ " and "+c); var a, o; if (NVRDataModel.getLogin().useLocalTimeZone) { a = moment.tz(s, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()); @@ -1184,10 +1305,10 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro $scope.$on('modal.removed', function (e, m) { - if ($rootScope.platformOS == 'desktop') { - NVRDataModel.debug ("Removing keyboard handler"); + if ($rootScope.platformOS == 'desktop') { + NVRDataModel.debug("Removing keyboard handler"); window.removeEventListener('keydown', keyboardHandler, true); - + } NVRDataModel.debug("Deregistering broadcast handles"); @@ -1429,7 +1550,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro }); return $http.delete(apiDelete) - .success(function (data) { + .then(function (data) { + data = data.data; $ionicLoading.hide(); // NVRDataModel.debug("delete output: " + JSON.stringify(data)); @@ -1459,8 +1581,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro //doRefresh(); - }) - .error(function (data) { + }, + function (data) { $ionicLoading.hide(); NVRDataModel.debug("delete error: " + JSON.stringify(data)); NVRDataModel.displayBanner('error', [$translate.instant('kDeleteEventError1'), $translate.instant('kDeleteEventError2')]); @@ -1810,8 +1932,7 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro // console.log ("**ONLY ALARM AT " + i + "of " + data.event.Frame.length); - if (ts != data.event.Frame[i].TimeStamp) - { + if (ts != data.event.Frame[i].TimeStamp) { tempAlarms.push({ id: data.event.Frame[i].Id, @@ -1819,7 +1940,7 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro }); ts = data.event.Frame[i].TimeStamp; } - + } diff --git a/www/js/EventServer.js b/www/js/EventServer.js index fae8b307..a460795c 100644 --- a/www/js/EventServer.js +++ b/www/js/EventServer.js @@ -16,39 +16,25 @@ angular.module('zmApp.controllers') var localNotificationId = 0; var pushInited = false; var isTimerOn = false; + var nativeWebSocketId = -1; + //-------------------------------------------------------------------------- // called when the websocket is opened //-------------------------------------------------------------------------- - function openHandshake() { - NVRDataModel.log("Inside openHandshake"); - var loginData = NVRDataModel.getLogin(); - if (loginData.isUseEventServer == false || loginData.eventServer == "") { - NVRDataModel.log("openHandShake: no event server"); - return; - } - - if (typeof ws == 'undefined') { - NVRDataModel.debug("websocket is undefined, need to create ws before I can sent auth"); - ws = $websocket(loginData.eventServer, { - reconnectIfNotNormalClose: false - }); - // ws.onOpen(openHandshake); - // return; - } + function handleOpen(data) { + NVRDataModel.debug("WebSocket open called with:" + JSON.stringify(data)); + var loginData = NVRDataModel.getLogin(); NVRDataModel.log("openHandshake: Websocket open, sending Auth"); - ws.send({ - event: 'auth', - data: { - user: loginData.username, - password: loginData.password - } - + sendMessage("auth", { + user: loginData.username, + password: loginData.password }); + if ($rootScope.apnsToken != '') { var plat = $ionicPlatform.is('ios') ? 'ios' : 'android'; var ld = NVRDataModel.getLogin(); @@ -60,276 +46,296 @@ angular.module('zmApp.controllers') // let's do this only if disabled. If enabled, I suppose registration // will be called? //if (ld.disablePush) - if (1) { - //console.log ("HANDSHAKE MESSAGE WITH "+$rootScope.monstring); - ws.send({ - event: 'push', - data: { - type: 'token', - platform: plat, - token: $rootScope.apnsToken, - monlist: $rootScope.monstring, - intlist: $rootScope.intstring, - state: pushstate - } - }); - } + //console.log ("HANDSHAKE MESSAGE WITH "+$rootScope.monstring); + + sendMessage("push", { + type: 'token', + platform: plat, + token: $rootScope.apnsToken, + monlist: $rootScope.monstring, + intlist: $rootScope.intstring, + state: pushstate + + }); } } - //-------------------------------------------------------------------------- - // Called once at app start. Does a lazy definition of websockets open - //-------------------------------------------------------------------------- - function init() { + function handleClose(event) { + console.log("*********** WEBSOCKET CLOSE CALLED"); + if (!NVRDataModel.getLogin().isUseEventServer) return; - $rootScope.isAlarm = 0; - $rootScope.alarmCount = "0"; + if (!isTimerOn) { + NVRDataModel.log("Will try to reconnect in 10 sec.."); + $timeout(init, 10000); + isTimerOn = true; + } + } - isTimerOn = false; + function handleError(event) { + console.log("*********** WEBSOCKET ERROR CALLED"); + if (!NVRDataModel.getLogin().isUseEventServer) return; - var d = $q.defer(); + if (!isTimerOn) { + NVRDataModel.log("Will try to reconnect in 10 sec.."); + $timeout(init, 10000); + isTimerOn = true; + } + } - var loginData = NVRDataModel.getLogin(); + function handleMessage(smsg) { + //NVRDataModel.debug ("Websocket received message:"+smsg); + str = JSON.parse(smsg); + NVRDataModel.debug("Real-time event: " + JSON.stringify(str)); - //console.log ("INIT GOT " + JSON.stringify(loginData)); - if (loginData.isUseEventServer == false || !loginData.eventServer) { - NVRDataModel.log("No Event Server present. Not initializing"); - d.reject("false"); - return d.promise; - } - //if (!$rootScope.apnsToken) - if (!pushInited) pushInit(); + // Error messages + if (str.status != 'Success') { + NVRDataModel.log("Event Error: " + JSON.stringify(str)); - // console.log ("WS TYPEOF="+ typeof ws); - // console.log ("WS="+JSON.stringify(ws)); - /*if (typeof ws !== 'undefined') - { - NVRDataModel.debug("websocket already initialized --Forcing close"); - ws.close(true); - ws=undefined; - - }*/ + if (str.reason == 'APNSDISABLED') { + console.log("FORCE CLOSING"); + ws.close(); + NVRDataModel.displayBanner('error', ['Event Server: APNS disabled'], 2000, 6000); + $rootScope.apnsToken = ""; + } - NVRDataModel.log("Initializing Websocket with URL " + - loginData.eventServer); - /* ws = $websocket.$new( - { - url: loginData.eventServer, - reconnect: true, - reconnectInterval: 60000, - lazy: true - });*/ - - ws = $websocket(loginData.eventServer, { - reconnectIfNotNormalClose: false - }); - ws.onOpen(openHandshake); + } - initCalled = true; - // Transmit auth information to server - // ws.$on('$open', openHandshake); + if (str.status == 'Success' && (str.event == 'auth')) { + if (str.version == undefined) + str.version = "0.1"; + if (NVRDataModel.versionCompare(str.version, zm.minEventServerVersion) == -1) { + $rootScope.zmPopup = $ionicPopup.alert({ + title: $translate.instant('kEventServerVersionTitle'), + template: $translate.instant('kEventServerVersionBody1') + " " + str.version + ". " + $translate.instant('kEventServerVersionBody2') + " " + + zm.minEventServerVersion, + okText: $translate.instant('kButtonOk'), + cancelText: $translate.instant('kButtonCancel'), + }); + } - if (ws) { - ws.onErrorCallbacks = []; - NVRDataModel.debug("Removing error handlers for websocket"); } - NVRDataModel.debug("Setting up websocket error handler"); - //ws.$on('$error', function(e) - ws.onError(function (e) { + if (str.status == 'Success' && str.event == 'alarm') // new events + { - NVRDataModel.debug("Websocket Errorhandler called"); + var localNotText; + // ZMN specific hack for Event Server + if (str.supplementary != 'true') { + new Audio('sounds/blop.mp3').play(); + localNotText = ""; + $rootScope.isAlarm = 1; + + // Show upto a max of 99 when it comes to display + // so aesthetics are maintained + if ($rootScope.alarmCount == "99") { + $rootScope.alarmCount = "99+"; + } + if ($rootScope.alarmCount != "99+") { + $rootScope.alarmCount = (parseInt($rootScope.alarmCount) + 1).toString(); + } - var timeElapsedSinceResume = NVRDataModel.getTimeSinceResumed(); + } else { + NVRDataModel.debug("received supplementary event information over websockets"); + } + var eventsToDisplay = []; + var listOfMonitors = []; + for (var iter = 0; iter < str.events.length; iter++) { + // lets stack the display so they don't overwrite + //eventsToDisplay.push(str.events[iter].Name + ": latest new alarm (" + str.events[iter].EventId + ")"); + var txt = str.events[iter].EventId; + if (str.events[iter].Cause) { + txt = str.events[iter].Cause; + } + eventsToDisplay.push(str.events[iter].Name + ": " + txt); + localNotText = localNotText + str.events[iter].Name + ": " + txt + ","; + listOfMonitors.push(str.events[iter].MonitorId); - if (timeElapsedSinceResume == -1) { - // so we display error - timeElapsedSinceResume = moment().subtract('1', hour); } + localNotText = localNotText.substring(0, localNotText.length - 1); - var duration = moment.duration(moment().diff(timeElapsedSinceResume)).asSeconds().toFixed(1); + // if we are in background, do a local notification, else do an in app display + if (!NVRDataModel.isBackground()) { - NVRDataModel.debug(">> time since resumed is " + duration + " seconds"); + //emit alarm details - this is when received over websockets + $rootScope.$broadcast('alarm', { + message: listOfMonitors + }); - if (duration > zm.waitTimeTillResume) { + if (str.supplementary != 'true') { - $timeout(function () { - var eserr = $translate.instant('kEventServerConnErr'); - NVRDataModel.displayBanner('error', [eserr]); - }, 1000); // leave time for transitions + NVRDataModel.debug("App is in foreground, displaying banner"); + if (eventsToDisplay.length > 0) { - } else { - NVRDataModel.debug("ES error happened " + timeElapsedSinceResume + " secs after resume, maybe fake, lets wait..."); + if (eventsToDisplay.length == 1) { + //console.log("Single Display: " + eventsToDisplay[0]); + NVRDataModel.displayBanner('alarm', [eventsToDisplay[0]], 5000, 5000); + } else { + NVRDataModel.displayBanner('alarm', eventsToDisplay, + 5000, 5000 * eventsToDisplay.length); + } + + } + } } + } + } - /*if (typeof ws !== 'undefined'){ - NVRDataModel.debug ("-->Forcing socket close"); - ws.close(true); + //-------------------------------------------------------------------------- + // Called once at app start. Does a lazy definition of websockets open + //-------------------------------------------------------------------------- + function init() { - }*/ + $rootScope.isAlarm = 0; + $rootScope.alarmCount = "0"; + isTimerOn = false; - ws = undefined; + var d = $q.defer(); + var loginData = NVRDataModel.getLogin(); - NVRDataModel.log("Will try to reconnect in 10 sec.."); - if (!isTimerOn) { - $timeout(init, 10000); - isTimerOn = true; - } + if (loginData.isUseEventServer == false || !loginData.eventServer) { + NVRDataModel.log("No Event Server present. Not initializing"); + d.reject("false"); + return d.promise; + } + NVRDataModel.log("Initializing Websocket with URL " + + loginData.eventServer); - //console.log ("VALUE TIME " + lastEventServerCheck); - //console.log ("NOW TIME " + Date.now()); - }); + if ($rootScope.platforOS == 'desktop') { + NVRDataModel.debug("Using browser websockets..."); + return setupDesktopSocket(); + } else { + NVRDataModel.debug("Using native websockets..."); + return setupMobileSocket(); - ws.onClose(function () - // ws.$on('$close', function() - { - NVRDataModel.log("Websocket closed"); - ws = undefined; + } - var ld = NVRDataModel.getLogin(); - /* if (ld.isUseEventServer && !isTimerOn) { - // this means remote error, because zmN still - // wants it on - $timeout ( init, 10000 ); - isTimerOn = true; - }*/ + } - }); - // Handles responses back from ZM ES + function setupMobileSocket() { - ws.onMessage(function (str) - // ws.$on('$message', function(str) + var loginData = NVRDataModel.getLogin(); + var d = $q.defer(); - { - str = JSON.parse(str.data); - //console.log ("FULL MESSAGE="+JSON.stringify(str.data)); - NVRDataModel.debug("Real-time event: " + JSON.stringify(str)); + var wsOptions = { + url: loginData.eventServer, + acceptAllCerts: !loginData.enableStrictSSL + }; + + CordovaWebsocketPlugin.wsConnect(wsOptions, + function (recvEvent) { + console.log("Received callback from WebSocket: " + recvEvent.callbackMethod); + if (recvEvent.callbackMethod == 'onMessage') { + handleMessage(recvEvent.message); + } else if (recvEvent.callbackMethod == 'onClose') { + handleClose(); + } else if (recvEvent.callbackMethod == 'onFail') { + handleError(); + } - // Error messages - if (str.status != 'Success') { - NVRDataModel.log("Event Error: " + JSON.stringify(str)); + }, + function (success) { + console.log("Connected to WebSocket with id: " + success.webSocketId); + nativeWebSocketId = success.webSocketId; + handleOpen(success); + if (!pushInited) { + NVRDataModel.debug("Initializing FCM push"); + pushInit(); + } + d.resolve(true); + return d.promise; + }, + function (error) { + console.log("Failed to connect to WebSocket: " + + "code: " + error.code + + ", reason: " + error.reason + + ", exception: " + error.exception); + d.resolve(false); + return d.promise; + } + ); + return d.promise; + } - if (str.reason == 'APNSDISABLED') { - ws.close(); - NVRDataModel.displayBanner('error', ['Event Server: APNS disabled'], 2000, 6000); - $rootScope.apnsToken = ""; - } + function setupDesktopSocket() { - } + var d = $q.defer(); + ws = new WebSocket(loginData.eventServer); - if (str.status == 'Success' && (str.event == 'auth')) { - if (str.version == undefined) - str.version = "0.1"; - if (NVRDataModel.versionCompare(str.version, zm.minEventServerVersion) == -1) { - $rootScope.zmPopup = $ionicPopup.alert({ - title: $translate.instant('kEventServerVersionTitle'), - template: $translate.instant('kEventServerVersionBody1') + " " + str.version + ". " + $translate.instant('kEventServerVersionBody2') + " " + - zm.minEventServerVersion, - okText: $translate.instant('kButtonOk'), - cancelText: $translate.instant('kButtonCancel'), - }); - } + ws.onopen = function (event) { + handleOpen(event.data); + if (!pushInited) { + NVRDataModel.debug("Initializing FCM push"); + pushInit(); + } + d.resolve("true"); + return d.promise; + }; - } - if (str.status == 'Success' && str.event == 'alarm') // new events - { + ws.onclose = function (event) { + handleClose(event); + d.reject("error"); + return d.promise; - var localNotText; - // ZMN specific hack for Event Server - if (str.supplementary != 'true') { - new Audio('sounds/blop.mp3').play(); - localNotText = ""; - $rootScope.isAlarm = 1; - - // Show upto a max of 99 when it comes to display - // so aesthetics are maintained - if ($rootScope.alarmCount == "99") { - $rootScope.alarmCount = "99+"; - } - if ($rootScope.alarmCount != "99+") { - $rootScope.alarmCount = (parseInt($rootScope.alarmCount) + 1).toString(); - } + }; - } else { - NVRDataModel.debug("received supplementary event information over websockets"); - } - var eventsToDisplay = []; - var listOfMonitors = []; - for (var iter = 0; iter < str.events.length; iter++) { - // lets stack the display so they don't overwrite - //eventsToDisplay.push(str.events[iter].Name + ": latest new alarm (" + str.events[iter].EventId + ")"); - var txt = str.events[iter].EventId; - if (str.events[iter].Cause) { - txt = str.events[iter].Cause; - } - eventsToDisplay.push(str.events[iter].Name + ": " + txt); - localNotText = localNotText + str.events[iter].Name + ": " + txt + ","; - listOfMonitors.push(str.events[iter].MonitorId); + ws.onerror = function (event) { + handleError(event); + d.reject("error"); + return d.promise; - } - localNotText = localNotText.substring(0, localNotText.length - 1); + }; - // if we are in background, do a local notification, else do an in app display - if (!NVRDataModel.isBackground()) { - //emit alarm details - this is when received over websockets - $rootScope.$broadcast('alarm', { - message: listOfMonitors - }); + ws.onmessage = function (event) { - if (str.supplementary != 'true') { + var smsg = event.data; + handleMessage(smsg); - NVRDataModel.debug("App is in foreground, displaying banner"); - if (eventsToDisplay.length > 0) { - if (eventsToDisplay.length == 1) { - //console.log("Single Display: " + eventsToDisplay[0]); - NVRDataModel.displayBanner('alarm', [eventsToDisplay[0]], 5000, 5000); - } else { - NVRDataModel.displayBanner('alarm', eventsToDisplay, - 5000, 5000 * eventsToDisplay.length); - } + }; + + return d.promise; + } - } - } - } - } //end of success handler - }); - d.resolve("true"); - return (d.promise); - } function disconnect() { - if (typeof ws === 'undefined') { - NVRDataModel.log("Event server socket is empty, nothing to disconnect"); - return; - } NVRDataModel.log("Clearing error/close cbk, disconnecting and deleting Event Server socket..."); - // ws.$close(); - ws.onErrorCallbacks = []; - ws.onCloseCallbacks = []; - ws.close(true); // force close - // ws.$un('open'); - // ws.$un('close'); - // ws.$un('message'); - ws = undefined; + + if ($rootScope.platforOS == 'desktop'){ + if (typeof ws === 'undefined') { + NVRDataModel.log("Event server socket is empty, nothing to disconnect"); + return; + } + + + ws.onmessage = null; + ws.close(); + ws = undefined; + } + else { + if (nativeWebSocketId != -1) //native; + CordovaWebsocketPlugin.wsClose(nativeWebSocketId, 1000, "Connection closed"); + nativeWebSocketId = -1; + + } + } @@ -348,43 +354,31 @@ angular.module('zmApp.controllers') return; } - if (typeof ws === 'undefined') { + if (typeof ws === 'undefined' && nativeWebSocketId == -1) { NVRDataModel.debug("Event server not initalized, not sending message"); return; } // console.log (">>>>>>>>>>>>>>>>>EVENT SERVER SENDING: type="+type+" DATA="+JSON.stringify(obj)); - ws.send({ + + var msg = { 'event': type, 'data': obj - }); - - /*if (ws.$status() == ws.$CLOSED) - { - NVRDataModel.log("Websocket was closed, trying to re-open"); - ws.$un('$open'); - //ws.$on ('$open', openHandshake); - ws.$open(); - - ws.$on('$open', openHandshake, function() - { + }; - //console.log(" sending " + type + " " + - // JSON.stringify(obj)); - //console.log("sending " + type + " " + JSON.stringify(obj)); - ws.$emit(type, obj); - - ws.$un('$open'); - ws.$on('$open', openHandshake); - - }); + var jmsg = JSON.stringify(msg); + NVRDataModel.debug("~~~~ sendMessage: Sending->" + jmsg); + if ($rootScope.platformOS == 'desktop') { + ws.send(jmsg); } - else* - { - ws.send(type, obj); - // console.log("sending " + type + " " + JSON.stringify(obj)); - }*/ + else { + if (nativeWebSocketId != -1) + CordovaWebsocketPlugin.wsSend(nativeWebSocketId, jmsg); + else + NVRDataModel.debug ("ERROR:native websocket not initialized, can't send "+jmsg); + } + } diff --git a/www/js/EventServerSettingsCtrl.js b/www/js/EventServerSettingsCtrl.js index 24928007..c5857369 100644 --- a/www/js/EventServerSettingsCtrl.js +++ b/www/js/EventServerSettingsCtrl.js @@ -212,7 +212,8 @@ EventServer.sendMessage("control", { type: 'filter', monlist: monstring, - intlist: intervalstring + intlist: intervalstring, + token: $rootScope.apnsToken }, 1); if ($rootScope.apnsToken != "") @@ -231,7 +232,12 @@ } - }); + }, + function (err) { + NVRDataModel.debug("Event Server init failed"); + } + + ); } // no event server configured/enabled else { diff --git a/www/js/EventsGraphsCtrl.js b/www/js/EventsGraphsCtrl.js index bfffb351..cad2cc0b 100644 --- a/www/js/EventsGraphsCtrl.js +++ b/www/js/EventsGraphsCtrl.js @@ -190,12 +190,13 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni // console.log("Monitor event URL:" + url); NVRDataModel.log("EventGraph: composed url is " + url); $http.get(url /*,{timeout:15000}*/ ) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug("Event count for monitor" + monitors[j].Monitor.Id + " is " + data.pagination.count); $scope.chart.data.datasets[0].data[j] = data.pagination.count; - }) - .error(function (data) { + }, + function (data) { // ideally I should be treating it as an error // but what I am really doing now is treating it like no events // works but TBD: make this into a proper error handler diff --git a/www/js/FirstUseCtrl.js b/www/js/FirstUseCtrl.js index 89f45e96..1ad42e9c 100644 --- a/www/js/FirstUseCtrl.js +++ b/www/js/FirstUseCtrl.js @@ -18,8 +18,11 @@ angular.module('zmApp.controllers').controller('zmApp.FirstUseCtrl', ['$scope', // if (window.cordova) { - cordova.plugins.certificates.trustUnsecureCerts(true); - NVRDataModel.log(">>>>>Accepting all certificates, since its first use"); + cordova.plugin.http.setSSLCertMode('nocheck', function() { + NVRDataModel.debug('--> First use -> SSL is permissive, will allow any certs for now. You can change it later.'); + }, function() { + console.log('-->First Use -> Error setting SSL permissive'); + }); } diff --git a/www/js/LoginCtrl.js b/www/js/LoginCtrl.js index 9410b276..d2dfe899 100644 --- a/www/js/LoginCtrl.js +++ b/www/js/LoginCtrl.js @@ -582,6 +582,27 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r if ($rootScope.platformOS != 'desktop') { + if ($scope.loginData.isUseBasicAuth) { + NVRDataModel.debug ("Cordova HTTP: configuring basic auth"); + cordova.plugin.http.useBasicAuth($scope.loginData.basicAuthUser, $scope.loginData.basicAuthPassword); + } + + if (!$scope.loginData.enableStrictSSL) { + + //alert("Enabling insecure SSL"); + NVRDataModel.log(">>>> Disabling strict SSL checking (turn off in Dev Options if you can't connect)"); + cordova.plugin.http.setSSLCertMode('nocheck', function() { + NVRDataModel.debug('--> SSL is permissive, will allow any certs. Use at your own risk.'); + }, function() { + console.log('-->Error setting SSL permissive'); + }); + + } else { + + NVRDataModel.log(">>>> Enabling strict SSL checking (turn off in Dev Options if you can't connect)"); + + } + if ($scope.loginData.saveToCloud) { NVRDataModel.debug ("writing data to cloud"); @@ -628,21 +649,28 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r oldName = $scope.loginData.serverName; if ($scope.loginData.isUseEventServer) { - EventServer.init(); - if ($rootScope.apnsToken && $scope.loginData.disablePush != true) { - NVRDataModel.log("Making sure we get push notifications"); - EventServer.sendMessage('push', { - type: 'token', - platform: $rootScope.platformOS, - token: $rootScope.apnsToken, - state: "enabled" - }, 1); - } - EventServer.sendMessage("control", { - type: 'filter', - monlist: $scope.loginData.eventServerMonitors, - intlist: $scope.loginData.eventServerInterval + EventServer.init() + .then (function (succ) { + if ($rootScope.apnsToken && $scope.loginData.disablePush != true) { + NVRDataModel.log("Making sure we get push notifications"); + EventServer.sendMessage('push', { + type: 'token', + platform: $rootScope.platformOS, + token: $rootScope.apnsToken, + state: "enabled" + }, 1); + } + EventServer.sendMessage("control", { + type: 'filter', + monlist: $scope.loginData.eventServerMonitors, + intlist: $scope.loginData.eventServerInterval, + token: $rootScope.apnsToken + }); + }, + function (err) { + NVRDataModel.log ("Event server init failed"); }); + } @@ -672,8 +700,9 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r NVRDataModel.log("Validating APIs at " + apiurl); $http.get(apiurl) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.getTimeZone(true); var loginStatus = $translate.instant('kExploreEnjoy') + " " + $rootScope.appName + "!"; EventServer.refresh(); @@ -688,7 +717,8 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r NVRDataModel.log("ZM relative cgi-path: " + zm_cgi + ", you entered: " + user_cgi); $http.get(ld.streamingurl + "/zms") - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug("Urk! cgi-path returned success, but it should not have come here"); loginStatus = $translate.instant('kLoginStatusNoCgi'); @@ -717,8 +747,8 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r }); } - }) - .error(function (error, status) { + }, + function (error, status) { // If its 5xx, then the cgi-bin path is valid // if its 4xx then the cgi-bin path is not valid @@ -757,8 +787,8 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r }); }); - }) - .error(function (error) { + }, + function (error) { NVRDataModel.displayBanner('error', [$translate.instant('kBannerAPICheckFailed'), $translate.instant('kBannerPleaseCheck')]); NVRDataModel.log("API login error " + JSON.stringify(error)); diff --git a/www/js/MenuController.js b/www/js/MenuController.js index 995abba3..d9d4d114 100644 --- a/www/js/MenuController.js +++ b/www/js/MenuController.js @@ -2,14 +2,14 @@ /* jslint browser: true*/ /* global cordova,StatusBar,angular,console */ -angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', '$ionicHistory', '$state', 'NVRDataModel', '$rootScope', '$ionicPopup', '$translate', '$timeout', '$location','EventServer', 'zmAutoLogin','$http',function ($scope, $ionicSideMenuDelegate, zm, $stateParams, $ionicHistory, $state, NVRDataModel, $rootScope, $ionicPopup, $translate, $timeout, $location, EventServer, zmAutoLogin, $http) { +angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', '$ionicHistory', '$state', 'NVRDataModel', '$rootScope', '$ionicPopup', '$translate', '$timeout', '$location','EventServer', 'zmAutoLogin','$http','SecuredPopups',function ($scope, $ionicSideMenuDelegate, zm, $stateParams, $ionicHistory, $state, NVRDataModel, $rootScope, $ionicPopup, $translate, $timeout, $location, EventServer, zmAutoLogin, $http, SecuredPopups) { $scope.openMenu = function () { $ionicSideMenuDelegate.toggleLeft(); }; //---------------------------------------------------------------- // This controller sits along with the main app to bring up - // the language menu from the main menu + // the language menu from the main //---------------------------------------------------------------- @@ -83,22 +83,48 @@ angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$io } - if (loginData.isUseEventServer) { - EventServer.init(); - if ($rootScope.apnsToken && loginData.disablePush != true) { - NVRDataModel.log("Making sure we get push notifications"); - EventServer.sendMessage('push', { - type: 'token', - platform: $rootScope.platformOS, - token: $rootScope.apnsToken, - state: "enabled" - }, 1); + + if (window.cordova) { + + if (loginData.isUseBasicAuth) { + NVRDataModel.debug ("Cordova HTTP: configuring basic auth"); + cordova.plugin.http.useBasicAuth(loginData.basicAuthUser, loginData.basicAuthPassword); + } + + if (!loginData.enableStrictSSL) { + + //alert("Enabling insecure SSL"); + NVRDataModel.log(">>>> Disabling strict SSL checking (turn off in Dev Options if you can't connect)"); + cordova.plugin.http.setSSLCertMode('nocheck', function() { + NVRDataModel.debug('--> SSL is permissive, will allow any certs. Use at your own risk.'); + }, function() { + console.log('-->Error setting SSL permissive'); + }); + + } else { + + NVRDataModel.log(">>>> Enabling strict SSL checking (turn off in Dev Options if you can't connect)"); + } - EventServer.sendMessage("control", { - type: 'filter', - monlist: loginData.eventServerMonitors, - intlist: loginData.eventServerInterval + + } + + + if (loginData.isUseEventServer) { + EventServer.init() + .then (function (succ) { + EventServer.sendMessage("control", { + type: 'filter', + monlist: loginData.eventServerMonitors, + intlist: loginData.eventServerInterval, + token: $rootScope.apnsToken + }); + }, + function (err) { + NVRDataModel.debug ("EventServer init failed"); }); + + } @@ -119,8 +145,9 @@ angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$io NVRDataModel.log("Validating APIs at " + apiurl); $http.get(apiurl) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.getTimeZone(true); var loginStatus = $translate.instant('kExploreEnjoy') + " " + $rootScope.appName + "!"; EventServer.refresh(); @@ -135,7 +162,8 @@ angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$io NVRDataModel.log("ZM relative cgi-path: " + zm_cgi + ", you entered: " + user_cgi); $http.get(ld.streamingurl + "/zms") - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug("Urk! cgi-path returned success, but it should not have come here"); loginStatus = $translate.instant('kLoginStatusNoCgi'); @@ -183,8 +211,8 @@ angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$io }); - }) - .error(function (error, status) { + }, + function (error, status) { // If its 5xx, then the cgi-bin path is valid // if its 4xx then the cgi-bin path is not valid @@ -233,8 +261,8 @@ angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$io }); }); - }) - .error(function (error) { + }, + function (error) { NVRDataModel.displayBanner('error', [$translate.instant('kBannerAPICheckFailed'), $translate.instant('kBannerPleaseCheck')]); NVRDataModel.log("API login error " + JSON.stringify(error)); diff --git a/www/js/MonitorCtrl.js b/www/js/MonitorCtrl.js index 38bdf765..881d6a2c 100644 --- a/www/js/MonitorCtrl.js +++ b/www/js/MonitorCtrl.js @@ -181,10 +181,10 @@ angular.module('zmApp.controllers') } }) - .success(function () { + .then(function () { NVRDataModel.debug("MonitorCtrl: Not restarting ZM - Make sure you have the patch installed in MonitorsController.php or this won't work"); - }) - .error(function (data, status, headers, config) { + }, + function (data, status, headers, config) { NVRDataModel.debug("MonitorCtrl: Error changing monitor " + JSON.stringify(data)); $scope.monfunc.myfailedIds.push(item); }); @@ -419,7 +419,8 @@ angular.module('zmApp.controllers') NVRDataModel.debug("MonitorCtrl:monitorStateCheck: " + apiMonCheck); //console.log("**** ZMC CHECK " + apiMonCheck); $http.get(apiMonCheck) - .success(function (data) { + .then(function (data) { + data = data.data; NVRDataModel.debug("MonitorCtrl: monitor check state returned: " + JSON.stringify(data)); if (data.statustext.indexOf("not running") > -1) { $scope.monitors[j].Monitor.isRunning = "false"; @@ -438,8 +439,8 @@ angular.module('zmApp.controllers') } $scope.monitors[j].Monitor.isRunningText = data.statustext; - }) - .error(function (data) { + }, + function (data) { NVRDataModel.debug("MonitorCtrl: Error->monitor check state returned: " + JSON.stringify(data)); NVRDataModel.displayBanner('error', [$translate.instant('kErrorRetrievingState'), $translate.instant('kPleaseTryAgain')]); diff --git a/www/js/MonitorModalCtrl.js b/www/js/MonitorModalCtrl.js index 3d765aed..cc110197 100644 --- a/www/js/MonitorModalCtrl.js +++ b/www/js/MonitorModalCtrl.js @@ -3,7 +3,7 @@ /* jslint browser: true*/ /* global saveAs, cordova,StatusBar,angular,console,ionic, moment, imagesLoaded, chrome */ -angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', 'SecuredPopups', '$translate', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, SecuredPopups, $translate) { +angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', 'SecuredPopups', '$translate', '$cordovaFile', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, SecuredPopups, $translate, $cordovaFile) { $scope.animationInProgress = false; $scope.imageFit = true; @@ -37,7 +37,7 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ /* $ionicLoading.show({ template: $translate.instant('kNegotiatingStreamAuth') + '...', animation: 'fade-in', - showBackdrop: true, + showBackdrop: false, duration: zm.loadingTimeout, maxWidth: 300, showDelay: 0 @@ -853,17 +853,18 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ }); - req.success(function (resp) { + req.then(function (resp) { //console.log("SUCCESS: " + JSON.stringify(resp)); $ionicLoading.hide(); - }); - - req.error(function (resp) { + }, + function (resp) { $ionicLoading.hide(); //console.log("ERROR: " + JSON.stringify(resp)); NVRDataModel.log("Error sending PTZ:" + JSON.stringify(resp), "error"); }); + + } $scope.getZoomLevel = function () { @@ -896,10 +897,20 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ function moveToMonitor(m, d) { - $scope.animationInProgress = true; + if ($scope.isZoneEdit) { NVRDataModel.log("Not cycling, as you are editing zones"); + return; } + + if ($scope.monitors.length <= 1) { + NVRDataModel.log("Not cycling, as you only have at most 1 monitors"); + return; + } + + + + $scope.animationInProgress = true; var curstate = $ionicHistory.currentStateName(); var found = 0; var mid; @@ -1036,9 +1047,9 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ //------------------------------------------------------------- // Turns on or off an alarm forcibly (mode true = on, false = off) //------------------------------------------------------------- - $scope.enableAlarm = function (mid, mode) { + $scope.triggerAlarm = function (mid, mode) { - if (mode) // trigger alarm + if (mode == 'on') // trigger alarm { $rootScope.zmPopup = SecuredPopups.show('show', { title: 'Confirm', @@ -1046,7 +1057,7 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ buttons: [{ text: $translate.instant('kButtonYes'), onTap: function (e) { - enableAlarm(mid, mode); + triggerAlarm(mid, mode); } }, { @@ -1059,11 +1070,11 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ }); } else - enableAlarm(mid, mode); + triggerAlarm(mid, mode); - function enableAlarm(mid, mode) { + function triggerAlarm(mid, mode) { var apiurl = NVRDataModel.getLogin().apiurl; - var c = mode ? "on" : "off"; + var c = mode=='on' ? 'on' : 'off'; var alarmurl = apiurl + "/monitors/alarm/id:" + mid + "/command:" + c + ".json"; NVRDataModel.log("Invoking " + alarmurl); @@ -1081,6 +1092,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ noBackdrop: true, duration: 2000, }); + + }, function (error) { @@ -1089,7 +1102,7 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ noBackdrop: true, duration: 3000, }); - NVRDataModel.debug("Error in enableAlarm " + JSON.stringify(error)); + NVRDataModel.debug("Error in triggerAlarm " + JSON.stringify(error)); }); } @@ -1183,49 +1196,75 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ NVRDataModel.log("SavetoPhone:Trying to save image from " + url); - var img = new Image(); - img.onload = function () { - // console.log("********* ONLOAD"); - canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - context = canvas.getContext('2d'); - context.drawImage(img, 0, 0); - - imageDataUrl = canvas.toDataURL('image/jpeg', 1.0); - imageData = imageDataUrl.replace(/data:image\/jpeg;base64,/, ''); - - if ($rootScope.platformOS != "desktop") { - try { - - cordova.exec( - SaveSuccess, - SaveError, - 'Canvas2ImagePlugin', - 'saveImageDataToLibrary', [imageData] - ); - } catch (e) { - - SaveError(e.message); - } - } else { + if ($rootScope.platformOS != 'desktop') { + var album = 'zmNinja'; + NVRDataModel.debug ("Trying to save image to album: "+album); + cordova.plugins.photoLibrary.requestAuthorization( + function () { + //url = "https://picsum.photos/200/300/?random"; + + var fileTransfer = new FileTransfer(); + var urle = encodeURI(url); + var fname = "zmninja.jpg"; + + fileTransfer.download(urle, cordova.file.dataDirectory + fname, + function(entry){ + NVRDataModel.debug("local download complete: " + entry.toURL()); + NVRDataModel.debug("Now trying to move it to album"); + cordova.plugins.photoLibrary.saveImage(entry.toURL(), album, + function (cameraRollAssetId) { + SaveSuccess(); + $cordovaFile.removeFile(cordova.file.dataDirectory, fname) + .then ( + function () { + NVRDataModel.debug ("file removed from data directory"); + }, + function (e) { + NVRDataModel.debug ("could not delete temp file: "+JSON.stringify(e)); + } + ); + + + }, function (err) { + NVRDataModel.debug ("Saving error:" + JSON.stringify(err)); + SaveError(); + + }); + + }, + function(err) { NVRDataModel.debug ("error downloading:"+JSON.stringify(err));SaveError();}, !loginData.enableStrictSSL, {}); - var fname = $scope.monitorName + "-" + - moment().format('MMM-DD-YY_HH-mm-ss') + ".png"; - canvas.toBlob(function (blob) { - saveAs(blob, fname); - SaveSuccess(); - }); - } - }; - try { - img.src = url; - // console.log ("SAVING IMAGE SOURCE"); - } catch (e) { - SaveError(e.message); + + + // User gave us permission to his library, retry reading it! + }, + function (err) { + // User denied the access + NVRDataModel.debug ("Permission not granted"); + SaveError(); + }, // if options not provided, defaults to {read: true}. + + { + read: true, + write: true + } + ); + } - } + else { + + $ionicLoading.hide(); + + $rootScope.zmPopup = SecuredPopups.show('alert', { + title: $translate.instant('kNote'), + template: $translate.instant('kDownloadVideoImage')+"<br/><br/><center><a href='" + url + "' class='button button-assertive icon ion-android-download' download=\"balls.jpg\">"+" "+$translate.instant('kDownload')+"</a></center>", + okText: $translate.instant('kDismiss'), + okType:'button-stable' + }); + + } + } @@ -1420,14 +1459,16 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ } }); - req.success(function (resp) { + req.then(function (resp) { + resp = resp.data; if (resp.result == "Ok" && ndx != -1) { var ld = NVRDataModel.getLogin(); var apiurl = ld.apiurl + "/events/" + resp.status.event + ".json"; //console.log ("API " + apiurl); $http.get(apiurl) - .success(function (data) { + .then(function (data) { + data = data.data; if ($scope.MontageMonitors[ndx].eventUrlTime != data.event.Event.StartTime) { var element = angular.element(document.getElementById($scope.MontageMonitors[ndx].Monitor.Id + "-timeline")); @@ -1441,19 +1482,21 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ } - }) - .error(function (data) { + }, + function (data) { $scope.MontageMonitors[ndx].eventUrlTime = "-"; }); } - }); + }, - req.error(function (resp) { + function (resp) { //console.log("ERROR: " + JSON.stringify(resp)); NVRDataModel.log("Error sending event command " + JSON.stringify(resp), "error"); }); + + } $scope.toggleListMenu = function () { @@ -1518,7 +1561,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ var ld = NVRDataModel.getLogin(); var url = ld.apiurl + "/monitors/" + mid + ".json"; $http.get(url) - .success(function (data) { + .then(function (data) { + data = data.data; $scope.isControllable = data.monitor.Monitor.Controllable; // *** Only for testing - comment out // @@ -1533,8 +1577,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ NVRDataModel.debug("configurePTZ : getting controllable data " + myurl); $http.get(myurl) - .success(function (data) { - + .then(function (data) { + data = data.data; // *** Only for testing - comment out - start// /*data.Control.Control.CanSleep = '1'; data.Control.Control.CanWake = '1'; @@ -1696,8 +1740,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ } NVRDataModel.log("ConfigurePTZ Modal: ControlDB reports PTZ command to be " + $scope.ptzMoveCommand); - }) - .error(function (data) { + }, + function (data) { // console.log("** Error retrieving move PTZ command"); NVRDataModel.log("ConfigurePTZ : Error retrieving PTZ command " + JSON.stringify(data), "error"); }); @@ -1705,8 +1749,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$ } else { NVRDataModel.log("configurePTZ " + mid + " is not PTZ controllable"); } - }) - .error(function (data) { + }, + function (data) { // console.log("** Error retrieving move PTZ command"); NVRDataModel.log("configurePTZ : Error retrieving PTZ command " + JSON.stringify(data), "error"); }); diff --git a/www/js/MontageCtrl.js b/www/js/MontageCtrl.js index fefe2ddc..f4c77a80 100644 --- a/www/js/MontageCtrl.js +++ b/www/js/MontageCtrl.js @@ -334,7 +334,7 @@ angular.module('zmApp.controllers') progressCalled = true; loadCount++; - console.log ("loaded "+loadCount+" of "+positions.length); + // console.log ("loaded "+loadCount+" of "+positions.length); // if (layouttype) $timeout (function(){layout(pckry);},100); }); @@ -1828,7 +1828,7 @@ angular.module('zmApp.controllers') // NVRDataModel.regenConnKeys(); $scope.monitors = NVRDataModel.getMonitorsNow(); - console.log ("MONITORS:"+JSON.stringify($scope.monitors)); + //console.log ("MONITORS:"+JSON.stringify($scope.monitors)); $scope.MontageMonitors = angular.copy($scope.monitors); diff --git a/www/js/NewsCtrl.js b/www/js/NewsCtrl.js index 6be6aa0e..55712753 100644 --- a/www/js/NewsCtrl.js +++ b/www/js/NewsCtrl.js @@ -88,15 +88,12 @@ angular.module('zmApp.controllers').controller('zmApp.NewsCtrl', ['$scope', '$ro $http.get(zm.blogUrl, { - transformResponse: function (d, h) { - var trunc = "])}while(1);</x>"; - d = d.substr(trunc.length); - return d; - } + responseType:'text' }) - .success(function (datastr) { - - + .then(function (datastr) { + datastr = datastr.data; + var trunc = "])}while(1);</x>"; + datastr= datastr.substr(trunc.length); // console.log ("DATA:"+data); // var data = JSON.parse(datastr); diff --git a/www/js/TimelineCtrl.js b/www/js/TimelineCtrl.js index 250fda9e..a5c7f818 100644 --- a/www/js/TimelineCtrl.js +++ b/www/js/TimelineCtrl.js @@ -1026,7 +1026,7 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla $ionicLoading.show({ template: $translate.instant('kLoadingGraph') + "...", animation: 'fade-in', - showBackdrop: true, + showBackdrop: false, maxWidth: 200, showDelay: 0, duration: zm.loadingTimeout, //specifically for Android - http seems to get stuck at times @@ -1103,9 +1103,9 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla //console.log ("**NOLANG" + fromDateNoLang + " " + toDateNoLang); NVRDataModel.getEventsPages(0, fromDateNoLang, toDateNoLang) - .then(function (data) { - var pages = data.pageCount || 1; - var itemsPerPage = parseInt(data.limit); + .then(function (epData) { + var pages = 1; + var itemsPerPage = parseInt(epData.limit); var iterCount; // So iterCount is the # of HTTP calls I need to make @@ -1117,17 +1117,18 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla // for dynamic binding which was easier, but due to performance reasons // I am waiting for the full data to load before I draw var promises = []; - while ((pages > 0) && (iterCount > 0)) { + while ((pages <= epData.pageCount) && (iterCount > 0)) { var promise = NVRDataModel.getEvents(0, pages, "none", fromDateNoLang, toDateNoLang); promises.push(promise); - pages--; + + pages++; iterCount--; } $q.all(promises) .then(function (data) { - NVRDataModel.debug("TimelineCtrl/drawgraph: all pages of graph data received"); + NVRDataModel.debug("TimelineCtrl/drawgraph: all pages of graph data received " ); graphIndex = 0; NVRDataModel.log("Creating " + $scope.monitors.length + " groups for the graph"); // create groups @@ -1143,8 +1144,10 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla } for (var j = 0; j < data.length; j++) { - var myevents = data[j]; + var myevents = data[j].events; + // console.log ("****************DATA ="+JSON.stringify(data[j])); + // console.log ("**********************************"); if (graphIndex > count) { NVRDataModel.log("Exiting page count graph - reached limit of " + count); break; @@ -1203,10 +1206,12 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla } //console.log ("ADDED "+tzs+" " +tze); + + if (!graphData.get(myevents[i].Event.Id)) { graphData.add({ //id: graphIndex, id: myevents[i].Event.Id, - content: "<span class='my-vis-font'>" + "( <i class='ion-android-notifications'></i>" + myevents[i].Event.AlarmFrames + ") " + "(" + myevents[j].Event.Id + ") " + myevents[i].Event.Notes + "</span>", + content: "<span class='my-vis-font'>" + "( <i class='ion-android-notifications'></i>" + myevents[i].Event.AlarmFrames + ") " + "(" + myevents[i].Event.Id + ") " + myevents[i].Event.Notes + "</span>", start: tzs, //start: myevents[i].Event.StartTime, @@ -1224,7 +1229,9 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla myevent: myevents[i] }); + //console.log ("IED="+myevents[i].Event.Id); graphIndex++; + } } else { //console.log ("SKIPPED GRAPH ID " + graphIndex); } @@ -1375,7 +1382,7 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla var _item; for (var x = 0; x < visible.length; x++) { _item = timeline.itemSet.items[x]; - if (_item.data.group == prop.group) { + if (_item && _item.data && _item.data.group == prop.group) { if (Math.abs(_item.left - prop.x) < minDist) { closestItem = _item; minDist = Math.abs(_item.left - prop.x); @@ -1398,7 +1405,7 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla /*$ionicLoading.show({ template: "", animation: 'fade-in', - showBackdrop: true, + showBackdrop: false, maxWidth: 200, showDelay: 0, duration: 1500, diff --git a/www/js/WizardCtrl.js b/www/js/WizardCtrl.js index 6ce8ea1b..a152f537 100644 --- a/www/js/WizardCtrl.js +++ b/www/js/WizardCtrl.js @@ -13,12 +13,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$ function login(u, zmu, zmp) { var d = $q.defer(); - - - - $http({ - method: 'POST', + method: 'post', //withCredentials: true, url: u, headers: { @@ -31,6 +27,7 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$ str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); var params = str.join("&"); + //console.log ("PARAMS in login:"+params); return params; }, @@ -41,7 +38,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$ view: "console" } }) - .success(function (data, status, headers) { + .then(function (data, status, headers) { + data = data.data; //console.log("LOOKING FOR " + zm.loginScreenString); //console.log("DATA RECEIVED " + JSON.stringify(data)); if (data.indexOf(zm.loginScreenString) == -1) { @@ -58,9 +56,9 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$ d.reject(false); return d.promise; } - }) - .error(function (error) { - //console.log("************ERROR"); + }, + function (error) { + // console.log("************ERROR:"+ JSON.stringify(error)); $scope.wizard.portalValidText = $translate.instant('kPortalDetectionFailed'); $scope.wizard.portalColor = "#e74c3c"; d.reject(false); @@ -197,10 +195,13 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$ var urls = [a1, a2, a3, a4, a5]; - NVRDataModel.getPathZms() // what does ZM have stored in PATH_ZMS? + // can't use getPathZms as loginData is not inited yet + $http.get ($scope.wizard.apiURL+"/configs/viewByName/ZM_PATH_ZMS.json") + //NVRDataModel.getPathZms() // what does ZM have stored in PATH_ZMS? .then(function (data) { // remove zms or nph-zms - var path = data.trim(); + var str = data.data.config.Value; + var path = str.trim(); path = path.replace("/nph-zms", ""); path = path.replace("/zms", ""); urls.push(baseUri.trim() + path); diff --git a/www/js/app.js b/www/js/app.js index 6287e09b..5935ee0d 100755 --- a/www/js/app.js +++ b/www/js/app.js @@ -25,7 +25,7 @@ angular.module('zmApp', [ 'com.2fdevs.videogular', 'com.2fdevs.videogular.plugins.controls', 'com.2fdevs.videogular.plugins.overlayplay', - 'ionic-native-transitions', + //'ionic-native-transitions', 'mgo-angular-wizard', 'pascalprecht.translate', 'uk.ac.soton.ecs.videogular.plugins.cuepoints', @@ -44,7 +44,7 @@ angular.module('zmApp', [ .constant('zm', { minAppVersion: '1.28.107', // if ZM is less than this, the app won't work recommendedAppVersion: '1.32.0', - minEventServerVersion: '1.0', + minEventServerVersion: '2.0', castAppId: 'BA30FB4C', alarmFlashTimer: 20000, // time to flash alarm gcmSenderId: '710936220256', @@ -264,7 +264,6 @@ angular.module('zmApp', [ }) - // credit https://gist.github.com/Zren/beaafd64f395e23f4604 .directive('mouseWheelScroll', function ($timeout) { @@ -322,7 +321,7 @@ angular.module('zmApp', [ //console.log ("requestConfig is " + JSON.stringify(requestConfig)); imageLoadingDataShare.set(1); $http(requestConfig) - .success(function (data) { + .then(function (data) { //console.log ("Inside HTTP after Calling " + requestConfig.url); //console.log ("data got " + JSON.stringify(data)); @@ -763,6 +762,9 @@ angular.module('zmApp', [ NVRDataModel.setLastUpdateCheck(moment().toISOString()); // $localstorage.set("lastUpdateCheck", moment().toISOString()); //console.log ("FULL STRING " + success.data.tag_name); + + // console.log ("^^^^^^^^^^^^^ GOT: " + JSON.stringify(success)); + var res = success.data.tag_name.match("v(.*)"); zmUpdateVersion = res[1]; var currentVersion = NVRDataModel.getAppVersion(); @@ -783,16 +785,16 @@ angular.module('zmApp', [ NVRDataModel.log("Checking for news updates"); $http.get(zm.blogUrl, { - transformResponse: function (d, h) { - var trunc = "])}while(1);</x>"; - if (d) { - d = d.substr(trunc.length); - } - return d; - } + responseType:'text', + transformResponse:undefined }) - .success(function (datastr) { + .then(function (datastr) { + // again, for cordova-http + + datastr = datastr.data; + var trunc = "])}while(1);</x>"; + datastr = datastr.substr(trunc.length); var data = JSON.parse(datastr); $rootScope.newBlogPost = ""; @@ -906,6 +908,30 @@ angular.module('zmApp', [ contentBannerInstance(); }, 2000); NVRDataModel.debug("auth-success broadcast:Successful"); + + + var ld = NVRDataModel.getLogin(); + + // we need AUTH_HASH_LOGIN to be on for WKWebView /mobile + if (ld.isUseAuth && $rootScope.platformOS != 'desktop') { + NVRDataModel.getAuthHashLogin() + .then (function (data) { + + if (data.data && data.data.config.Value != '1') { + $ionicPopup.alert({ + title: $translate.instant('kError'), + template: $translate.instant('kAuthHashDisabled') + }); + } + + }, + function (err) { + console.log ("AUTH HASH ERROR: "+JSON.stringify(conf)); + }); + } + + + }); $rootScope.getProfileName = function () { @@ -1059,10 +1085,11 @@ angular.module('zmApp', [ $http({ - method:'POST', + method:'post', url: loginAPI, timeout:httpDelay, headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + responseType:'text', transformResponse: undefined, transformRequest: function(obj) { var str = []; @@ -1242,7 +1269,7 @@ angular.module('zmApp', [ //NVRDataModel.debug ("*** AUTH LOGIN URL IS " + loginData.url); $http({ - method: 'POST', + method: 'post', timeout: httpDelay, //withCredentials: true, url: loginData.url + '/index.php', @@ -1266,8 +1293,9 @@ angular.module('zmApp', [ view: "console" } }) - .success(function (data, status, headers) { + .then(function (data, status, headers) { // console.log(">>>>>>>>>>>>>> PARALLEL POST SUCCESS"); + data = data.data; $ionicLoading.hide(); // Coming here does not mean success @@ -1324,8 +1352,8 @@ angular.module('zmApp', [ return (d.promise); - }) - .error(function (error, status) { + }, + function (error, status) { // console.log(">>>>>>>>>>>>>> PARALLEL POST ERROR"); $ionicLoading.hide(); @@ -1380,7 +1408,7 @@ angular.module('zmApp', [ // First run in ionic //==================================================================== - .run(function ($ionicPlatform, $ionicPopup, $rootScope, zm, $state, $stateParams, NVRDataModel, $cordovaSplashscreen, $http, $interval, zmAutoLogin, zmCheckUpdates, $fileLogger, $timeout, $ionicHistory, $window, $ionicSideMenuDelegate, EventServer, $ionicContentBanner, $ionicLoading, $ionicNativeTransitions, $translate, $localstorage) { + .run(function ($ionicPlatform, $ionicPopup, $rootScope, zm, $state, $stateParams, NVRDataModel, $cordovaSplashscreen, $http, $interval, zmAutoLogin, zmCheckUpdates, $fileLogger, $timeout, $ionicHistory, $window, $ionicSideMenuDelegate, EventServer, $ionicContentBanner, $ionicLoading, /* $ionicNativeTransitions,*/ $translate, $localstorage) { $ionicPlatform.ready(function () { @@ -1967,9 +1995,9 @@ angular.module('zmApp', [ $rootScope.$state = $state; $rootScope.$stateParams = $stateParams; - if (window.cordova && window.cordova.plugins.Keyboard) { - // console.log("no keyboard"); - // cordova.plugins.Keyboard.disableScroll(true); + if (window.cordova ) { + console.log("------------->Keyboard foonone"); + //window.cordova.plugins.Keyboard.disableScroll(true); } if (window.StatusBar) { // org.apache.cordova.statusbar required @@ -2000,13 +2028,14 @@ angular.module('zmApp', [ // console.log("file logger"); - if (NVRDataModel.getLogin().disableNative) { + /*if (NVRDataModel.getLogin().disableNative) { NVRDataModel.log("Disabling native transitions..."); $ionicNativeTransitions.enable(false); } else { NVRDataModel.log("Enabling native transitions..."); $ionicNativeTransitions.enable(true); - } + }*/ + // At this stage, DataModel.init is not called yet // but I do need to know the language @@ -2230,7 +2259,7 @@ angular.module('zmApp', [ //------------------------------------------------------------------ // My route map connecting menu options to their respective templates and controllers - .config(function ($stateProvider, $urlRouterProvider, $httpProvider, $ionicConfigProvider, $provide, $compileProvider, $ionicNativeTransitionsProvider, $logProvider, $translateProvider) { + .config(function ($stateProvider, $urlRouterProvider, $httpProvider, $ionicConfigProvider, $provide, $compileProvider, /*$ionicNativeTransitionsProvider,*/ $logProvider, $translateProvider) { //$logProvider.debugEnabled(false); //$compileProvider.debugInfoEnabled(false); @@ -2254,8 +2283,111 @@ angular.module('zmApp', [ }; }]); + + // Wraps around $http that switches between browser XHR + // or cordova-advanced-http based on if cordova is available + // credits: + // a) https://www.exratione.com/2013/08/angularjs-wrapping-http-for-fun-and-profit/ + // b) https://gist.github.com/adamreisnz/354364e2a58786e2be71 + + $provide.decorator('$http', ['$delegate', '$q', function($delegate, $q) { + // create function which overrides $http function + var $http = $delegate; + + var wrapper = function () { + var url; + var method; + + url = arguments[0].url; + method = arguments[0].method; + var isOutgoingRequest = /^(http|https):\/\//.test(url); + if (window.cordova && isOutgoingRequest) { + + + var d = $q.defer(); + var options = { + method: method, + data: arguments[0].data, + headers: arguments[0].headers, + timeout: arguments[0].timeout, + responseType: arguments[0].responseType + }; + // console.log ("**** -->"+method+"<-- using native HTTP with:"+encodeURI(url)+" payload:"+JSON.stringify(options)); + cordova.plugin.http.sendRequest(encodeURI(url),options, + function (succ) { + // automatic JSON parse if no responseType: text + // fall back to text if JSON parse fails too + if (options.responseType =='text') { + // don't parse into JSON + d.resolve({"data":succ.data}); + return d.promise; + } + else { + try { + d.resolve({"data":JSON.parse(succ.data)}); + return d.promise; + } + catch (e) { + + //console.log ("*** Native HTTP response: JSON parsing failed for "+url+", returning text"); + d.resolve({"data":succ.data}); + return d.promise; + } + + } + }, + function (err) { + console.log ("*** Inside native HTTP error: "+JSON.stringify(err)); + + d.reject(err); + return d.promise; + }); + return d.promise; + + } + else { // not cordova, so lets go back to default http + // console.log ("**** "+method+" using XHR HTTP for "+url); + return $http.apply($http, arguments); + } + + }; + + // wrap around all HTTP methods + Object.keys($http).filter(function (key) { + return (typeof $http[key] === 'function'); + }).forEach(function (key) { + wrapper[key] = function () { + return $http[key].apply($http, arguments); + }; + }); + // wrap convenience functions + $delegate.get = function (url,config) { + return wrapper(angular.extend(config || {}, { + method: 'get', + url: url + })); + }; + + $delegate.post = function (url,data,config) { + return wrapper(angular.extend(config || {}, { + method: 'post', + url: url, + data:data + })); + }; + + $delegate.delete = function (url,config) { + return wrapper(angular.extend(config || {}, { + method: 'delete', + url: url + })); + }; + + return wrapper; + }]); + // If you do this, Allow Origin can't be * - //$httpProvider.defaults.withCredentials = true; + $httpProvider.defaults.withCredentials = true; $httpProvider.interceptors.push('timeoutHttpIntercept'); $ionicConfigProvider.navBar.alignTitle('center'); //$ionicConfigProvider.backButton.text('').icon('ion-chevron-left'); @@ -2265,10 +2397,11 @@ angular.module('zmApp', [ // so it messes up scrolldelegate zoom and possibly others //$ionicConfigProvider.scrolling.jsScrolling(false); $compileProvider.debugInfoEnabled(false); + $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|cdvphotolibrary):/); - $ionicNativeTransitionsProvider.setDefaultOptions({ + /*$ionicNativeTransitionsProvider.setDefaultOptions({ duration: 250, - }); + }); */ $translateProvider.useStaticFilesLoader({ prefix: 'lang/locale-', diff --git a/www/lang/locale-en.json b/www/lang/locale-en.json index fcdd8ecc..2950c0dd 100644 --- a/www/lang/locale-en.json +++ b/www/lang/locale-en.json @@ -15,6 +15,7 @@ "kApplyingChanges" :"Applying changes. Please wait", "kArrangingImages" :"arranging images", "kAt" :"at", + "kAuthHashDisabled" :" AUTH_HASH_LOGINS needs to be enabled to display images. Please restart ZM afer enabling it.", "kAuthSuccess" :"authentication success", "kAuthenticating" :"authenticating", "kAuthenticatingWebScrape" : "authenticating via web scrape", @@ -75,7 +76,10 @@ "kDiscoveringAPI" :"discovering api", "kDiscoveringCGI" :"discovering cgi", "kDiscoveringPortal" :"discovering portal", + "kDismiss" : "Dismiss", "kDone" :"done", + "kDownload" :"Download", + "kDownloadVideoImage" : "Tap the button below to download. If you are downloading a video, there is no progress indication, so please monitor file size", "kEmailNotConfigured" :"Email not configured", "kEnable24hr" :"enable 24hr time format", "kEnableDebug" :"Enable debug logs", diff --git a/www/templates/events-modal.html b/www/templates/events-modal.html index 598bde23..1f168b69 100644 --- a/www/templates/events-modal.html +++ b/www/templates/events-modal.html @@ -155,11 +155,21 @@ <a href="" ng-click="toggleGapless()"> <i class="ion-ios-loop-strong"></i>-{{loginData.gapless? ('kOn' | translate): ('kOff' | translate)}}</a> </li> - <li ng-if="defaultVideo=='' && isToggleListMenu && (!isSnapShot() || liveFeedMid) "> - <a href="" ng-click="saveEventImageToPhoneWithPerms(false)"> - <i class="ion-ios-camera"></i> + <li ng-if="isToggleListMenu"> + <a href="" ng-click="saveEventImageToPhoneWithPerms(false, 'any')"> + <i class="ion-camera"></i> </a> </li> + + + + <li ng-if="isToggleListMenu && defaultVideo !='' && defaultVideo != undefined"> + <a href="" ng-click="saveEventVideoToPhoneWithPerms()"> + <i class="ion-android-download"></i> + </a> + </li> + + <li ng-if="defaultVideo=='' && isToggleListMenu"> <a href="" ng-click="saveEventImageToPhoneWithPerms(true)"> <i class="ion-android-notifications"></i> diff --git a/www/templates/events.html b/www/templates/events.html index c517966f..79c0ef8f 100644 --- a/www/templates/events.html +++ b/www/templates/events.html @@ -78,12 +78,17 @@ {{humanize(event.Event.Length)}} </span> <br/> + + <!-- No longer needed - direct video download supported now + <button ng-if="gifshotSupported && loginData.enableGIFMP4 " class="button button-small button-clear icon gif-icon" ng-click="permissionsDownload(event)"> </button> <a ng-if="(event.Event.DefaultVideo!='' && event.Event.DefaultVideo!==undefined) && $root.platformOS=='desktop' && loginData.enableGIFMP4 " class="button button-clear button-small icon mp4-icon" href="{{event.Event.videoPath}}" download="{{event.Event.Id}}-video.mp4" ng-click="mp4warning()"></a> + --> + <button ng-if="event.Event.DefaultVideo!='' && event.Event.DefaultVideo!=undefined && $root.platformOS!='desktop' && loginData.enableGIFMP4 " class="button button-small button-clear icon mp4-icon" ng-click="downloadFileToDevice(event.Event.videoPath, event.Event.Id)"> @@ -95,8 +100,14 @@ <div align="right" class="col col-40" ng-if="loginData.enableThumbs" > - <img ng-image-appear no-loader transition-duration="0.3s" animation="fillIn" bg-color="#6C7A89" ng-src="{{constructThumbnail(event)}}" - on-tap="closeIfOpen(event);openModalWithSnapshot(event)" width="{{event.Event.thumbWidth}}px" height="{{event.Event.thumbHeight}}px" /> + + <img bg-color="#6C7A89" src="{{constructThumbnail(event)}}" + on-tap="closeIfOpen(event);openModalWithSnapshot(event)" width="{{event.Event.thumbWidth}}px" height="{{event.Event.thumbHeight}}px" /> + <!-- + <img ng-image-appear no-loader transition-duration="0.3s" animation="fillIn" bg-color="#6C7A89" ng-src="{{constructThumbnail(event)}}" + on-tap="closeIfOpen(event);openModalWithSnapshot(event)" width="{{event.Event.thumbWidth}}px" height="{{event.Event.thumbHeight}}px" /> + --> + <!--<p>{{event.Event.thumbWidth}}px*{{event.Event.thumbHeight}}px</p>--> diff --git a/www/templates/monitors-modal.html b/www/templates/monitors-modal.html index 980e2384..0b6ec69c 100644 --- a/www/templates/monitors-modal.html +++ b/www/templates/monitors-modal.html @@ -148,12 +148,12 @@ </a> </li> <li ng-if="isToggleListMenu"> - <a href="" ng-click="enableAlarm(monitorId,true)"> + <a href="" ng-click="triggerAlarm(monitorId,'on')"> <i class="icon ion-flash"></i> </a> </li> <li ng-if="isToggleListMenu"> - <a href="" ng-click="enableAlarm(monitorId,false)"> + <a href="" ng-click="triggerAlarm(monitorId,'off')"> <i class="icon ion-flash-off"></i> </a> </li> |
