├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── ionic-issue-bot.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .pre-commit-config.yaml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs
└── interfaces
│ ├── callbackfunction.md
│ ├── checkforupdateresponse.md
│ ├── iappinfo.md
│ ├── icurrentconfig.md
│ ├── ideployconfig.md
│ ├── ideploypluginapi.md
│ ├── ipluginbaseapi.md
│ ├── isnapshotinfo.md
│ ├── isyncoptions.md
│ └── window.md
├── hooks
└── beforePrepare.js
├── package.json
├── plugin.xml
├── scripts
├── apply-changes.sh
├── create-local-app.sh
├── docs.sh
├── ng-prepare.sh
└── update-plugin-version-code.sh
├── src
├── android
│ ├── IonicCordovaCommon.java
│ └── cordovapluginionic.gradle
├── browser
│ └── IonicCordovaCommon.js
└── ios
│ ├── IonicCordovaCommon.h
│ └── IonicCordovaCommon.m
├── tests
└── test_common.ts
├── tsconfig.json
├── tsconfig.ng.json
├── tslint.json
└── www
├── IonicCordova.ts
├── common.ts
├── definitions.ts
├── guards.ts
├── index.ts
└── ngx
└── index.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | end_of_line = lf
3 | insert_final_newline = true
4 |
5 | [*.{js,jsx,ts,tsx,json,scss}]
6 | indent_style = space
7 | indent_size = 2
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **cordova plugin ls output:**
8 | ```
9 | cordova plugin ls output here
10 | ```
11 |
12 | **ionic info output:**
13 | ```
14 | ionic info output here
15 | ```
16 |
17 | **Describe the bug**
18 | A clear and concise description of what the bug is.
19 |
20 | **To Reproduce**
21 | Steps to reproduce the behavior:
22 | 1. Go to '...'
23 | 2. Click on '....'
24 | 3. Scroll down to '....'
25 | 4. See error
26 |
27 | **Expected behavior**
28 | A clear and concise description of what you expected to happen.
29 |
30 | **Screenshots**
31 | If applicable, add screenshots to help explain your problem.
32 |
33 | **Additional context**
34 | Add any other context about the problem here.
35 |
--------------------------------------------------------------------------------
/.github/ionic-issue-bot.yml:
--------------------------------------------------------------------------------
1 | triage:
2 | label: triage
3 | dryRun: false
4 |
5 | closeAndLock:
6 | labels:
7 | - label: "ionitron: support"
8 | message: >
9 | Thanks for the issue! This issue appears to be a support request. We
10 | use this issue tracker exclusively for bug reports and feature
11 | requests. For support questions, please see our [Support
12 | Page](https://ionicframework.com/support).
13 | Thank you for using Ionic!
14 | - label: "ionitron: pro"
15 | message: >
16 | Thanks for the issue! This issue appears to be related to our
17 | commercial products. We use this issue tracker exclusively for bug
18 | reports and feature requests. Please [open a support
19 | ticket](https://ionicframework.com/support/request) and we'll be happy
20 | to assist you.
21 | Thank you for using Ionic!
22 | - label: "ionitron: missing template"
23 | message: >
24 | Thanks for the issue! It appears that you have not filled out the
25 | provided issue template. We use this issue template in order to gather
26 | more information and further assist you. Please create a new issue and
27 | ensure the template is fully filled out.
28 | Thank you for using Ionic!
29 | - label: "ionitron: old major version"
30 | message: >
31 | Thanks for the issue! This issue appears to be associated with an old
32 | version of the cordova-plugin-ionic. Please update to the latest version.
33 | If the issue is relevant and if it persists after updating to the latest
34 | version, please create a new issue. Thank you for using Ionic!
35 |
36 | close: true
37 | lock: true
38 | dryRun: false
39 |
40 | stale:
41 | days: 180
42 | maxIssuesPerRun: 100
43 | exemptLabels:
44 | - triage
45 | exemptProjects: true
46 | exemptMilestones: true
47 | label: "ionitron: stale issue"
48 | message: >
49 | Thanks for the issue! This issue is being closed due to inactivity. If this
50 | is still an issue with the latest version of Ionic, please create a new
51 | issue and ensure the template is fully filled out.
52 |
53 |
54 | Thank you for using Ionic!
55 | close: true
56 | lock: true
57 | dryRun: false
58 |
59 | wrongRepo:
60 | repos:
61 | - label: "ionitron: framework"
62 | repo: ionic
63 | message: >
64 | Thanks for the issue! We use this issue tracker exclusively for bug
65 | reports and feature requests for cordova-plugin-ionic. It appears that this
66 | issue is associated with the Ionic Framework. I am moving this issue to
67 | the Ionic Framework repository. Please track this issue over there.
68 | Thank you for using Ionic!
69 | - label: "ionitron: capacitor"
70 | repo: capacitor
71 | message: >
72 | Thanks for the issue! We use this issue tracker exclusively for bug
73 | reports and feature requests for cordova-plugin-ionic. It appears that this
74 | issue is associated with Capacitor. I am moving this issue to the
75 | Capacitor repository. Please track this issue over there.
76 | Thank you for using Ionic!
77 | - label: "ionitron: ionic-native"
78 | repo: ionic-native
79 | message: >
80 | reports and feature requests for the Ionic CLI. It appears that this
81 | reports and feature requests for cordova-plugin-ionic. It appears that this
82 | issue is associated with Ionic Native. I am moving this issue to the
83 | Ionic Native repository. Please track this issue over there.
84 | Thank you for using Ionic!
85 | - label: "ionitron: ionic-site"
86 | repo: ionic-site
87 | message: >
88 | Thanks for the issue! We use this issue tracker exclusively for bug
89 | reports and feature requests for cordova-plugin-ionic. It appears that this
90 | issue is associated with the Ionic website. I am moving this issue to
91 | the Ionic website repository. Please track this issue over there.
92 | Thank you for using Ionic!
93 | close: true
94 | lock: true
95 | dryRun: false
96 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | dist/
4 | tmp/
5 | .idea/
6 | package-lock.json
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | tmp/
4 | scripts/
5 | apply-changes.sh
6 | create-local-app.sh
7 | bump-versions.sh
8 | docs/
9 | tests/
10 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | - repo: git://github.com/awebdeveloper/pre-commit-tslint/
2 | sha: 'v0.0.2' # Use the sha or tag you want to point at
3 | hooks:
4 | - id: tslint
5 | exclude: 'tests'
6 | additional_dependencies: ['tslint@5.9.1', 'tslint-ionic-rules@0.0.14', 'typescript@2.6.2']
7 | args: ['--config', 'tslint.json', '--project', 'tsconfig.json', '--fix']
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Changelog
2 | ======
3 | ## 5.5.3
4 | * Fixed iOS "no space left on device"
5 |
6 | ## 5.5.2
7 | * Fixed iOS error 28 – no space left on device
8 |
9 | ## 5.5.1
10 | * Updated iOS live update logic to properly handle non-200 status code responses from the update server.
11 |
12 | ## 5.5.0
13 | * Removed no longer used cordova-plugin-whitelist dependency
14 | * Updated gradle dependencies to use implementation rather than compile
15 |
16 | ## 5.4.8
17 | * Fixed background update mode to handle a scenario in which a live update for an older binary is applied to a new binary
18 |
19 | ## 5.4.7
20 | * fix(ng): Angular Ivy support ([#236](https://github.com/ionic-team/cordova-plugin-ionic/pull/236))
21 |
22 | ## 5.4.6
23 | * fix(ios): create intermediate directories for downloads ([#231](https://github.com/ionic-team/cordova-plugin-ionic/pull/231))
24 |
25 | ## 5.4.5
26 | * fix(android): make sure parent folders exist on file creation ([#226](https://github.com/ionic-team/cordova-plugin-ionic/pull/226))
27 |
28 | ## 5.4.4
29 | * Fix issue where too many network requests at once could fire and cause performance issues.
30 |
31 | ## 5.4.3
32 | * Fix issue where types caused incompatability with Ionic v3 (Typescript 2.x)
33 |
34 | ## 5.4.0
35 | * Remove cordova-plugin-file as dependency to fix ([#213](https://github.com/ionic-team/cordova-plugin-ionic/issues/213))
36 |
37 | ## 5.3.1
38 | * Add cordova-plugin-whitelist dependency ([#215](https://github.com/ionic-team/cordova-plugin-ionic/pull/215))
39 |
40 | ## 5.3.0
41 | * Added an 'incompatibleUpdateAvailable' property to the 'CheckForUpdateResponse' ([#204](https://github.com/ionic-team/cordova-plugin-ionic/pull/204))
42 | * 'ConfigurationInfo' now contains the 'BuildId' in addition to the 'SnapshotId' ([#204](https://github.com/ionic-team/cordova-plugin-ionic/pull/204))
43 |
44 | ## 5.2.9
45 | * Get dataDirectory from getAppInfo function ([#197](https://github.com/ionic-team/cordova-plugin-ionic/pull/197))
46 | * Add proxy for browser platform to support it ([#199](https://github.com/ionic-team/cordova-plugin-ionic/pull/199))
47 |
48 | ## 5.2.8
49 | * Fix Type Error in IDeployConfig ([#196](https://github.com/ionic-team/cordova-plugin-ionic/pull/196))
50 |
51 | ## 5.2.7
52 | * Change hook to run before_prepare and make it async ([#178](https://github.com/ionic-team/cordova-plugin-ionic/pull/178))
53 | * Fixed bug where the a new binary update would load an older cached version of the app ([#179)](https://github.com/ionic-team/cordova-plugin-ionic/issues/179))
54 |
55 | ## 5.2.6
56 | * Check for Capacitor and switch folder ([#164](https://github.com/ionic-team/cordova-plugin-ionic/pull/164))
57 | * Remove unused import ([#163](https://github.com/ionic-team/cordova-plugin-ionic/pull/163))
58 | * Delay device ready until pro checks are done ([#161](https://github.com/ionic-team/cordova-plugin-ionic/pull/161))
59 |
60 | ## 5.2.5
61 | * Fix bug where binaryVersionName and binaryVersionCode are not returned from getConfiguation call
62 | * Fix bug where downloadUpdate progress call back would go from 0 to 50 rather than 100 ([#156](https://github.com/ionic-team/cordova-plugin-ionic/pull/156]))
63 | * Check if the device is online before checking for updates ([#154](https://github.com/ionic-team/cordova-plugin-ionic/pull/154))
64 |
65 | ## 5.2.4
66 | * update check device resp to be accurate ([#148](https://github.com/ionic-team/cordova-plugin-ionic/pull/148))
67 |
68 | ## 5.2.3
69 |
70 | * Fixed bug with AndroidManifest.xml syntax for real since our release script kept breaking it
71 |
72 | ## 5.2.2
73 |
74 | * Fixed bug with AndroidManifest.xml syntax
75 |
76 | ## 5.2.1
77 |
78 | * Add ACCESS_NETWORK_STATE permission to make navigator.onLine work on android
79 |
80 | ## 5.2.0
81 |
82 | * Added `DisableDeploy` Cordova preference allowing disabling of the plugin
83 | * Requires `cordova-plugin-ionic-webview@^2.1.4` for `DisableDeploy` support to work correctly
84 |
85 | ## 5.1.6
86 |
87 | * Fixed a bug with none update method strategy that could cause background updates upon resume of the app from background
88 |
89 | ## 5.0.6
90 |
91 | * Fixed a bug with version rebulds that could make some initial redirects take up to 15 seconds.
92 |
93 | ## 5.0.5
94 |
95 | * Rebuild a deploy directory in the case where the binary version has changed since the update was downloaded.
96 |
97 | # 5.0.0
98 |
99 | * Release!
100 | * Misc. bugfixes from rc3
101 |
102 | ## 5.0.0-rc.3
103 |
104 | * Improved dev tools
105 |
106 | ## 5.0.0-rc.2
107 |
108 | * Disable certain features if browser `fetch` is unavailable
109 | * Update some API methods for coherent returns
110 |
111 | ## 5.0.0-rc.1
112 |
113 | * Removed the switch statement in Android Native code to support older Java platforms
114 |
115 | ## 5.0.0-rc.0
116 |
117 | * Removed the deprecated API, to be added to version 4.2.0
118 |
119 | ## 5.0.0-alpha.0
120 |
121 | * Rewrote the plugin in Typescript.
122 | * Added support for application file manifests.
123 | * Added full support for partial update downloads, greatly decreasing network bandwidth.
124 | * **Deprecated old plugin API** in favor of modern promise-based API using async/await. Existing methods are still available, but may be removed in the future.
125 |
126 | ## 4.1.7
127 |
128 | * Fix a redirect bug in iOS that would give the `background` update method inconsistent behavior
129 |
130 | ## 4.1.6
131 |
132 | * Fix redirect bug in extract when version already exists (PR #82)
133 |
134 | ## 4.1.5
135 |
136 | * Fix UUID storage bug on iOS (PR #79)
137 |
138 | ## 4.1.4
139 |
140 | * Fix `checkAndApply` bug on Android (PR #77)
141 |
142 | ## 4.1.3
143 |
144 | * Fix broken release (4.1.2)
145 |
146 | ## 4.1.2
147 |
148 | * Handle `partial` flag from Pro API in `check-device` endpoint.
149 |
150 | ## 4.1.1
151 |
152 | * Send plugin version to Ionic Pro when checking for updates.
153 |
154 | ## 4.1.0
155 |
156 | * Added support for partial downloads.
157 |
158 | ## 4.0.1
159 |
160 | * Fixed a bug where `deleteVersion` would errorwhen called. (PR #63)
161 |
162 | # 4.0.0
163 |
164 | * Removed some extraneous plugin result calls.
165 | * **BREAKING** Unified all API functions to return `true` on success.
166 |
167 | ## 3.1.3
168 |
169 | * Fixed a bug where `ng-cordova` could potentially be overwritten when a deploy is applied.
170 | * Update no-zip branch
171 |
172 | ## 3.1.2
173 |
174 | * Fixed the extract callback value
175 | * Fixed a bug where the splashscreen would show for long periods while using the `background` update method on Android
176 |
177 | ## 3.1.1
178 |
179 | * Fixed another issue with the cordova.js regex.
180 |
181 | ## 3.1.0
182 |
183 | * Added a `WARN_DEBUG` flag to allow bypass of the debug dialog. (PR $49)
184 | * Fixed a bug where minified script tags could be overwritten.
185 | * Fixed a bug where redirect could error incorrectly on Android.
186 |
187 | # 3.0.0
188 |
189 | * Updated Cordova Splashscreen dependency (PR #41)
190 | * Fixed the callback responses from the `download` and `extract` functions to reflect the docs.
191 | * Store updates to plugin config make via the `init` methods in preferences.
192 |
193 | ## 2.0.4
194 |
195 | * Added a supported platforms note (PR #33)
196 | * Added correct callback calld for initialize and redirect methods (PR #20)
197 |
198 | ## 2.0.3
199 |
200 | * Fixed a bug where the splashscreen could hang in some cases when dismissing the debug dialog within the automatic update methods on Android.
201 |
202 | ## 2.0.2
203 |
204 | * Fixed a bug where the splashscreen could hang in some cases when using the `background` update method on Android.
205 |
206 | ## 2.0.1
207 |
208 | * Fixed a bug with the splashscreen dependency definition
209 |
210 | # 2.0.0
211 |
212 | * **BREAKING** Refactored the deploy plugin API to take a config object at `init`, but no longer needs app ID's/channels in individual calls.
213 | * Fixed a bug where the splashscreen was hiding before the deploy had finished on iOS.
214 | * Fixed an iOS bug where redirects were failing as a result of a regex comparison.
215 | * Fixed `auto` update method in Android to properly show the splash screen.
216 | * Streamlined the way debug builds are handled. The plugin will now ask before each redirect away from the bundled version, allowing easier local development.
217 |
218 | ## 1.1.9
219 |
220 | * Track channel.
221 | * Add ability to clear debug dialog.
222 |
223 | ## 1.1.8
224 |
225 | * Hooked up the `MAX_STORE` variable.
226 |
227 | ## 1.1.7
228 |
229 | * When the app is a `DEBUG` build, the deploy feature will show a prompt and ask whether to apply updates
230 |
231 | ## 1.1.6
232 |
233 | * Fixed a bug with `auto` mode when versions were already present.
234 | * Added `MAX_STORE` flag for future use.
235 | * Changed default behavior to `background` downloads.
236 |
237 | ## 1.1.5
238 |
239 | * Added this changelog and updated the README
240 |
241 | ## 1.1.4
242 |
243 | * Added background download flags and changed `AUTO_UPDATE` config to `UPDATE_METHOD`
244 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2016-present Drifty Co.
2 | http://drifty.com/
3 |
4 | MIT License
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the
8 | "Software"), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Ionic Live Update SDK
2 | ======
3 |
4 | ## Documentation
5 |
6 | Documentation can be found [here](https://ionicframework.com/docs/appflow/deploy/intro)
7 |
8 | ## Support
9 |
10 | If you need support please use our [customer support portal](https://ionic.zendesk.com/hc/en-us).
11 |
--------------------------------------------------------------------------------
/docs/interfaces/callbackfunction.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: CallbackFunction
4 |
5 | A callback function to handle the result.
6 |
7 | ## Type parameters
8 | #### T
9 | ## Hierarchy
10 |
11 | **CallbackFunction**
12 |
13 | ## Callable
14 | ▸ **__call**(result?: *[T]()*): `void`
15 |
16 | A callback function to handle the result.
17 |
18 | **Parameters:**
19 |
20 | | Name | Type |
21 | | ------ | ------ |
22 | | `Optional` result | [T]() |
23 |
24 | **Returns:** `void`
25 |
26 | ## Index
27 |
28 | ---
29 |
30 |
--------------------------------------------------------------------------------
/docs/interfaces/checkforupdateresponse.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: CheckForUpdateResponse
4 |
5 | The response object describing if an update is available.
6 |
7 | ## Hierarchy
8 |
9 | **CheckForUpdateResponse**
10 |
11 | ## Index
12 |
13 | ### Properties
14 |
15 | * [available](checkforupdateresponse.md#available)
16 | * [build](checkforupdateresponse.md#build)
17 | * [compatible](checkforupdateresponse.md#compatible)
18 | * [incompatibleUpdateAvailable](checkforupdateresponse.md#incompatibleupdateavailable)
19 | * [partial](checkforupdateresponse.md#partial)
20 | * [snapshot](checkforupdateresponse.md#snapshot)
21 | * [url](checkforupdateresponse.md#url)
22 |
23 | ---
24 |
25 | ## Properties
26 |
27 |
28 |
29 | ### available
30 |
31 | **● available**: *`boolean`*
32 |
33 | Whether or not an update is available.
34 |
35 | ___
36 |
37 |
38 | ### `` build
39 |
40 | **● build**: *`undefined` \| `string`*
41 |
42 | The id of the build if available.
43 |
44 | ___
45 |
46 |
47 | ### compatible
48 |
49 | **● compatible**: *`boolean`*
50 |
51 | Equivalent to available since v5 this can be ignored in favor of available
52 | *__deprecated__*:
53 |
54 | ___
55 |
56 |
57 | ### `` incompatibleUpdateAvailable
58 |
59 | **● incompatibleUpdateAvailable**: *`undefined` \| `true` \| `false`*
60 |
61 | Whether or not there is an update available that is not compatible with this device.
62 |
63 | ___
64 |
65 |
66 | ### partial
67 |
68 | **● partial**: *`false`*
69 |
70 | Legacy indicator of whether the update is a partial one. This will always be false and can be ignored
71 | *__deprecated__*:
72 |
73 | ___
74 |
75 |
76 | ### `` snapshot
77 |
78 | **● snapshot**: *`undefined` \| `string`*
79 |
80 | The id of the snapshot if available.
81 |
82 | ___
83 |
84 |
85 | ### `` url
86 |
87 | **● url**: *`undefined` \| `string`*
88 |
89 | The url to fetch the manifest of files in the update.
90 |
91 | ___
92 |
93 |
--------------------------------------------------------------------------------
/docs/interfaces/iappinfo.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: IAppInfo
4 |
5 | Information about the application.
6 |
7 | ## Hierarchy
8 |
9 | **IAppInfo**
10 |
11 | ## Index
12 |
13 | ### Properties
14 |
15 | * [binaryVersionCode](iappinfo.md#binaryversioncode)
16 | * [binaryVersionName](iappinfo.md#binaryversionname)
17 | * [bundleName](iappinfo.md#bundlename)
18 | * [bundleVersion](iappinfo.md#bundleversion)
19 | * [dataDirectory](iappinfo.md#datadirectory)
20 | * [device](iappinfo.md#device)
21 | * [platform](iappinfo.md#platform)
22 | * [platformVersion](iappinfo.md#platformversion)
23 | * [version](iappinfo.md#version)
24 |
25 | ---
26 |
27 | ## Properties
28 |
29 |
30 |
31 | ### binaryVersionCode
32 |
33 | **● binaryVersionCode**: *`string` \| `number`*
34 |
35 | The versionCode on Android or CFBundleVersion on iOS this should be changed every time you do a new build debug or otherwise.
36 |
37 | ___
38 |
39 |
40 | ### binaryVersionName
41 |
42 | **● binaryVersionName**: *`string`*
43 |
44 | The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
45 |
46 | ___
47 |
48 |
49 | ### bundleName
50 |
51 | **● bundleName**: *`string`*
52 |
53 | The bundle name.
54 |
55 | ___
56 |
57 |
58 | ### bundleVersion
59 |
60 | **● bundleVersion**: *`string`*
61 |
62 | *__deprecated__*: The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
63 |
64 | ___
65 |
66 |
67 | ### dataDirectory
68 |
69 | **● dataDirectory**: *`string`*
70 |
71 | Directory where the snapshots are stored
72 |
73 | ___
74 |
75 |
76 | ### device
77 |
78 | **● device**: *`string`*
79 |
80 | A generated device ID (NOT a native device ID)
81 |
82 | ___
83 |
84 |
85 | ### platform
86 |
87 | **● platform**: *"ios" \| "android"*
88 |
89 | The platform that the app is currently installed on.
90 |
91 | ___
92 |
93 |
94 | ### platformVersion
95 |
96 | **● platformVersion**: *`string`*
97 |
98 | The version of the native platform.
99 |
100 | ___
101 |
102 |
103 | ### version
104 |
105 | **● version**: *`string`*
106 |
107 | *__deprecated__*: The versionCode on Android or CFBundleVersion on iOS this should be changed every time you do a new build debug or otherwise.
108 |
109 | ___
110 |
111 |
--------------------------------------------------------------------------------
/docs/interfaces/icurrentconfig.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: ICurrentConfig
4 |
5 | The current configuration for the deploy plugin on the device.
6 |
7 | ## Hierarchy
8 |
9 | **ICurrentConfig**
10 |
11 | ## Index
12 |
13 | ### Properties
14 |
15 | * [appId](icurrentconfig.md#appid)
16 | * [binaryVersion](icurrentconfig.md#binaryversion)
17 | * [binaryVersionCode](icurrentconfig.md#binaryversioncode)
18 | * [binaryVersionName](icurrentconfig.md#binaryversionname)
19 | * [channel](icurrentconfig.md#channel)
20 | * [currentBuildId](icurrentconfig.md#currentbuildid)
21 | * [currentVersionId](icurrentconfig.md#currentversionid)
22 | * [disabled](icurrentconfig.md#disabled)
23 | * [host](icurrentconfig.md#host)
24 | * [maxVersions](icurrentconfig.md#maxversions)
25 | * [minBackgroundDuration](icurrentconfig.md#minbackgroundduration)
26 | * [updateMethod](icurrentconfig.md#updatemethod)
27 |
28 | ---
29 |
30 | ## Properties
31 |
32 |
33 |
34 | ### appId
35 |
36 | **● appId**: *`string`*
37 |
38 | The [Ionic Pro](https://ionicframework.com/docs/pro/) app id.
39 |
40 | ___
41 |
42 |
43 | ### binaryVersion
44 |
45 | **● binaryVersion**: *`string`*
46 |
47 | *__deprecated__*: The binary version of the native bundle versionName on Android or CFBundleShortVersionString on iOS deprecated in favor of versionName
48 |
49 | ___
50 |
51 |
52 | ### binaryVersionCode
53 |
54 | **● binaryVersionCode**: *`string`*
55 |
56 | The build version code of the native bundle versionCode on Android or CFBundleVersion on iOS
57 |
58 | ___
59 |
60 |
61 | ### binaryVersionName
62 |
63 | **● binaryVersionName**: *`string`*
64 |
65 | The binary version of the native bundle versionName on Android or CFBundleShortVersionString on iOS
66 |
67 | ___
68 |
69 |
70 | ### channel
71 |
72 | **● channel**: *`string`*
73 |
74 | The [channel](https://ionicframework.com/docs/pro/deploy/channels) that the plugin should listen for updates on.
75 |
76 | ___
77 |
78 |
79 | ### `` currentBuildId
80 |
81 | **● currentBuildId**: *`undefined` \| `string`*
82 |
83 | The id of the currently applied build or undefined if none is applied.
84 |
85 | ___
86 |
87 |
88 | ### `` currentVersionId
89 |
90 | **● currentVersionId**: *`undefined` \| `string`*
91 |
92 | The id of the currently applied updated or undefined if none is applied.
93 |
94 | ___
95 |
96 |
97 | ### disabled
98 |
99 | **● disabled**: *`boolean`*
100 |
101 | Whether the user disabled deploy updates or not.
102 |
103 | ___
104 |
105 |
106 | ### host
107 |
108 | **● host**: *`string`*
109 |
110 | The host API the plugin is configured to check for updates from.
111 |
112 | ___
113 |
114 |
115 | ### maxVersions
116 |
117 | **● maxVersions**: *`number`*
118 |
119 | The maximum number of updates to be stored locally on the device.
120 |
121 | ___
122 |
123 |
124 | ### minBackgroundDuration
125 |
126 | **● minBackgroundDuration**: *`number`*
127 |
128 | The number of seconds the app needs to be in the background before the plugin considers it closed for the purposes of fetching and applying a new update.
129 |
130 | ___
131 |
132 |
133 | ### updateMethod
134 |
135 | **● updateMethod**: *"none" \| "auto" \| "background"*
136 |
137 | The currently configured updateMethod for the plugin.
138 |
139 | ___
140 |
141 |
--------------------------------------------------------------------------------
/docs/interfaces/ideployconfig.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: IDeployConfig
4 |
5 | The configuration for the deploy plugin on the device.
6 |
7 | ## Hierarchy
8 |
9 | **IDeployConfig**
10 |
11 | ## Index
12 |
13 | ### Properties
14 |
15 | * [appId](ideployconfig.md#appid)
16 | * [channel](ideployconfig.md#channel)
17 | * [debug](ideployconfig.md#debug)
18 | * [host](ideployconfig.md#host)
19 | * [maxVersions](ideployconfig.md#maxversions)
20 | * [minBackgroundDuration](ideployconfig.md#minbackgroundduration)
21 | * [updateMethod](ideployconfig.md#updatemethod)
22 |
23 | ---
24 |
25 | ## Properties
26 |
27 |
28 |
29 | ### `` appId
30 |
31 | **● appId**: *`undefined` \| `string`*
32 |
33 | The [Ionic Pro](https://ionicframework.com/docs/pro/) app id.
34 |
35 | ___
36 |
37 |
38 | ### `` channel
39 |
40 | **● channel**: *`undefined` \| `string`*
41 |
42 | The [channel](https://ionicframework.com/docs/pro/deploy/channels) that the plugin should listen for updates on.
43 |
44 | ___
45 |
46 |
47 | ### `` debug
48 |
49 | **● debug**: *`undefined` \| `true` \| `false`*
50 |
51 | whether or not the app should in debug mode
52 |
53 | ___
54 |
55 |
56 | ### `` host
57 |
58 | **● host**: *`undefined` \| `string`*
59 |
60 | *__ignore__*:
61 |
62 | ___
63 |
64 |
65 | ### `` maxVersions
66 |
67 | **● maxVersions**: *`undefined` \| `number`*
68 |
69 | The number of previous updates to be cached on the device
70 |
71 | ___
72 |
73 |
74 | ### `` minBackgroundDuration
75 |
76 | **● minBackgroundDuration**: *`undefined` \| `number`*
77 |
78 | The number of seconds the app should be in the background for before the plugin considers it closed and checks for an updated on resume of the app.
79 |
80 | ___
81 |
82 |
83 | ### `` updateMethod
84 |
85 | **● updateMethod**: *"none" \| "auto" \| "background"*
86 |
87 | The update method the app should use when checking for available updates
88 |
89 | ___
90 |
91 |
--------------------------------------------------------------------------------
/docs/interfaces/ideploypluginapi.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: IDeployPluginAPI
4 |
5 | The Public API for the deploy Plugin
6 |
7 | ## Hierarchy
8 |
9 | **IDeployPluginAPI**
10 |
11 | ## Index
12 |
13 | ### Methods
14 |
15 | * [checkForUpdate](ideploypluginapi.md#checkforupdate)
16 | * [configure](ideploypluginapi.md#configure)
17 | * [deleteVersionById](ideploypluginapi.md#deleteversionbyid)
18 | * [downloadUpdate](ideploypluginapi.md#downloadupdate)
19 | * [extractUpdate](ideploypluginapi.md#extractupdate)
20 | * [getAvailableVersions](ideploypluginapi.md#getavailableversions)
21 | * [getConfiguration](ideploypluginapi.md#getconfiguration)
22 | * [getCurrentVersion](ideploypluginapi.md#getcurrentversion)
23 | * [reloadApp](ideploypluginapi.md#reloadapp)
24 | * [sync](ideploypluginapi.md#sync)
25 |
26 | ---
27 |
28 | ## Methods
29 |
30 |
31 |
32 | ### checkForUpdate
33 |
34 | ▸ **checkForUpdate**(): `Promise`<[CheckForUpdateResponse](checkforupdateresponse.md)>
35 |
36 | *__description__*: Check for available updates for the currently configured app id and channel.
37 |
38 | *__since__*: v5.0.0
39 |
40 | **Returns:** `Promise`<[CheckForUpdateResponse](checkforupdateresponse.md)>
41 | A response describing an update if one is available.
42 |
43 | ___
44 |
45 |
46 | ### configure
47 |
48 | ▸ **configure**(config: *[IDeployConfig](ideployconfig.md)*): `Promise`<`void`>
49 |
50 | *__description__*: Update the default configuration for the plugin on the current device. The new configuration will be persisted across app close and binary updates.
51 |
52 | *__since__*: v5.0.0
53 |
54 | **Parameters:**
55 |
56 | | Name | Type | Description |
57 | | ------ | ------ | ------ |
58 | | config | [IDeployConfig](ideployconfig.md) | The new configuration for the plugin on this device. |
59 |
60 | **Returns:** `Promise`<`void`>
61 |
62 | ___
63 |
64 |
65 | ### deleteVersionById
66 |
67 | ▸ **deleteVersionById**(versionId: *`string`*): `Promise`<`boolean`>
68 |
69 | *__description__*: Remove the files specific to a snapshot from the device.
70 |
71 | **Parameters:**
72 |
73 | | Name | Type |
74 | | ------ | ------ |
75 | | versionId | `string` |
76 |
77 | **Returns:** `Promise`<`boolean`>
78 | true if the update was deleted.
79 |
80 | ___
81 |
82 |
83 | ### downloadUpdate
84 |
85 | ▸ **downloadUpdate**(progress?: *[CallbackFunction](callbackfunction.md)<`number`>*): `Promise`<`boolean`>
86 |
87 | *__description__*: Download the new files from an available update found by the checkForUpdate method and prepare the update.
88 |
89 | *__since__*: v5.0.0
90 |
91 | **Parameters:**
92 |
93 | | Name | Type | Description |
94 | | ------ | ------ | ------ |
95 | | `Optional` progress | [CallbackFunction](callbackfunction.md)<`number`> | A progress callback function which will be called with a number representing the percent of completion of the download and prepare. |
96 |
97 | **Returns:** `Promise`<`boolean`>
98 | true if the download succeeded
99 |
100 | ___
101 |
102 |
103 | ### extractUpdate
104 |
105 | ▸ **extractUpdate**(progress?: *[CallbackFunction](callbackfunction.md)<`number`>*): `Promise`<`boolean`>
106 |
107 | *__description__*: Extract a downloaded bundle of updated files.
108 |
109 | *__since__*: v5.0.0
110 |
111 | **Parameters:**
112 |
113 | | Name | Type | Description |
114 | | ------ | ------ | ------ |
115 | | `Optional` progress | [CallbackFunction](callbackfunction.md)<`number`> | A progress callback function which will be called with a number representing the percent of completion of the extract. |
116 |
117 | **Returns:** `Promise`<`boolean`>
118 | true if the extract succeeded
119 |
120 | ___
121 |
122 |
123 | ### getAvailableVersions
124 |
125 | ▸ **getAvailableVersions**(): `Promise`<[ISnapshotInfo](isnapshotinfo.md)[]>
126 |
127 | *__description__*: Get a list of the snapshots available on the device.
128 |
129 | *__since__*: v5.0.0
130 |
131 | **Returns:** `Promise`<[ISnapshotInfo](isnapshotinfo.md)[]>
132 | a list of available updates.
133 |
134 | ___
135 |
136 |
137 | ### getConfiguration
138 |
139 | ▸ **getConfiguration**(): `Promise`<[ICurrentConfig](icurrentconfig.md)>
140 |
141 | *__description__*: Get the current configuration for the plugin on the current device.
142 |
143 | *__since__*: v5.0.0
144 |
145 | **Returns:** `Promise`<[ICurrentConfig](icurrentconfig.md)>
146 | The current configuration of the plugin.
147 |
148 | ___
149 |
150 |
151 | ### getCurrentVersion
152 |
153 | ▸ **getCurrentVersion**(): `Promise`<[ISnapshotInfo](isnapshotinfo.md) \| `undefined`>
154 |
155 | *__description__*: Get info about the currently deployed update or undefined if none are applied.
156 |
157 | *__since__*: v5.0.0
158 |
159 | **Returns:** `Promise`<[ISnapshotInfo](isnapshotinfo.md) \| `undefined`>
160 | The info about the currently applied update or undefined if none is applied.
161 |
162 | ___
163 |
164 |
165 | ### reloadApp
166 |
167 | ▸ **reloadApp**(): `Promise`<`boolean`>
168 |
169 | *__description__*: Reload the app if a more recent version of the app is available.
170 |
171 | *__since__*: v5.0.0
172 |
173 | **Returns:** `Promise`<`boolean`>
174 | true if the reload succeeded
175 |
176 | ___
177 |
178 |
179 | ### sync
180 |
181 | ▸ **sync**(syncOptions: *[ISyncOptions](isyncoptions.md)*): `Promise`<[ISnapshotInfo](isnapshotinfo.md) \| `undefined`>
182 |
183 | *__description__*: Check for an update, download it, and apply it in one step.
184 |
185 | *__since__*: v5.0.0
186 |
187 | **Parameters:**
188 |
189 | | Name | Type | Description |
190 | | ------ | ------ | ------ |
191 | | syncOptions | [ISyncOptions](isyncoptions.md) | (Optional) Application update overrides. |
192 |
193 | **Returns:** `Promise`<[ISnapshotInfo](isnapshotinfo.md) \| `undefined`>
194 | The info about the currently applied update or undefined if none is applied.
195 |
196 | ___
197 |
198 |
--------------------------------------------------------------------------------
/docs/interfaces/ipluginbaseapi.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: IPluginBaseAPI
4 |
5 | The IonicCordova Plugin API
6 |
7 | ## Hierarchy
8 |
9 | **IPluginBaseAPI**
10 |
11 | ## Index
12 |
13 | ### Properties
14 |
15 | * [deploy](ipluginbaseapi.md#deploy)
16 |
17 | ### Methods
18 |
19 | * [getAppDetails](ipluginbaseapi.md#getappdetails)
20 | * [getAppInfo](ipluginbaseapi.md#getappinfo)
21 |
22 | ---
23 |
24 | ## Properties
25 |
26 |
27 |
28 | ### deploy
29 |
30 | **● deploy**: *[IDeployPluginAPI](ideploypluginapi.md)*
31 |
32 | An instance of the Ionic Deploy Plugin API
33 |
34 | ___
35 |
36 | ## Methods
37 |
38 |
39 |
40 | ### getAppDetails
41 |
42 | ▸ **getAppDetails**(): `Promise`<[IAppInfo](iappinfo.md)>
43 |
44 | *__description__*: Get info about the current app.
45 |
46 | **Returns:** `Promise`<[IAppInfo](iappinfo.md)>
47 |
48 | ___
49 |
50 |
51 | ### getAppInfo
52 |
53 | ▸ **getAppInfo**(success: *`Function`*, failure: *`Function`*): `void`
54 |
55 | **Parameters:**
56 |
57 | | Name | Type | Description |
58 | | ------ | ------ | ------ |
59 | | success | `Function` | \- |
60 | | failure | `Function` | |
61 |
62 | **Returns:** `void`
63 |
64 | ___
65 |
66 |
--------------------------------------------------------------------------------
/docs/interfaces/isnapshotinfo.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: ISnapshotInfo
4 |
5 | Information about a snapshot
6 |
7 | ## Hierarchy
8 |
9 | **ISnapshotInfo**
10 |
11 | ## Index
12 |
13 | ### Properties
14 |
15 | * [binaryVersion](isnapshotinfo.md#binaryversion)
16 | * [binaryVersionCode](isnapshotinfo.md#binaryversioncode)
17 | * [binaryVersionName](isnapshotinfo.md#binaryversionname)
18 | * [binary_version](isnapshotinfo.md#binary_version)
19 | * [buildId](isnapshotinfo.md#buildid)
20 | * [channel](isnapshotinfo.md#channel)
21 | * [deploy_uuid](isnapshotinfo.md#deploy_uuid)
22 | * [versionId](isnapshotinfo.md#versionid)
23 |
24 | ---
25 |
26 | ## Properties
27 |
28 |
29 |
30 | ### binaryVersion
31 |
32 | **● binaryVersion**: *`string`*
33 |
34 | *__deprecated__*: The binary version the snapshot was downloaded for. The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
35 |
36 | ___
37 |
38 |
39 | ### binaryVersionCode
40 |
41 | **● binaryVersionCode**: *`string`*
42 |
43 | The binary version build code the snapshot was downloaded for. The versionCode on Android or CFBundleVersion on iOS this should be changed every time you do a new build debug or otherwise.
44 |
45 | ___
46 |
47 |
48 | ### binaryVersionName
49 |
50 | **● binaryVersionName**: *`string`*
51 |
52 | The binary version name the snapshot was downloaded for. The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
53 |
54 | ___
55 |
56 |
57 | ### binary_version
58 |
59 | **● binary_version**: *`string`*
60 |
61 | *__deprecated__*: in favor of [binaryVersion](#binaryversion)
62 |
63 | The binary version the snapshot was downloaded for.
64 |
65 | ___
66 |
67 |
68 | ### buildId
69 |
70 | **● buildId**: *`string`*
71 |
72 | The id for the snapshot.
73 |
74 | ___
75 |
76 |
77 | ### channel
78 |
79 | **● channel**: *`string`*
80 |
81 | The channel that the snapshot was downloaded for..
82 |
83 | ___
84 |
85 |
86 | ### deploy_uuid
87 |
88 | **● deploy_uuid**: *`string`*
89 |
90 | *__deprecated__*: in favor of [versionId](#versionid)
91 |
92 | The id for the snapshot.
93 |
94 | ___
95 |
96 |
97 | ### versionId
98 |
99 | **● versionId**: *`string`*
100 |
101 | The id for the snapshot.
102 |
103 | ___
104 |
105 |
--------------------------------------------------------------------------------
/docs/interfaces/isyncoptions.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: ISyncOptions
4 |
5 | Configuration options for the call to `sync`
6 |
7 | ## Hierarchy
8 |
9 | **ISyncOptions**
10 |
11 | ## Index
12 |
13 | ### Properties
14 |
15 | * [updateMethod](isyncoptions.md#updatemethod)
16 |
17 | ---
18 |
19 | ## Properties
20 |
21 |
22 |
23 | ### `` updateMethod
24 |
25 | **● updateMethod**: *"background" \| "auto"*
26 |
27 | Whether the update should be applied immediately or on the next app start.
28 |
29 | ___
30 |
31 |
--------------------------------------------------------------------------------
/docs/interfaces/window.md:
--------------------------------------------------------------------------------
1 | [Cordova Plugin Ionic](../../README.md)
2 |
3 | # Interface: Window
4 |
5 | ## Hierarchy
6 |
7 | **Window**
8 |
9 | ## Index
10 |
11 | ### Properties
12 |
13 | * [IonicCordova](window.md#ioniccordova)
14 |
15 | ---
16 |
17 | ## Properties
18 |
19 |
20 |
21 | ### IonicCordova
22 |
23 | **● IonicCordova**: *[IPluginBaseAPI](ipluginbaseapi.md)*
24 |
25 | ___
26 |
27 |
--------------------------------------------------------------------------------
/hooks/beforePrepare.js:
--------------------------------------------------------------------------------
1 | const exec = require('child_process').exec;
2 |
3 | module.exports = function(ctx) {
4 | return new Promise((resolve, reject) => {
5 | exec(
6 | 'ionic --version --no-interactive',
7 | { cwd: ctx.opts.projectRoot },
8 | ionicVersionOutput.bind({}, ctx.opts.projectRoot, resolve, reject)
9 | )
10 | });
11 | };
12 |
13 | function ionicVersionOutput(rootDir, resolve, reject, err, version, stderr) {
14 | if(err) {
15 | console.error('There was an error checking your version of the Ionic CLI are you sure you have it installed?');
16 | console.log(err);
17 | console.log(stderr);
18 | reject();
19 | }
20 | const versionInfo = version.split('.');
21 | let majorVersion = undefined;
22 | while (versionInfo.length && isNaN(majorVersion)) {
23 | majorVersion = versionInfo.shift();
24 | }
25 | if (isNaN(majorVersion)) {
26 | console.error('There was an error checking your version of the Ionic CLI are you sure you have it installed?');
27 | reject();
28 | }
29 | else if (majorVersion < 4) {
30 | console.error(`You are running version ${majorVersion} of the Ionic CLI. Version 4 or greater is required for this plugin.`);
31 | reject();
32 | }
33 |
34 | console.log('Generating initial manifest for Ionic Deploy...');
35 | exec(
36 | 'ionic deploy manifest --no-interactive',
37 | { cwd: rootDir },
38 | ionicManifestOutput.bind({}, resolve, reject)
39 | )
40 | }
41 |
42 | function ionicManifestOutput(resolve, reject, err, version, stderr) {
43 | if(err) {
44 | console.error('There was an error generating the initial manifest of files for the deploy plugin.');
45 | reject()
46 | }
47 | resolve();
48 | console.log('Ionic Deploy initial manifest successfully generated.');
49 | }
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cordova-plugin-ionic",
3 | "version": "5.5.3",
4 | "cordova": {
5 | "id": "cordova-plugin-ionic",
6 | "platforms": [
7 | "android",
8 | "ios"
9 | ]
10 | },
11 | "main": "dist/index.js",
12 | "types": "dist/index.d.ts",
13 | "description": "Ionic Cordova SDK",
14 | "scripts": {
15 | "apply-dev": "npm run build && ./scripts/apply-changes.sh",
16 | "create-dev": "./scripts/create-local-app.sh",
17 | "clean": "rimraf dist",
18 | "lint": "tslint --config tslint.json --project tsconfig.json",
19 | "precommit": "npm run lint",
20 | "watch": "tsc -w",
21 | "watch-dev": "watch 'npm run apply-dev' ./www",
22 | "build": "npm run clean && tsc && ngc -p tsconfig.ng.json && ./scripts/ng-prepare.sh",
23 | "sync-plugin-xml": "sync-cordova-xml2 package.json plugin.xml --output=plugin.xml",
24 | "version": "npm run sync-plugin-xml && git add plugin.xml && ./scripts/update-plugin-version-code.sh",
25 | "prepublishOnly": "npm run build",
26 | "test": "echo 'We should really get unit tests running'",
27 | "release": "npm run build && np --any-branch",
28 | "docs": "./scripts/docs.sh"
29 | },
30 | "repository": "https://github.com/ionic-team/cordova-plugin-ionic.git",
31 | "issue": "https://github.com/ionic-team/cordova-plugin-ionic/issues",
32 | "bugs": {
33 | "url": "https://github.com/ionic-team/cordova-plugin-ionic/issues"
34 | },
35 | "keywords": [
36 | "ionic",
37 | "cordova",
38 | "deploy",
39 | "liveupdates",
40 | "mobile",
41 | "hybrid",
42 | "ecosystem:cordova",
43 | "cordova-android",
44 | "cordova-ios"
45 | ],
46 | "author": "Ionic",
47 | "contributors": [
48 | {
49 | "name": "Max Lynch",
50 | "email": "max@ionic.io"
51 | },
52 | {
53 | "name": "William Pelrine",
54 | "email": "rudy@ionic.io"
55 | }
56 | ],
57 | "license": "MIT",
58 | "devDependencies": {
59 | "@angular/compiler": "^9.1.1",
60 | "@angular/compiler-cli": "^9.1.1",
61 | "@angular/core": "^7.2.15",
62 | "@types/cordova": "0.0.34",
63 | "jest": "^22.4.3",
64 | "np": "^3.0.4",
65 | "rimraf": "^2.6.2",
66 | "sync-cordova-xml2": "0.0.2",
67 | "ts-jest": "^22.4.2",
68 | "tslint": "^5.9.1",
69 | "tslint-ionic-rules": "0.0.14",
70 | "typedoc": "^0.11.1",
71 | "typedoc-plugin-markdown": "^1.1.6",
72 | "watch": "^1.0.2"
73 | },
74 | "jest": {
75 | "globals": {
76 | "ts-jest": {
77 | "tsConfigFile": "tsconfig.json"
78 | }
79 | },
80 | "moduleFileExtensions": [
81 | "ts",
82 | "js"
83 | ],
84 | "transform": {
85 | ".(ts)": "./node_modules/ts-jest/preprocessor.js"
86 | },
87 | "testRegex": "tests/.*\\.(ts|js)$"
88 | },
89 | "dependencies": {
90 | "typescript": "3.8.3"
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | cordova-plugin-ionic
4 | Ionic Cordova SDK
5 | MIT
6 | ionic,cordova,deploy,liveupdates,mobile,hybrid,ecosystem:cordova,cordova-android,cordova-ios
7 | https://github.com/ionic-team/cordova-plugin-ionic.git
8 | https://github.com/ionic-team/cordova-plugin-ionic/issues
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | $APP_ID
33 |
34 |
35 | $CHANNEL_NAME
36 |
37 |
38 | $UPDATE_API
39 |
40 |
41 | $UPDATE_METHOD
42 |
43 |
44 | $MAX_STORE
45 |
46 |
47 | $MIN_BACKGROUND_DURATION
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | $APP_ID
65 | $CHANNEL_NAME
66 | $UPDATE_API
67 | $UPDATE_METHOD
68 | $MAX_STORE
69 | $MIN_BACKGROUND_DURATION
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | Ionic
81 |
82 |
--------------------------------------------------------------------------------
/scripts/apply-changes.sh:
--------------------------------------------------------------------------------
1 | # local manual "watch" script
2 | set -o errexit
3 |
4 | # Script vars
5 | APP_PATH=${IONIC_APP_NAME:-../testapp}
6 |
7 | # Sync to iOS
8 | cp ./dist/* ${APP_PATH}/platforms/ios/www/plugins/cordova-plugin-ionic/dist/
9 | sed -i '' 's/"use strict"/cordova.define("cordova-plugin-ionic.common", function(require, exports, module) {"use strict"/' ${APP_PATH}/platforms/ios/www/plugins/cordova-plugin-ionic/dist/common.js
10 | sed -i '' 's/"use strict"/cordova.define("cordova-plugin-ionic.guards", function(require, exports, module) {"use strict"/' ${APP_PATH}/platforms/ios/www/plugins/cordova-plugin-ionic/dist/guards.js
11 | echo '});' >> ${APP_PATH}/platforms/ios/www/plugins/cordova-plugin-ionic/dist/common.js
12 | echo '});' >> ${APP_PATH}/platforms/ios/www/plugins/cordova-plugin-ionic/dist/guards.js
13 |
14 | # Sync to Android
15 | cp ./dist/* ${APP_PATH}/platforms/android/platform_www/plugins/cordova-plugin-ionic/dist/
16 | sed -i '' 's/"use strict"/cordova.define("cordova-plugin-ionic.common", function(require, exports, module) {"use strict"/' ${APP_PATH}/platforms/android/platform_www/plugins/cordova-plugin-ionic/dist/common.js
17 | sed -i '' 's/"use strict"/cordova.define("cordova-plugin-ionic.guards", function(require, exports, module) {"use strict"/' ${APP_PATH}/platforms/android/platform_www/plugins/cordova-plugin-ionic/dist/guards.js
18 | echo '});' >> ${APP_PATH}/platforms/android/platform_www/plugins/cordova-plugin-ionic/dist/common.js
19 | echo '});' >> ${APP_PATH}/platforms/android/platform_www/plugins/cordova-plugin-ionic/dist/guards.js
20 |
--------------------------------------------------------------------------------
/scripts/create-local-app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o errexit
3 | set -o nounset
4 |
5 | # Stop CLI prompts and init config vars
6 | APP_ID=${IONIC_APP_ID:-2de70dab}
7 | APP_NAME=${IONIC_APP_NAME:-testapp}
8 | CHANNEL=${IONIC_CHANNEL:-Master}
9 | CI=1
10 | UPDATE_METHOD=${IONIC_UPDATE_METHOD:-auto}
11 | BACKGROUND_DURATION=${IONIC_BACKGROUND_DURATION:-1}
12 |
13 | # Build the plugin ts
14 | npm run build
15 |
16 | # Create a blank ionic app cd
17 | cd ..
18 | ionic start ${APP_NAME} blank --type=ionic-angular --cordova
19 | cd ${APP_NAME}
20 | npm run build
21 |
22 | # Add cordova platform and install the plugin
23 | cordova platform add ios@latest
24 | cordova platform add android@latest
25 | cordova plugin add ../cordova-plugin-ionic --save \
26 | --variable MIN_BACKGROUND_DURATION="$BACKGROUND_DURATION" \
27 | --variable APP_ID="${APP_ID}" \
28 | --variable CHANNEL_NAME="${CHANNEL}" \
29 | --variable UPDATE_METHOD="${UPDATE_METHOD}" --link
30 | cordova prepare ios
31 | cordova prepare android
32 |
--------------------------------------------------------------------------------
/scripts/docs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o errexit
3 | rm -rf docs
4 | npx typedoc --theme markdown --mdEngine github --name "Cordova Plugin Ionic" --readme none --hideGenerator --out docs/ --includeDeclarations --excludeExternals --mdHideSources --mode file types/IonicCordova.d.ts
5 | rm -rf docs/README.md
6 | rm -rf docs/modules
7 | ls -d -1 docs/interfaces/* | xargs sed -i '' 's/README\.md.*\.md/..\/README.md/g'
8 |
--------------------------------------------------------------------------------
/scripts/ng-prepare.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 |
3 | cat<dist/ngx/package.json
4 | {
5 | "name": "cordova-plugin-ionic",
6 | "main": "ngx/index.js",
7 | "module": "ngx/index.js",
8 | "typings": "ngx/index.d.ts"
9 | }
10 | EOF
11 |
--------------------------------------------------------------------------------
/scripts/update-plugin-version-code.sh:
--------------------------------------------------------------------------------
1 | # Releases a new version to npm
2 | set -f errexit
3 |
4 | # Script vars
5 | VERSION=$(npm list cordova-plugin-ionic | grep "@" | cut -d "@" -f 2 | cut -d " " -f 1);
6 | echo "updating PLUGIN_VERSION in www/common.ts to $VERSION";
7 | # Update common.ts
8 | sed -E -i "" "s/^ public PLUGIN_VERSION = '[0-9a-zA-Z\.-]+';/ public PLUGIN_VERSION = '$VERSION';/g" www/common.ts
9 | git add www/common.ts
10 |
11 | echo "Remember to update the changelog..."
12 |
--------------------------------------------------------------------------------
/src/android/IonicCordovaCommon.java:
--------------------------------------------------------------------------------
1 | package com.ionicframework.common;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.res.AssetManager;
6 | import android.net.Uri;
7 |
8 | import org.apache.commons.io.FileUtils;
9 | import org.apache.cordova.CordovaWebView;
10 | import org.apache.cordova.CallbackContext;
11 | import org.apache.cordova.CordovaPlugin;
12 | import org.apache.cordova.CordovaInterface;
13 | import org.apache.cordova.PluginResult;
14 | import org.json.JSONArray;
15 | import org.json.JSONObject;
16 | import org.json.JSONException;
17 |
18 | import android.os.Environment;
19 | import android.util.Log;
20 | import android.app.Activity;
21 | import android.content.pm.PackageInfo;
22 | import android.os.Build;
23 |
24 | import java.io.DataInputStream;
25 | import java.io.File;
26 | import java.io.FileOutputStream;
27 | import java.io.IOException;
28 | import java.io.InputStream;
29 | import java.io.OutputStream;
30 | import java.net.URL;
31 | import java.util.Iterator;
32 | import java.util.UUID;
33 |
34 | public class IonicCordovaCommon extends CordovaPlugin {
35 | public static final String TAG = "IonicCordovaCommon";
36 | private static final String PREFS_KEY = "ionicDeploySavedPreferences";
37 | private static final String CUSTOM_PREFS_KEY = "ionicDeployCustomPreferences";
38 | private AssetManager assetManager;
39 |
40 |
41 | private SharedPreferences prefs;
42 | private String uuid;
43 |
44 | private interface FileOp {
45 | void run(final JSONArray args, final CallbackContext callbackContext) throws Exception;
46 | }
47 |
48 | /**
49 | * Sets the context of the Command. This can then be used to do things like
50 | * get file paths associated with the Activity.
51 | *
52 | * @param cordova The context of the main Activity.
53 | * @param webView The CordovaWebView Cordova is running in.
54 | */
55 | public void initialize(CordovaInterface cordova, CordovaWebView webView) {
56 | super.initialize(cordova, webView);
57 |
58 | // Initialize shared preferences
59 | Context cxt = this.cordova.getActivity().getApplicationContext();
60 | this.prefs = cxt.getSharedPreferences("com.ionic.common.preferences", Context.MODE_PRIVATE);
61 | assetManager = cordova.getContext().getAssets();
62 |
63 | // Get or generate a plugin UUID
64 | this.uuid = this.prefs.getString("uuid", UUID.randomUUID().toString());
65 | prefs.edit().putString("uuid", this.uuid).apply();
66 | }
67 |
68 | private void threadhelper(final FileOp f, final JSONArray args, final CallbackContext callbackContext){
69 | cordova.getThreadPool().execute(new Runnable() {
70 | public void run() {
71 | try {
72 | f.run(args, callbackContext);
73 | } catch ( Exception e) {
74 | callbackContext.error(e.getMessage());
75 | }
76 | }
77 | });
78 | }
79 |
80 | /**
81 | * Executes the request and returns PluginResult.
82 | *
83 | * @param action The action to execute.
84 | * @param args JSONArray of arguments for the plugin.
85 | * @param callbackContext The callback id used when calling back into JavaScript.
86 | * @return True if the action was valid, false if not.
87 | */
88 | public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
89 | if (action.equals("getAppInfo")) {
90 | this.getAppInfo(callbackContext);
91 | } else if (action.equals("getPreferences")) {
92 | this.getPreferences(callbackContext);
93 | } else if (action.equals("setPreferences")) {
94 | this.setPreferences(callbackContext, args.getJSONObject(0));
95 | } else if (action.equals("configure")){
96 | this.configure(callbackContext, args.getJSONObject(0));
97 | } else if (action.equals("copyTo")){
98 | this.copyTo(callbackContext, args.getJSONObject(0));
99 | } else if (action.equals("remove")){
100 | this.remove(callbackContext, args.getJSONObject(0));
101 | } else if (action.equals("downloadFile")){
102 | threadhelper( new FileOp( ){
103 | public void run(final JSONArray passedArgs, final CallbackContext cbcontext) throws JSONException {
104 | downloadFile(cbcontext, passedArgs.getJSONObject(0));
105 | }
106 | }, args, callbackContext);
107 |
108 | } else {
109 | return false;
110 | }
111 |
112 | return true;
113 | }
114 |
115 | private File getDirectory(String directory) {
116 | Context c = cordova.getContext();
117 | switch(directory) {
118 | case "DOCUMENTS":
119 | return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
120 | case "DATA":
121 | return c.getFilesDir();
122 | case "CACHE":
123 | return c.getCacheDir();
124 | case "EXTERNAL":
125 | return c.getExternalFilesDir(null);
126 | case "EXTERNAL_STORAGE":
127 | return Environment.getExternalStorageDirectory();
128 | }
129 | return null;
130 | }
131 |
132 | private void copyAssets(String assetPath, String targetDir) throws IOException {
133 | String[] files = null;
134 | try {
135 | files = assetManager.list(assetPath);
136 | } catch (IOException e) {
137 | Log.e("tag", "Failed to get asset file list.", e);
138 | }
139 | if (files != null) for (String filename : files) {
140 | InputStream in = null;
141 | OutputStream out = null;
142 | try {
143 | if (assetManager.list(assetPath + "/" + filename).length > 0) {
144 | File newDir = new File(targetDir, filename);
145 | newDir.mkdir();
146 | copyAssets(assetPath + "/" + filename, newDir.getPath());
147 | continue;
148 | }
149 | in = assetManager.open(assetPath + "/" + filename);
150 | File destDir = new File(targetDir);
151 | if (!destDir.exists()) {
152 | destDir.mkdirs();
153 | }
154 | File outFile = new File(targetDir, filename);
155 | out = new FileOutputStream(outFile);
156 | copyFile(in, out);
157 | } catch(IOException e) {
158 | Log.e("tag", "Failed to copy asset file: " + filename, e);
159 | }
160 | finally {
161 | if (in != null) {
162 | try {
163 | in.close();
164 | } catch (IOException e) {
165 | // NOOP
166 | }
167 | }
168 | if (out != null) {
169 | try {
170 | out.close();
171 | } catch (IOException e) {
172 | // NOOP
173 | }
174 | }
175 | }
176 | }
177 | }
178 |
179 | private void copyFile(InputStream in, OutputStream out) throws IOException {
180 | byte[] buffer = new byte[1024];
181 | int read;
182 | while((read = in.read(buffer)) != -1){
183 | out.write(buffer, 0, read);
184 | }
185 | }
186 |
187 | /**
188 | * copy a directory or file to another location
189 | *
190 | */
191 | public void copyTo(CallbackContext callbackContext, JSONObject options) throws JSONException {
192 | Log.d(TAG, "copyTo called with " + options.toString());
193 | PluginResult result;
194 |
195 | try {
196 | JSONObject source = options.getJSONObject("source");
197 | String target = options.getString("target");
198 |
199 | if (source.getString("directory").equals("APPLICATION")) {
200 | this.copyAssets(source.getString("path"), target);
201 | } else {
202 | File srcDir = this.getDirectory(source.getString("directory"));
203 | File srcFile = new File(srcDir.getPath() + "/" + source.getString("path"));
204 |
205 | if (!srcFile.exists()) {
206 | result = new PluginResult(PluginResult.Status.ERROR, "source file or directory does not exist");
207 | result.setKeepCallback(false);
208 | callbackContext.sendPluginResult(result);
209 | return;
210 | }
211 |
212 | if (srcFile.isDirectory()) {
213 | FileUtils.copyDirectory(srcFile, new File(target));
214 | } else {
215 | FileUtils.copyFile(srcFile, new File(target));
216 | }
217 | }
218 | } catch (Exception e) {
219 | result = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
220 | result.setKeepCallback(false);
221 | callbackContext.sendPluginResult(result);
222 | return;
223 | }
224 |
225 | result = new PluginResult(PluginResult.Status.OK);
226 | result.setKeepCallback(false);
227 | callbackContext.sendPluginResult(result);
228 | }
229 |
230 | /**
231 | * recursively remove a directory or a file
232 | *
233 | */
234 | public void remove(CallbackContext callbackContext, JSONObject options) throws JSONException {
235 | Log.d(TAG, "recursiveRemove called with " + options.toString());
236 | String target = options.getString("target");
237 | File dest = new File(target);
238 | final PluginResult result;
239 |
240 | if (!dest.exists()) {
241 | result = new PluginResult(PluginResult.Status.ERROR, "file or directory does not exist");
242 | result.setKeepCallback(false);
243 | callbackContext.sendPluginResult(result);
244 | return;
245 | }
246 |
247 | try {
248 | FileUtils.forceDelete(dest);
249 | } catch (IOException e) {
250 | result = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
251 | result.setKeepCallback(false);
252 | callbackContext.sendPluginResult(result);
253 | return;
254 | }
255 |
256 | result = new PluginResult(PluginResult.Status.OK);
257 | result.setKeepCallback(false);
258 | callbackContext.sendPluginResult(result);
259 | }
260 |
261 | public void downloadFile(CallbackContext callbackContext, JSONObject options) throws JSONException {
262 | Log.d(TAG, "downloadFile called with " + options.toString());
263 | String url = options.getString("url");
264 | String dest = options.getString("target");
265 | final PluginResult result;
266 |
267 | try {
268 | URL u = new URL(url);
269 | InputStream is = u.openStream();
270 |
271 | DataInputStream dis = new DataInputStream(is);
272 |
273 | byte[] buffer = new byte[1024];
274 | int length;
275 |
276 | File downFile = new File(dest);
277 | downFile.getParentFile().mkdirs();
278 | downFile.createNewFile();
279 | FileOutputStream fos = new FileOutputStream(downFile);
280 | while ((length = dis.read(buffer))>0) {
281 | fos.write(buffer, 0, length);
282 | }
283 |
284 | } catch (Exception e) {
285 | Log.e(TAG, "downloadFile error", e);
286 | result = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
287 | result.setKeepCallback(false);
288 | callbackContext.sendPluginResult(result);
289 | return;
290 | }
291 | result = new PluginResult(PluginResult.Status.OK);
292 | result.setKeepCallback(false);
293 | callbackContext.sendPluginResult(result);
294 | }
295 |
296 | /**
297 | * Get basic app information. Used for the Ionic monitoring service.
298 | *
299 | * @param callbackContext The callback id used when calling back into JavaScript.
300 | */
301 | public void getAppInfo(CallbackContext callbackContext) throws JSONException {
302 | JSONObject j = new JSONObject();
303 |
304 | try {
305 | PackageInfo pInfo = this.cordova.getActivity().getPackageManager().getPackageInfo(this.cordova.getActivity().getPackageName(), 0);
306 | String versionName = pInfo.versionName;
307 | String name = pInfo.packageName;
308 | int versionCode = pInfo.versionCode;
309 | String platformVersion = String.valueOf(Build.VERSION.RELEASE);
310 |
311 | j.put("platform", "android");
312 | j.put("platformVersion", platformVersion);
313 | j.put("version", versionCode);
314 | j.put("binaryVersionCode", versionCode);
315 | j.put("bundleName", name);
316 | j.put("bundleVersion", versionName);
317 | j.put("binaryVersionName", versionName);
318 | j.put("device", this.uuid);
319 | j.put("dataDirectory", toDirUrl(cordova.getActivity().getFilesDir()));
320 |
321 | Log.d(TAG, "Got package info. Version: " + versionName + ", bundleName: " + name + ", versionCode: " + versionCode);
322 | final PluginResult result = new PluginResult(PluginResult.Status.OK, j);
323 | result.setKeepCallback(false);
324 | callbackContext.sendPluginResult(result);
325 | } catch(Exception ex) {
326 | Log.e(TAG, "Unable to get package info", ex);
327 | callbackContext.error(ex.toString());
328 | }
329 | }
330 |
331 | /**
332 | * Grabs a string from the activity's resources.
333 | *
334 | * @param aString The name of the resource to retrieve
335 | * @return The string contents of the resource
336 | */
337 | private String getStringResourceByName(String aString) {
338 | Activity activity = cordova.getActivity();
339 | String packageName = activity.getPackageName();
340 | int resId = activity.getResources().getIdentifier(aString, "string", packageName);
341 | return activity.getString(resId);
342 | }
343 |
344 | /**
345 | * Get saved prefs configured via code at runtime
346 | *
347 | */
348 | public JSONObject getCustomConfig() throws JSONException {
349 | SharedPreferences prefs = this.cordova.getActivity().getApplicationContext().getSharedPreferences("com.ionic.deploy.preferences", Context.MODE_PRIVATE);
350 | String prefsString = prefs.getString(this.CUSTOM_PREFS_KEY, null);
351 | if (prefsString != null) {
352 | JSONObject customPrefs = new JSONObject(prefsString);
353 | return customPrefs;
354 | }
355 | return new JSONObject("{}");
356 | }
357 |
358 | /**
359 | * Set saved prefs configured via code at runtime
360 | *
361 | */
362 | public void configure(CallbackContext callbackContext, JSONObject newConfig) throws JSONException {
363 | Log.i(TAG, "Set custom config called with " + newConfig.toString());
364 | SharedPreferences prefs = this.cordova.getActivity().getApplicationContext().getSharedPreferences("com.ionic.deploy.preferences", Context.MODE_PRIVATE);
365 | SharedPreferences.Editor editor = prefs.edit();
366 | JSONObject storedConfig = this.getCustomConfig();
367 | this.mergeObjects(storedConfig, newConfig);
368 | editor.putString(this.CUSTOM_PREFS_KEY, storedConfig.toString());
369 | editor.commit();
370 | Log.i(TAG, "config updated");
371 |
372 | final PluginResult result = new PluginResult(PluginResult.Status.OK, storedConfig);
373 | result.setKeepCallback(false);
374 | callbackContext.sendPluginResult(result);
375 | }
376 |
377 | /**
378 | * Get cordova plugin preferences and state information.
379 | *
380 | * @param callbackContext The callback id used when calling back into JavaScript.
381 | */
382 | public void getPreferences(CallbackContext callbackContext) throws JSONException {
383 |
384 | JSONObject nativePrefs = this.getNativeConfig();
385 | JSONObject customPrefs = this.getCustomConfig();
386 |
387 | // Check for prefs that have been saved before
388 | SharedPreferences prefs = this.cordova.getActivity().getApplicationContext().getSharedPreferences("com.ionic.deploy.preferences", Context.MODE_PRIVATE);
389 | String prefsString = prefs.getString(this.PREFS_KEY, null);
390 | if (prefsString != null) {
391 | JSONObject savedPrefs;
392 | Log.i(TAG, "Found saved prefs: " + prefsString);
393 | // grab the save prefs
394 | savedPrefs = new JSONObject(prefsString);
395 |
396 | // update with the lastest things from config.xml
397 | this.mergeObjects(savedPrefs, nativePrefs);
398 |
399 | // update with the lastest things from custom configuration
400 | this.mergeObjects(savedPrefs, customPrefs);
401 |
402 | final PluginResult result = new PluginResult(PluginResult.Status.OK, savedPrefs);
403 | result.setKeepCallback(false);
404 | callbackContext.sendPluginResult(result);
405 | return;
406 | }
407 |
408 | // no saved prefs were found
409 | try {
410 | nativePrefs.put("updates", new JSONObject("{}"));
411 | final PluginResult result = new PluginResult(PluginResult.Status.OK, nativePrefs);
412 | result.setKeepCallback(false);
413 | callbackContext.sendPluginResult(result);
414 | } catch(Exception ex) {
415 | Log.e(TAG, "Unable to get preferences", ex);
416 | callbackContext.error(ex.toString());
417 | }
418 | }
419 |
420 | private JSONObject getNativeConfig() throws JSONException {
421 | JSONObject j = new JSONObject();
422 | int maxV;
423 | int minBackgroundDuration;
424 | try {
425 | maxV = Integer.parseInt(getStringResourceByName("ionic_max_versions"));
426 | } catch(NumberFormatException e) {
427 | maxV = 2;
428 | }
429 |
430 | try {
431 | minBackgroundDuration = Integer.parseInt(getStringResourceByName("ionic_min_background_duration"));
432 | } catch(NumberFormatException e) {
433 | minBackgroundDuration = 30;
434 | }
435 | String versionName;
436 | int versionCode;
437 | try {
438 | PackageInfo pInfo = this.cordova.getActivity().getPackageManager().getPackageInfo(this.cordova.getActivity().getPackageName(), 0);
439 | versionName = pInfo.versionName;
440 | versionCode = pInfo.versionCode;
441 | } catch(Exception ex) {
442 | Log.e(TAG, "Unable to get package info", ex);
443 | versionName = "unknown";
444 | versionCode = 0;
445 | }
446 |
447 | String appId = getStringResourceByName("ionic_app_id");
448 | j.put("appId", appId);
449 | j.put("disabled", preferences.getBoolean("DisableDeploy", false));
450 | j.put("channel", getStringResourceByName("ionic_channel_name"));
451 | j.put("host", getStringResourceByName("ionic_update_api"));
452 | j.put("updateMethod", getStringResourceByName("ionic_update_method"));
453 | j.put("maxVersions", maxV);
454 | j.put("minBackgroundDuration", minBackgroundDuration);
455 | j.put("binaryVersion", versionName);
456 | j.put("binaryVersionName", versionName);
457 | j.put("binaryVersionCode", versionCode);
458 |
459 |
460 | Log.d(TAG, "Got Native Prefs for AppID: " + appId);
461 | return j;
462 | }
463 |
464 | /**
465 | * Add any keys from obj2 into obj1 overwriting them if they exist
466 | */
467 | private void mergeObjects(JSONObject obj1, JSONObject obj2) {
468 | Iterator it = obj2.keys();
469 | while (it.hasNext()) {
470 | String key = (String)it.next();
471 | try {
472 | obj1.putOpt(key, obj2.opt(key));
473 | } catch (JSONException ex) {
474 | Log.d(TAG, "key didn't exist when merging object");
475 | }
476 | }
477 | }
478 |
479 | /**
480 | * Set cordova plugin preferences and state information.
481 | * @param callbackContext The callback id used when calling back into JavaScript.
482 | * @param newPrefs
483 | */
484 | public void setPreferences(CallbackContext callbackContext, JSONObject newPrefs) {
485 | Log.i(TAG, "Set preferences called with prefs" + newPrefs.toString());
486 | SharedPreferences prefs = this.cordova.getActivity().getApplicationContext().getSharedPreferences("com.ionic.deploy.preferences", Context.MODE_PRIVATE);
487 | SharedPreferences.Editor editor = prefs.edit();
488 | editor.putString(this.PREFS_KEY, newPrefs.toString());
489 | editor.commit();
490 | Log.i(TAG, "preferences updated");
491 | final PluginResult result = new PluginResult(PluginResult.Status.OK, newPrefs);
492 | result.setKeepCallback(false);
493 | callbackContext.sendPluginResult(result);
494 | }
495 |
496 | private static String toDirUrl(File f) {
497 | return Uri.fromFile(f).toString() + '/';
498 | }
499 |
500 | }
501 |
--------------------------------------------------------------------------------
/src/android/cordovapluginionic.gradle:
--------------------------------------------------------------------------------
1 | repositories{
2 | mavenCentral()
3 | }
4 |
5 | dependencies {
6 | implementation 'commons-io:commons-io:2.4'
7 | }
8 |
9 | android {
10 | packagingOptions {
11 | exclude 'META-INF/NOTICE'
12 | exclude 'META-INF/LICENSE'
13 | }
14 | }
15 |
16 | ext.postBuildExtras = {
17 | android {
18 | compileOptions {
19 | sourceCompatibility JavaVersion.VERSION_1_8
20 | targetCompatibility JavaVersion.VERSION_1_8
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/browser/IonicCordovaCommon.js:
--------------------------------------------------------------------------------
1 | function notSupported(win,fail) {
2 | console.log('IonicCordova is not supported on browser platform');
3 | setTimeout(function(){
4 | if (win) {
5 | win();
6 | }
7 | },0);
8 | }
9 |
10 | function getPreferences(win,fail) {
11 | setTimeout(function(){
12 | win({
13 | minBackgroundDuration: 30,
14 | disabled: true
15 | });
16 | },0);
17 | }
18 |
19 | module.exports = {
20 | getPreferences: getPreferences,
21 | getAppInfo: notSupported,
22 | setPreferences:notSupported,
23 | configure: notSupported
24 | };
25 |
26 | require("cordova/exec/proxy").add("IonicCordovaCommon", module.exports);
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/ios/IonicCordovaCommon.h:
--------------------------------------------------------------------------------
1 | //
2 | // IonicCordovaCommon.h
3 | // IonicCordovaCommon
4 | //
5 | // Created by Ionic on 4/26/2018.
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | #import
26 |
27 | @interface IonicCordovaCommon : CDVPlugin
28 |
29 | /**
30 | * Get basic app information. Used for the Ionic monitoring service.
31 | *
32 | * @param command
33 | *
34 | * The callback id used when calling back into JavaScript.
35 | */
36 | - (void) getAppInfo:(CDVInvokedUrlCommand*)command;
37 |
38 | /**
39 | * Get cordova plugin preferences and state information.
40 | *
41 | * @param command
42 | *
43 | * The callback id used when calling back into JavaScript.
44 | */
45 | - (void) getPreferences:(CDVInvokedUrlCommand *)command;
46 |
47 | /**
48 | * Set cordova plugin preferences and state information.
49 | *
50 | * @param command
51 | *
52 | * The callback id used when calling back into JavaScript.
53 | */
54 | - (void) setPreferences:(CDVInvokedUrlCommand *)command;
55 |
56 | /**
57 | * Set cordova custom plugin preferences and state information.
58 | *
59 | * @param command
60 | *
61 | * The callback id used when calling back into JavaScript.
62 | */
63 | - (void) configure:(CDVInvokedUrlCommand *)command;
64 |
65 | - (void) copyTo:(CDVInvokedUrlCommand *)command;
66 |
67 | - (void) remove:(CDVInvokedUrlCommand *)command;
68 |
69 | - (void) downloadFile:(CDVInvokedUrlCommand *)command;
70 |
71 | /**
72 | * Get cordova plugin native congiguration and state information (config.xml stuff)
73 | *
74 | */
75 | - (NSMutableDictionary*) getNativeConfig;
76 |
77 | /**
78 | * Get cordova plugin custom congiguration overrides (things changed via configure method)
79 | *
80 | */
81 | - (NSMutableDictionary*) getCustomConfig;
82 |
83 | @end
84 |
--------------------------------------------------------------------------------
/src/ios/IonicCordovaCommon.m:
--------------------------------------------------------------------------------
1 | #import "IonicCordovaCommon.h"
2 | #import
3 | #import
4 |
5 |
6 | @interface IonicCordovaCommon()
7 |
8 | @property Boolean revertToBase;
9 | @property NSString *baseIndexPath;
10 |
11 | @end
12 |
13 | @implementation IonicCordovaCommon
14 |
15 | - (void) pluginInitialize {
16 | NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
17 |
18 | self.revertToBase = true;
19 | self.baseIndexPath = [[NSBundle mainBundle] pathForResource:@"www" ofType: nil];
20 |
21 | if ([prefs stringForKey:@"uuid"] == nil) {
22 | [prefs setObject:[[NSUUID UUID] UUIDString] forKey:@"uuid"];
23 | }
24 | [prefs synchronize];
25 | }
26 |
27 | - (void) remove:(CDVInvokedUrlCommand*)command {
28 | NSDictionary *options = command.arguments[0];
29 | NSString *path = options[@"target"];
30 | NSLog(@"Got remove path: %@", path);
31 | NSError *removeError = nil;
32 | if (![[NSFileManager defaultManager] removeItemAtPath:path error:&removeError]) {
33 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: [removeError localizedDescription]] callbackId:command.callbackId];
34 | return;
35 | }
36 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
37 | }
38 |
39 | - (void) copyTo:(CDVInvokedUrlCommand*)command {
40 | NSDictionary *options = command.arguments[0];
41 | NSLog(@"Got copyTo: %@", options);
42 | NSString *srcDir = options[@"source"][@"directory"];
43 | NSString *srcPath = options[@"source"][@"path"];
44 | NSString *dest = options[@"target"];
45 |
46 | if (![srcDir isEqualToString:@"APPLICATION"]) {
47 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"Only Application directory is supported"] callbackId:command.callbackId];
48 | return;
49 | }
50 | NSMutableString *source = [NSMutableString stringWithString:[[NSBundle mainBundle] resourcePath]];
51 | [source appendString:@"/"];
52 | [source appendString:srcPath];
53 | NSError *createDirError = nil;
54 | if (![[NSFileManager defaultManager] createDirectoryAtPath:dest withIntermediateDirectories:YES attributes:nil error:&createDirError]) {
55 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: [createDirError localizedDescription]] callbackId:command.callbackId];
56 | return;
57 | }
58 | [[NSFileManager defaultManager] removeItemAtPath:dest error:nil];
59 | NSError *copyError = nil;
60 | if (![[NSFileManager defaultManager] copyItemAtPath:source toPath:dest error:©Error]) {
61 | NSLog(@"Error copying files: %@", [copyError localizedDescription]);
62 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: [copyError localizedDescription]] callbackId:command.callbackId];
63 | return;
64 | }
65 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
66 | }
67 |
68 | - (void) downloadFile:(CDVInvokedUrlCommand*)command {
69 | NSDictionary *options = command.arguments[0];
70 | NSString *target = options[@"target"];
71 | NSString *urlStr = options[@"url"];
72 | NSLog(@"Got downloadFile: %@", options);
73 |
74 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
75 | [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
76 | if (error) {
77 | NSLog(@"Download Error:%@",error.description);
78 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: [error localizedDescription]] callbackId:command.callbackId];
79 | }
80 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
81 | if (httpResponse.statusCode != 200) {
82 | NSString *errorMsg = [NSString stringWithFormat:@"HTTP response status code: %ld", httpResponse.statusCode];
83 | NSLog(@"Download Error: %@", errorMsg);
84 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: errorMsg] callbackId:command.callbackId];
85 | }
86 | if (data) {
87 | [[NSFileManager defaultManager] createDirectoryAtPath:[target stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
88 | [data writeToFile:target atomically:YES];
89 | NSLog(@"File is saved to %@", target);
90 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
91 | }
92 | }] resume];
93 | }
94 |
95 | - (void) getAppInfo:(CDVInvokedUrlCommand*)command {
96 | NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
97 | NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
98 | NSString* platformVersion = [[UIDevice currentDevice] systemVersion];
99 | NSString* versionCode = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"];
100 | NSString* bundleName = [[NSBundle mainBundle] infoDictionary][@"CFBundleIdentifier"];
101 | NSString* versionName = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"];
102 | NSString* uuid = [prefs stringForKey:@"uuid"];
103 | NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
104 | NSString * cordovaDataDirectory = [libPath stringByAppendingPathComponent:@"NoCloud"];
105 |
106 | if (versionName == nil) {
107 | versionName = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"];
108 | if (versionName == nil) {
109 | versionName = @"";
110 | }
111 | }
112 |
113 | json[@"platform"] = @"ios";
114 | json[@"platformVersion"] = platformVersion;
115 | json[@"version"] = versionCode;
116 | json[@"binaryVersionCode"] = versionCode;
117 | json[@"bundleName"] = bundleName;
118 | json[@"bundleVersion"] = versionName;
119 | json[@"binaryVersionName"] = versionName;
120 | json[@"device"] = uuid;
121 | json[@"dataDirectory"] = [[NSURL fileURLWithPath:cordovaDataDirectory] absoluteString];
122 | NSLog(@"Got app info: %@", json);
123 |
124 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:json] callbackId:command.callbackId];
125 |
126 | }
127 |
128 | - (void) getPreferences:(CDVInvokedUrlCommand*)command {
129 | // Get updated preferences if available
130 | NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
131 | NSDictionary *immutableStoredPrefs = [prefs objectForKey:@"ionicDeploySavedPreferences"];
132 | NSMutableDictionary *savedPrefs = [immutableStoredPrefs mutableCopy];
133 | NSMutableDictionary *nativeConfig = [self getNativeConfig];
134 | NSMutableDictionary *customConfig = [self getCustomConfig];
135 |
136 | if (savedPrefs!= nil) {
137 |
138 | NSLog(@"found some saved prefs doing precedence ops: %@", savedPrefs);
139 | // Merge with most up to date Native Settings
140 | [savedPrefs addEntriesFromDictionary:nativeConfig];
141 |
142 | // Merge with any custom settings
143 | [savedPrefs addEntriesFromDictionary:customConfig];
144 |
145 | NSLog(@"Returning saved prefs: %@", savedPrefs);
146 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary: savedPrefs] callbackId:command.callbackId];
147 | return;
148 | }
149 |
150 | // No saved prefs found get them all from config
151 | // Make sure to initialize empty updates object
152 | NSLog(@"initing updates key");
153 | nativeConfig[@"updates"] = [[NSDictionary alloc] init];
154 | NSLog(@"Initialized App Prefs: %@", nativeConfig);
155 |
156 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:nativeConfig] callbackId:command.callbackId];
157 | }
158 |
159 | - (void) setPreferences:(CDVInvokedUrlCommand*)command {
160 | NSDictionary *json = command.arguments[0];
161 | NSLog(@"Got prefs to save: %@", json);
162 | [[NSUserDefaults standardUserDefaults] setObject:json forKey:@"ionicDeploySavedPreferences"];
163 | [[NSUserDefaults standardUserDefaults] synchronize];
164 |
165 | [self getPreferences:command];
166 | }
167 |
168 | - (NSMutableDictionary*) getNativeConfig {
169 | // Get preferences from cordova
170 | NSString *appId = [NSString stringWithFormat:@"%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"IonAppId"]];
171 | NSNumber * disabled = [NSNumber numberWithBool:[[self.commandDelegate.settings objectForKey:[@"DisableDeploy" lowercaseString]] boolValue]];
172 | NSString *host = [NSString stringWithFormat:@"%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"IonApi"]];
173 | NSString *updateMethod = [NSString stringWithFormat:@"%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"IonUpdateMethod"]];
174 | NSString *channel = [NSString stringWithFormat:@"%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"IonChannelName"]];
175 | NSNumber *maxV = [NSNumber numberWithInt:[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"IonMaxVersions"] intValue]];
176 | NSNumber *minBackgroundDuration = [NSNumber numberWithInt:[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"IonMinBackgroundDuration"] intValue]];
177 | NSString* versionCode = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"];
178 | NSString* versionName = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"];
179 |
180 | // Build the preferences json object
181 | NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
182 | json[@"appId"] = appId;
183 | json[@"disabled"] = disabled;
184 | json[@"channel"] = channel;
185 | json[@"host"] = host;
186 | json[@"updateMethod"] = updateMethod;
187 | json[@"maxVersions"] = maxV;
188 | json[@"minBackgroundDuration"] = minBackgroundDuration;
189 | json[@"binaryVersionCode"] = versionCode;
190 | json[@"binaryVersion"] = versionName;
191 | json[@"binaryVersionName"] = versionName;
192 | NSLog(@"Got Native app preferences: %@", json);
193 | return json;
194 | }
195 |
196 | - (NSMutableDictionary*) getCustomConfig {
197 | // Get custom preferences if available
198 | NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
199 | NSDictionary *immutableConfig = [prefs objectForKey:@"ionicDeployCustomPreferences"];
200 | NSMutableDictionary *customConfig = [immutableConfig mutableCopy];
201 | if (customConfig!= nil) {
202 | NSLog(@"Found custom config: %@", customConfig);
203 | return customConfig;
204 | }
205 | NSLog(@"No custom config found");
206 | NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
207 | return json;
208 | }
209 |
210 | - (void) configure:(CDVInvokedUrlCommand *)command {
211 | NSDictionary *newConfig = command.arguments[0];
212 | NSLog(@"Got new config to save: %@", newConfig);
213 | NSMutableDictionary *storedConfig = [self getCustomConfig];
214 | [storedConfig addEntriesFromDictionary:newConfig];
215 | [[NSUserDefaults standardUserDefaults] setObject:storedConfig forKey:@"ionicDeployCustomPreferences"];
216 | [[NSUserDefaults standardUserDefaults] synchronize];
217 |
218 | [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newConfig] callbackId:command.callbackId];
219 | }
220 |
221 | @end
222 |
--------------------------------------------------------------------------------
/tests/test_common.ts:
--------------------------------------------------------------------------------
1 | import * as IonicCordova from '../www/common';
2 |
3 | let mockPluginAPI = {
4 | IonicDeploy: {
5 | },
6 | IonicCordova: {
7 | }
8 | };
9 |
10 | function mockCordova() {
11 | return {exec: execMock()};
12 | };
13 |
14 | function execMock() {
15 | return jest.fn( (success, error, pluginName, method, extras) => {
16 | return mockPluginAPI[pluginName][method](success, error);
17 | });
18 | }
19 |
20 | function callbackMock(returnValue: any, succeed: boolean) {
21 | return jest.fn( (success, error) => {
22 | succeed ? success(returnValue) : error(returnValue);
23 | });
24 | }
25 |
26 | const pluginConfig = {
27 | appId: 'myapp',
28 | disabled: false,
29 | host: 'https://myhost.com',
30 | channel: 'mychannel',
31 | updateMethod: 'auto',
32 | maxVersions: 5,
33 | currentVersionId: 'version1'
34 | };
35 |
36 | describe('IonicCordova', () => {
37 |
38 | beforeEach( () => {
39 | global.cordova = mockCordova();
40 | });
41 |
42 | afterEach( () => {
43 | mockPluginAPI = {
44 | IonicDeploy: {
45 | },
46 | IonicCordova: {
47 | }
48 | };
49 | });
50 |
51 | it('should have a deploy object', async () => {
52 | const pluginBase = IonicCordova;
53 | expect(pluginBase.deploy).toBeDefined();
54 | });
55 |
56 | describe('IonicDeploy', () => {
57 |
58 | afterEach( () => {
59 | mockPluginAPI = {
60 | IonicDeploy: {
61 | },
62 | IonicCordova: {
63 | }
64 | };
65 | });
66 |
67 | it('should have a parent object', async () => {
68 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
69 | const pluginBase = IonicCordova;
70 | expect(pluginBase.deploy._parent).toBeDefined();
71 | });
72 |
73 | it('should set preferences on successful init of deploy plugin', async () => {
74 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
75 | const pluginBase = IonicCordova;
76 | expect(global.cordova.exec.mock.calls.length).toBe(1);
77 | expect(global.cordova.exec.mock.calls[0][2]).toBe('IonicCordova');
78 | expect(global.cordova.exec.mock.calls[0][3]).toBe('getPreferences');
79 | expect(await pluginBase.deploy._pluginConfig).toEqual(pluginConfig);
80 | });
81 |
82 | it('should log returned error on failed initialization of deploy plugin', async () => {
83 | mockPluginAPI.IonicCordova.getPreferences = callbackMock('random failure', false)
84 | const pluginBase = IonicCordova;
85 | expect(global.cordova.exec.mock.calls.length).toBe(1);
86 | expect(global.cordova.exec.mock.calls[0][2]).toBe('IonicCordova');
87 | expect(global.cordova.exec.mock.calls[0][3]).toBe('getPreferences');
88 | expect(pluginBase.deploy._pluginConfig).rejects
89 | });
90 |
91 | describe('init', () => {
92 |
93 | afterEach( () => {
94 | mockPluginAPI = {
95 | IonicDeploy: {
96 | },
97 | IonicCordova: {
98 | }
99 | };
100 | });
101 |
102 | it('should call the failure function when passed a bad config', async done => {
103 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
104 | const pluginBase = IonicCordova;
105 | const badConfig = {
106 | appId: 26
107 | };
108 | const success = function(result) {
109 | expect(true).toEqual(false);
110 | done();
111 | };
112 | const failure = function(err) {
113 | expect(err).toBe('Invalid Config Object');
114 | done();
115 | };
116 | expect(pluginBase.deploy.init(badConfig, success, failure)).toBe(undefined);
117 | }
118 |
119 | it('should update preferences when called', async done => {
120 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
121 | const pluginBase = IonicCordova;
122 | expect(await pluginBase.deploy._pluginConfig).toEqual(pluginConfig);
123 | const newConfig = {
124 | appId: 'newappid',
125 | disabled: true,
126 | host: 'http://newhost.com',
127 | channel: 'newchannel',
128 | }
129 | mockPluginAPI.IonicDeploy.syncPreferences = callbackMock(undefined, true)
130 |
131 |
132 | const success = function(result) {
133 | expect(result).toBeUndefined();
134 | done();
135 | };
136 | const failure = function(err) {
137 | expect(true).toEqual(false);
138 | done();
139 | };
140 | pluginBase.deploy.init(newConfig, success, failure);
141 | const expectedConfig = Object.assign({}, pluginConfig, newConfig);
142 | expect(await pluginBase.deploy._pluginConfig).toEqual(expectedConfig);
143 |
144 | expect(global.cordova.exec.mock.calls.length).toBe(2);
145 | expect(global.cordova.exec.mock.calls[0][2]).toBe('IonicCordova');
146 | expect(global.cordova.exec.mock.calls[0][3]).toBe('getPreferences');
147 | expect(global.cordova.exec.mock.calls[1][2]).toBe('IonicDeploy');
148 | expect(global.cordova.exec.mock.calls[1][3]).toBe('syncPreferences');
149 | });
150 |
151 | it('should update preferences multiple times when called more than once', async done => {
152 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
153 | const pluginBase = IonicCordova;
154 | expect(await pluginBase.deploy._pluginConfig).toEqual(pluginConfig);
155 | mockPluginAPI.IonicDeploy.syncPreferences = callbackMock(undefined, true)
156 |
157 | const success = function(result) {
158 | expect(result).toBeUndefined();
159 | done();
160 | };
161 | const failure = function(err) {
162 | expect(true).toEqual(false);
163 | done();
164 | };
165 | let newConfig = {
166 | channel: 'newchannel',
167 | };
168 | pluginBase.deploy.init(newConfig, success, failure);
169 | let expectedConfig = Object.assign({}, pluginConfig, newConfig);
170 | expect(await pluginBase.deploy._pluginConfig).toEqual(expectedConfig);
171 |
172 | newConfig = {
173 | channel: 'anotherchannel'
174 | };
175 |
176 | expectedConfig = Object.assign({}, pluginConfig, newConfig);
177 | expect(await pluginBase.deploy._pluginConfig).toEqual(expectedConfig);
178 |
179 | expect(global.cordova.exec.mock.calls.length).toBe(3);
180 | expect(global.cordova.exec.mock.calls[0][2]).toBe('IonicCordova');
181 | expect(global.cordova.exec.mock.calls[0][3]).toBe('getPreferences');
182 | expect(global.cordova.exec.mock.calls[1][2]).toBe('IonicDeploy');
183 | expect(global.cordova.exec.mock.calls[1][3]).toBe('syncPreferences');
184 | expect(global.cordova.exec.mock.calls[2][2]).toBe('IonicDeploy');
185 | expect(global.cordova.exec.mock.calls[2][3]).toBe('syncPreferences');
186 |
187 | });
188 |
189 | it('should call failure when initialization has failed to get preferences', async done => {
190 | mockPluginAPI.IonicCordova.getPreferences = callbackMock('some error', false)
191 | const pluginBase = IonicCordova;
192 | const newConfig = {
193 | appId: 'newappid',
194 | disabled: true,
195 | host: 'http://newhost.com',
196 | channel: 'newchannel',
197 | }
198 |
199 | const success = function(succ) {
200 | expect(true).toEqual(false);
201 | done();
202 | };
203 | const fail = function(err) {
204 | expect(err).toEqual('some error');
205 | done();
206 | };
207 | await pluginBase.deploy.init(newConfig, success, fail);
208 | });
209 | }
210 |
211 | describe('configure', () => {
212 |
213 | afterEach( () => {
214 | mockPluginAPI = {
215 | IonicDeploy: {
216 | },
217 | IonicCordova: {
218 | }
219 | };
220 | });
221 |
222 | it('should throw when passed a bad config', async () => {
223 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
224 | const pluginBase = IonicCordova;
225 | const badConfig = {
226 | appId: 26
227 | };
228 | expect(pluginBase.deploy.configure(badConfig)).rejects.toThrow('Invalid Config Object');
229 | }
230 |
231 | it('should update preferences when called', async () => {
232 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
233 | const pluginBase = IonicCordova;
234 | expect(pluginBase.deploy._pluginConfig).resolves.toBe(pluginConfig);
235 | const newConfig = {
236 | appId: 'newappid',
237 | disabled: true,
238 | host: 'http://newhost.com',
239 | channel: 'newchannel',
240 | }
241 | mockPluginAPI.IonicDeploy.syncPreferences = callbackMock(undefined, true)
242 | expect(pluginBase.deploy.configure(newConfig)).resolves.toBe(undefined);
243 | const expectedConfig = Object.assign({}, pluginConfig, newConfig);
244 | expect(pluginBase.deploy._pluginConfig).resolves.toBe(expectedConfig);
245 | }
246 |
247 | it('should reject when initilization has failed to get preferences', async () => {
248 | mockPluginAPI.IonicCordova.getPreferences = callbackMock('some error', false)
249 | const pluginBase = IonicCordova;
250 | const newConfig = {
251 | appId: 'newappid',
252 | disabled: true,
253 | host: 'http://newhost.com',
254 | channel: 'newchannel',
255 | }
256 | expect(pluginBase.deploy.configure(newConfig)).rejects.toThrow('some error');
257 | });
258 |
259 | it('should update preferences multiple times when called more than once', async () => {
260 |
261 | mockPluginAPI.IonicCordova.getPreferences = callbackMock(pluginConfig, true)
262 | const pluginBase = IonicCordova;
263 | expect(pluginBase.deploy._pluginConfig).resolves.toBe(pluginConfig);
264 |
265 | let newConfig = {
266 | channel: 'channel1',
267 | }
268 | mockPluginAPI.IonicDeploy.syncPreferences = callbackMock(undefined, true)
269 | expect(pluginBase.deploy.configure(newConfig)).resolves.toBe(undefined);
270 | let expectedConfig = Object.assign({}, pluginConfig, newConfig);
271 | expect(pluginBase.deploy._pluginConfig).resolves.toBe(expectedConfig);
272 | newConfig = {
273 | channel: 'channel2'
274 | }
275 |
276 | expectedConfig = Object.assign({}, pluginConfig, newConfig);
277 | expect(pluginBase.deploy.configure(newConfig)).resolves.toBe(undefined);
278 | expect(pluginBase.deploy._pluginConfig).resolves.toBe(expectedConfig);
279 | }
280 | });
281 | }
282 | });
283 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowUnreachableCode": false,
4 | "noImplicitAny": true,
5 | "declaration": true,
6 | "experimentalDecorators": true,
7 | "importHelpers": false,
8 | "module": "commonjs",
9 | "outDir": "dist",
10 | "moduleResolution": "node",
11 | "noFallthroughCasesInSwitch": true,
12 | "noUnusedLocals": true,
13 | "pretty": true,
14 | "strict": true,
15 | "target": "es5",
16 | "lib": [
17 | "es2015",
18 | "dom"
19 | ]
20 | },
21 | "include": ["www/common.ts", "www/guards.ts", "www/index.ts"]
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.ng.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "es2015",
5 | "outDir": "dist/ngx",
6 | "rootDir": "www"
7 | },
8 | "include": ["www/ngx/index.ts"],
9 | "angularCompilerOptions": {
10 | "enableIvy": false
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint-ionic-rules",
3 | "rules": {
4 | "eofline": false
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/www/IonicCordova.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | interface Window {
3 | IonicCordova: IPluginBaseAPI;
4 | }
5 | type IDeployPluginAPI = DeployPluginAPI;
6 | }
7 |
8 | /**
9 | * The Public API for the deploy Plugin
10 | */
11 | export interface DeployPluginAPI {
12 |
13 | /**
14 | * @description Update the default configuration for the plugin on the current device. The new configuration will be persisted across app close and binary updates.
15 | *
16 | * @since v5.0.0
17 | *
18 | * @param config The new configuration for the plugin on this device.
19 | */
20 | configure(config: IDeployConfig): Promise;
21 |
22 | /**
23 | * @description Get the current configuration for the plugin on the current device.
24 | *
25 | * @since v5.0.0
26 | *
27 | * @return The current configuration of the plugin.
28 | */
29 | getConfiguration(): Promise;
30 |
31 | /**
32 | * @description Check for available updates for the currently configured app id and channel.
33 | *
34 | * @since v5.0.0
35 | *
36 | * @return A response describing an update if one is available.
37 | */
38 | checkForUpdate(): Promise;
39 |
40 | /**
41 | * @description Download the new files from an available update found by the checkForUpdate method and prepare the update.
42 | *
43 | * @since v5.0.0
44 | *
45 | * @param progress A progress callback function which will be called with a number representing the percent of completion of the download and prepare.
46 | *
47 | * @return true if the download succeeded
48 | */
49 | downloadUpdate(progress?: CallbackFunction): Promise;
50 |
51 | /**
52 | * @description Extract a downloaded bundle of updated files.
53 | *
54 | * @since v5.0.0
55 | *
56 | * @param progress A progress callback function which will be called with a number representing the percent of completion of the extract.
57 | *
58 | * @return true if the extract succeeded
59 | */
60 | extractUpdate(progress?: CallbackFunction): Promise;
61 |
62 | /**
63 | * @description Reload the app if a more recent version of the app is available.
64 | *
65 | * @since v5.0.0
66 | *
67 | * @return true if the reload succeeded
68 | */
69 | reloadApp(): Promise;
70 |
71 | /**
72 | * @description Check for an update, download it, and apply it in one step.
73 | *
74 | * @since v5.0.0
75 | *
76 | * @param syncOptions (Optional) Application update overrides.
77 | *
78 | * @param progress (Optional) A callback which will recieve progress updates
79 | *
80 | * @return The info about the currently applied update or undefined if none is applied.
81 | */
82 | sync(syncOptions: ISyncOptions, progress?: CallbackFunction): Promise;
83 |
84 | /**
85 | *
86 | * @description Get info about the currently deployed update or undefined if none are applied.
87 | *
88 | * @since v5.0.0
89 | *
90 | * @return The info about the currently applied update or undefined if none is applied.
91 | */
92 | getCurrentVersion(): Promise;
93 |
94 | /**
95 | * @description Get a list of the snapshots available on the device.
96 | *
97 | * @since v5.0.0
98 | *
99 | * @return a list of available updates.
100 | */
101 | getAvailableVersions(): Promise;
102 |
103 | /**
104 | * @description Remove the files specific to a snapshot from the device.
105 | *
106 | * @param version The versionId
107 | *
108 | * @return true if the update was deleted.
109 | */
110 | deleteVersionById(versionId: string): Promise;
111 |
112 | /**
113 | * @description Returns info specific to a snapshot from the device.
114 | *
115 | * @param version The versionId
116 | *
117 | * @return Returns info specific to a snapshot from the device.
118 | *
119 | */
120 | getVersionById(versionId: string): Promise;
121 | }
122 |
123 | /**
124 | * The IonicCordova Plugin API
125 | */
126 | export interface IPluginBaseAPI {
127 | /**
128 | *
129 | * @param success
130 | * @param failure
131 | */
132 | getAppInfo(success: Function, failure: Function): void;
133 |
134 | /**
135 | * @description Get info about the current app.
136 | *
137 | */
138 | getAppDetails(): Promise;
139 |
140 | /**
141 | * An instance of the Ionic Deploy Plugin API
142 | */
143 | deploy: IDeployPluginAPI;
144 | }
145 |
146 | /**
147 | * The configuration for the deploy plugin on the device.
148 | */
149 | export interface IDeployConfig {
150 |
151 | /**
152 | * The [Ionic Pro](https://ionicframework.com/docs/pro/) app id.
153 | */
154 | appId?: string;
155 |
156 | /**
157 | * whether or not the app should in debug mode
158 | */
159 | debug?: boolean;
160 |
161 | /**
162 | * @ignore
163 | */
164 | host?: string;
165 |
166 | /**
167 | * The [channel](https://ionicframework.com/docs/pro/deploy/channels) that the plugin should listen for updates on.
168 | */
169 | channel?: string;
170 |
171 | /**
172 | * The number of previous updates to be cached on the device
173 | */
174 | maxVersions?: number;
175 |
176 | /**
177 | * The number of seconds the app should be in the background for before the plugin considers it closed
178 | * and checks for an updated on resume of the app.
179 | */
180 | minBackgroundDuration?: number;
181 |
182 | /**
183 | * The update method the app should use when checking for available updates
184 | */
185 | updateMethod?: 'none' | 'auto' | 'background';
186 | }
187 |
188 | /**
189 | * The current configuration for the deploy plugin on the device.
190 | */
191 | export interface ICurrentConfig {
192 | /**
193 | * The [Ionic Pro](https://ionicframework.com/docs/pro/) app id.
194 | */
195 | appId: string;
196 |
197 | /**
198 | * The [channel](https://ionicframework.com/docs/pro/deploy/channels) that the plugin should listen for updates on.
199 | */
200 | channel: string;
201 |
202 | /**
203 | * @deprecated
204 | * The binary version of the native bundle versionName on Android or CFBundleShortVersionString on iOS
205 | * deprecated in favor of versionName
206 | */
207 | binaryVersion: string;
208 |
209 | /**
210 | * The binary version of the native bundle versionName on Android or CFBundleShortVersionString on iOS
211 | */
212 | binaryVersionName: string;
213 |
214 | /**
215 | * The build version code of the native bundle versionCode on Android or CFBundleVersion on iOS
216 | */
217 | binaryVersionCode: string;
218 |
219 | /**
220 | * Whether the user disabled deploy updates or not.
221 | */
222 | disabled: boolean;
223 |
224 | /**
225 | * The host API the plugin is configured to check for updates from.
226 | */
227 | host: string;
228 |
229 | /**
230 | * The currently configured updateMethod for the plugin.
231 | */
232 | updateMethod: 'none' | 'auto' | 'background';
233 |
234 | /**
235 | * The maximum number of updates to be stored locally on the device.
236 | */
237 | maxVersions: number;
238 |
239 | /**
240 | * The number of seconds the app needs to be in the background before the plugin considers it
241 | * closed for the purposes of fetching and applying a new update.
242 | */
243 | minBackgroundDuration: number;
244 |
245 | /**
246 | * The id of the currently applied updated or undefined if none is applied.
247 | */
248 | currentVersionId?: string;
249 |
250 | /**
251 | * The id of the currently applied build or undefined if none is applied.
252 | */
253 | currentBuildId?: string;
254 | }
255 |
256 | /**
257 | * Information about a snapshot
258 | */
259 | export interface ISnapshotInfo {
260 |
261 | /**
262 | * @deprecated in favor of [versionId](#versionid)
263 | *
264 | * The id for the snapshot.
265 | */
266 | deploy_uuid: string;
267 |
268 | /**
269 | * The id for the snapshot.
270 | */
271 | versionId: string;
272 |
273 | /**
274 | * The id for the snapshot.
275 | */
276 | buildId: string;
277 |
278 | /**
279 | * The channel that the snapshot was downloaded for..
280 | */
281 | channel: string;
282 |
283 | /**
284 | * @deprecated in favor of [binaryVersion](#binaryversion)
285 | *
286 | * The binary version the snapshot was downloaded for.
287 | */
288 | binary_version: string;
289 |
290 | /**
291 | * @deprecated
292 | * The binary version the snapshot was downloaded for.
293 | * The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
294 | */
295 | binaryVersion: string;
296 |
297 | /**
298 | * The binary version name the snapshot was downloaded for.
299 | * The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
300 | */
301 | binaryVersionName: string;
302 |
303 | /**
304 | * The binary version build code the snapshot was downloaded for.
305 | * The versionCode on Android or CFBundleVersion on iOS this should be changed every time you do a new build debug or otherwise.
306 | */
307 | binaryVersionCode: string;
308 | }
309 |
310 | /**
311 | * Configuration options for the call to `sync`
312 | */
313 | export interface ISyncOptions {
314 | /**
315 | * Whether the update should be applied immediately or on the next app start.
316 | */
317 | updateMethod?: 'background' | 'auto';
318 | }
319 |
320 | /**
321 | * The response object describing if an update is available.
322 | */
323 | export interface CheckForUpdateResponse {
324 | /**
325 | * Whether or not an update is available.
326 | */
327 | available: boolean;
328 |
329 | /**
330 | * Equivalent to available since v5 this can be ignored in favor of available
331 | * @deprecated
332 | */
333 | compatible: boolean;
334 |
335 | /**
336 | * Legacy indicator of whether the update is a partial one. This will always be false and can be ignored
337 | * @deprecated
338 | */
339 | partial: false;
340 |
341 | /**
342 | * The id of the snapshot if available.
343 | */
344 | snapshot?: string;
345 |
346 | /**
347 | * The id of the build if available.
348 | */
349 | build?: string;
350 |
351 | /**
352 | * The url to fetch the manifest of files in the update.
353 | */
354 | url?: string;
355 |
356 | /**
357 | * Whether or not there is an update available that is not compatible with this device.
358 | */
359 | incompatibleUpdateAvailable?: boolean;
360 | }
361 |
362 | /**
363 | * Information about the application.
364 | */
365 | export interface IAppInfo {
366 |
367 | /**
368 | * The platform that the app is currently installed on.
369 | */
370 | platform: 'ios' | 'android';
371 |
372 | /**
373 | * The version of the native platform.
374 | */
375 | platformVersion: string;
376 |
377 | /**
378 | * @deprecated
379 | * The versionCode on Android or CFBundleVersion on iOS this should be changed every time you do a new build debug or otherwise.
380 | */
381 | version: string;
382 |
383 | /**
384 | * The versionCode on Android or CFBundleVersion on iOS this should be changed every time you do a new build debug or otherwise.
385 | */
386 | binaryVersionCode: string | number;
387 |
388 | /**
389 | * The bundle name.
390 | */
391 | bundleName: string;
392 |
393 | /**
394 | * @deprecated
395 | * The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
396 | */
397 | bundleVersion: string;
398 |
399 | /**
400 | * The versionName on Android or CFBundleShortVersionString on iOS this is the end user readable version listed on the stores.
401 | */
402 | binaryVersionName: string;
403 |
404 | /**
405 | * A generated device ID (NOT a native device ID)
406 | */
407 | device: string;
408 |
409 | /**
410 | * Directory where the snapshots are stored
411 | */
412 | dataDirectory: string;
413 |
414 | /**
415 | * Directory where the application files are stored
416 | */
417 | applicationDirectory: string;
418 | }
419 |
420 | /**
421 | * A callback function to handle the result.
422 | */
423 | export interface CallbackFunction { (result?: T): void; }
424 |
--------------------------------------------------------------------------------
/www/common.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import {
4 | CallbackFunction,
5 | CheckForUpdateResponse,
6 | IAppInfo,
7 | ICurrentConfig,
8 | IDeployConfig,
9 | IPluginBaseAPI,
10 | ISnapshotInfo,
11 | ISyncOptions,
12 | } from './IonicCordova';
13 |
14 | declare const cordova: Cordova;
15 |
16 | const channel = cordova.require('cordova/channel');
17 | channel.createSticky('onIonicProReady');
18 | channel.waitForInitialization('onIonicProReady');
19 |
20 | declare const Ionic: any;
21 | declare const WEBVIEW_SERVER_URL: string;
22 | declare const Capacitor: any;
23 |
24 | enum UpdateMethod {
25 | BACKGROUND = 'background',
26 | AUTO = 'auto',
27 | NONE = 'none',
28 | }
29 |
30 | enum UpdateState {
31 | Available = 'available',
32 | Pending = 'pending',
33 | Ready = 'ready',
34 | }
35 |
36 | import {
37 | FetchManifestResp, IAvailableUpdate,
38 | ISavedPreferences,
39 | ManifestFileEntry,
40 | } from './definitions';
41 |
42 | import {
43 | isPluginConfig
44 | } from './guards';
45 |
46 |
47 | class Path {
48 | static join(...paths: string[]): string {
49 | let fullPath: string = paths.shift() || '';
50 | for (const path of paths) {
51 | if (fullPath && fullPath.slice(-1) !== '/') {
52 | fullPath += '/';
53 | }
54 | fullPath = path.slice(0, 1) !== '/' ? fullPath + path : fullPath + path.slice(1);
55 | }
56 | return fullPath;
57 | }
58 | }
59 |
60 | /**
61 | * LIVE UPDATE API
62 | *
63 | * The plugin API for the live updates feature.
64 | */
65 |
66 | class IonicDeployImpl {
67 |
68 | private readonly appInfo: IAppInfo;
69 | private _savedPreferences: ISavedPreferences;
70 | private _fileManager: FileManager = new FileManager();
71 | private SNAPSHOT_CACHE = 'ionic_built_snapshots';
72 | private MANIFEST_FILE = 'pro-manifest.json';
73 | public PLUGIN_VERSION = '5.5.3';
74 |
75 | constructor(appInfo: IAppInfo, preferences: ISavedPreferences) {
76 | this.appInfo = appInfo;
77 | this._savedPreferences = preferences;
78 | }
79 |
80 | async _handleInitialPreferenceState() {
81 | // make sure we're not going to redirect to a stale version
82 | await this.cleanupStaleVersions();
83 | const isOnline = navigator && navigator.onLine;
84 | if (!isOnline) {
85 | console.warn('The device appears to be offline. Loading last available version and skipping update checks.');
86 | this.reloadApp();
87 | return;
88 | }
89 |
90 | const updateMethod = this._savedPreferences.updateMethod;
91 | switch (updateMethod) {
92 | case UpdateMethod.AUTO:
93 | // NOTE: call sync with background as override to avoid sync
94 | // reloading the app and manually reload always once sync has
95 | // set the correct currentVersionId
96 | console.log('calling _sync');
97 | try {
98 | await this.sync({updateMethod: UpdateMethod.BACKGROUND});
99 | } catch (e) {
100 | console.warn(e);
101 | console.warn('Sync failed. Defaulting to last available version.');
102 | }
103 | console.log('calling _reload');
104 | await this.reloadApp();
105 | console.log('done _reloading');
106 | break;
107 | case UpdateMethod.NONE:
108 | this.reloadApp();
109 | break;
110 | default:
111 | // NOTE: default anything that doesn't explicitly match to background updates
112 | await this.reloadApp();
113 | try {
114 | this.sync({updateMethod: UpdateMethod.BACKGROUND});
115 | } catch (e) {
116 | console.warn(e);
117 | console.warn('Background sync failed. Unable to check for new updates.');
118 | }
119 | return;
120 | }
121 | }
122 |
123 | getSnapshotCacheDir(versionId: string): string {
124 | return new URL(Path.join(this.appInfo.dataDirectory, this.SNAPSHOT_CACHE, versionId)).pathname;
125 | }
126 |
127 | getBundledAppDir(): string {
128 | let folder = 'www';
129 | if (typeof (Capacitor) !== 'undefined') {
130 | folder = 'public';
131 | }
132 | return folder;
133 | }
134 |
135 | private async _savePrefs(prefs: ISavedPreferences): Promise {
136 | return new Promise(async (resolve, reject) => {
137 | try {
138 | cordova.exec(async (savedPrefs: ISavedPreferences) => {
139 | resolve(savedPrefs);
140 | }, reject, 'IonicCordovaCommon', 'setPreferences', [prefs]);
141 | } catch (e) {
142 | reject(e.message);
143 | }
144 | });
145 | }
146 |
147 | async configure(config: IDeployConfig) {
148 | if (!isPluginConfig(config)) {
149 | throw new Error('Invalid Config Object');
150 | }
151 | await new Promise((resolve, reject) => {
152 | cordova.exec(resolve, reject, 'IonicCordovaCommon', 'configure', [config]);
153 | });
154 | Object.assign(this._savedPreferences, config);
155 | this._savePrefs(this._savedPreferences);
156 | }
157 |
158 | async checkForUpdate(): Promise {
159 | const isOnline = navigator && navigator.onLine;
160 | if (!isOnline) {
161 | throw new Error('The device is offline.');
162 | }
163 | const prefs = this._savedPreferences;
164 | const appInfo = this.appInfo;
165 | const endpoint = `${prefs.host}/apps/${prefs.appId}/channels/check-device`;
166 |
167 | const device_details = {
168 | binary_version: prefs.binaryVersionName,
169 | device_id: appInfo.device || null,
170 | platform: appInfo.platform,
171 | platform_version: appInfo.platformVersion,
172 | snapshot: prefs.currentVersionId,
173 | build: prefs.currentBuildId
174 | };
175 |
176 | const body = {
177 | channel_name: prefs.channel,
178 | app_id: prefs.appId,
179 | device: device_details,
180 | plugin_version: this.PLUGIN_VERSION,
181 | manifest: true
182 | };
183 |
184 | const timeout = new Promise( (resolve, reject) => {
185 | setTimeout(reject, 5000, 'Request timed out. The device maybe offline.');
186 | });
187 | const request = fetch(endpoint, {
188 | method: 'POST',
189 | headers: new Headers({
190 | 'Content-Type': 'application/json'
191 | }),
192 | body: JSON.stringify(body)
193 | });
194 |
195 | const resp = await (Promise.race([timeout, request]) as Promise);
196 |
197 | let jsonResp;
198 | if (resp.status < 500) {
199 | jsonResp = await resp.json();
200 | }
201 | if (resp.ok) {
202 | const checkForUpdateResp: CheckForUpdateResponse = jsonResp.data;
203 | if (checkForUpdateResp.available && checkForUpdateResp.url && checkForUpdateResp.snapshot && checkForUpdateResp.build) {
204 | prefs.availableUpdate = {
205 | binaryVersionCode: prefs.binaryVersionCode,
206 | binaryVersionName: prefs.binaryVersionName,
207 | channel: prefs.channel,
208 | state: UpdateState.Available,
209 | lastUsed: new Date().toISOString(),
210 | url: checkForUpdateResp.url,
211 | versionId: checkForUpdateResp.snapshot,
212 | buildId: checkForUpdateResp.build
213 | };
214 | await this._savePrefs(prefs);
215 | }
216 | return checkForUpdateResp;
217 | }
218 |
219 | throw new Error(`Error Status ${resp.status}: ${jsonResp ? jsonResp.error.message : await resp.text()}`);
220 | }
221 |
222 | async downloadUpdate(progress?: CallbackFunction): Promise {
223 | const prefs = this._savedPreferences;
224 | if (prefs.availableUpdate && prefs.availableUpdate.state === UpdateState.Available) {
225 | const { fileBaseUrl, manifestJson } = await this._fetchManifest(prefs.availableUpdate.url);
226 | const diffedManifest = await this._diffManifests(manifestJson);
227 | await this.prepareUpdateDirectory(prefs.availableUpdate.versionId);
228 | await this._downloadFilesFromManifest(fileBaseUrl, diffedManifest, prefs.availableUpdate.versionId, progress);
229 | prefs.availableUpdate.state = UpdateState.Pending;
230 | await this._savePrefs(prefs);
231 | return true;
232 | }
233 | return false;
234 | }
235 |
236 | private async _downloadFilesFromManifest(baseUrl: string, manifest: ManifestFileEntry[], versionId: string, progress?: CallbackFunction) {
237 | console.log('Downloading update...');
238 | let size = 0, downloaded = 0;
239 | manifest.forEach(i => {
240 | size += i.size;
241 | });
242 |
243 | const beforeDownloadTimer = new Timer('downloadTimer');
244 | const downloadFile = async (file: ManifestFileEntry) => {
245 | const base = new URL(baseUrl);
246 | const newUrl = new URL(file.href, baseUrl);
247 | newUrl.search = base.search;
248 | const filePath = Path.join(this.getSnapshotCacheDir(versionId), file.href);
249 | await this._fileManager.downloadAndWriteFile(newUrl.toString(), filePath);
250 | // Update progress
251 | downloaded += file.size;
252 | if (progress) {
253 | progress(Math.floor((downloaded / size) * 100));
254 | }
255 | };
256 |
257 | let downloads = [];
258 | let count = 0;
259 | console.log(`About to download ${manifest.length} new files for update.`);
260 | const maxBatch = 20;
261 | let numberBatches = Math.round(manifest.length / maxBatch);
262 | if (manifest.length % maxBatch !== 0) {
263 | numberBatches = numberBatches + 1;
264 | }
265 | for (const entry of manifest) {
266 | if (downloads.length >= maxBatch) {
267 | count++;
268 | await Promise.all(downloads);
269 | beforeDownloadTimer.diff(`downloaded batch ${count} of ${numberBatches} downloads. Done downloading ${count * maxBatch} of ${manifest.length} files`);
270 | downloads = [];
271 | }
272 | downloads.push(downloadFile(entry));
273 | }
274 | if (downloads.length) {
275 | count++;
276 | await Promise.all(downloads);
277 | beforeDownloadTimer.diff(`downloaded batch ${count} of ${numberBatches} downloads. Done downloading all ${manifest.length} files`);
278 | }
279 | beforeDownloadTimer.end(`Downloaded ${manifest.length} files`);
280 | }
281 |
282 | private async _fetchManifest(url: string): Promise {
283 | const resp = await fetch(url, {
284 | method: 'GET',
285 | redirect: 'follow',
286 | });
287 | return {
288 | fileBaseUrl: resp.url,
289 | manifestJson: await resp.json()
290 | };
291 | }
292 |
293 | private async _diffManifests(newManifest: ManifestFileEntry[]) {
294 | try {
295 | const manifestResp = await fetch(`${WEBVIEW_SERVER_URL}/${this.MANIFEST_FILE}`);
296 | const bundledManifest: ManifestFileEntry[] = await manifestResp.json();
297 | const bundleManifestStrings = bundledManifest.map(entry => JSON.stringify(entry));
298 | return newManifest.filter(entry => bundleManifestStrings.indexOf(JSON.stringify(entry)) === -1);
299 | } catch (e) {
300 | return newManifest;
301 | }
302 | }
303 |
304 | private async prepareUpdateDirectory(versionId: string) {
305 | await this._cleanSnapshotDir(versionId);
306 | console.log('Cleaned version directory');
307 |
308 | await this._copyBaseAppDir(versionId);
309 | console.log('Copied base app resources');
310 | }
311 |
312 | async extractUpdate(progress?: CallbackFunction): Promise {
313 | const prefs = this._savedPreferences;
314 | if (!prefs.availableUpdate || prefs.availableUpdate.state !== UpdateState.Pending) {
315 | return false;
316 | }
317 |
318 | if (progress) {
319 | progress(100);
320 | }
321 |
322 | prefs.availableUpdate.state = UpdateState.Ready;
323 | prefs.updates[prefs.availableUpdate.versionId] = prefs.availableUpdate;
324 | await this._savePrefs(prefs);
325 | return true;
326 | }
327 |
328 | async reloadApp(): Promise {
329 | const prefs = this._savedPreferences;
330 |
331 | // Save the current update if it's ready
332 | if (prefs.availableUpdate && prefs.availableUpdate.state === UpdateState.Ready) {
333 | prefs.currentVersionId = prefs.availableUpdate.versionId;
334 | prefs.currentBuildId = prefs.availableUpdate.buildId;
335 | delete prefs.availableUpdate;
336 | await this._savePrefs(prefs);
337 | }
338 |
339 | // Is there a non-binary version deployed?
340 | if (prefs.currentVersionId) {
341 | // Are we already running the deployed version?
342 | if (await this._isRunningVersion(prefs.currentVersionId)) {
343 | console.log(`Already running version ${prefs.currentVersionId}`);
344 | await this._savePrefs(prefs);
345 | channel.onIonicProReady.fire();
346 | Ionic.WebView.persistServerBasePath();
347 | await this.cleanupVersions();
348 | return false;
349 | }
350 |
351 | // Is the current version on the device?
352 | if (!(prefs.currentVersionId in prefs.updates)) {
353 | console.error(`Missing version ${prefs.currentVersionId}`);
354 | channel.onIonicProReady.fire();
355 | return false;
356 | }
357 |
358 | // Reload the webview
359 | const newLocation = this.getSnapshotCacheDir(prefs.currentVersionId);
360 | Ionic.WebView.setServerBasePath(newLocation);
361 | return true;
362 | }
363 |
364 | channel.onIonicProReady.fire();
365 | return false;
366 | }
367 |
368 | // compare an update to the current version using both name & code
369 | private isUpdateForCurrentBinary(update: IAvailableUpdate) {
370 | const currentVersionCode = this._savedPreferences.binaryVersionCode;
371 | const currentVersionName = this._savedPreferences.binaryVersionName;
372 | console.log(`Current: versionCode: ${currentVersionCode} versionName: ${currentVersionName}`);
373 | console.log(`update: versionCode: ${update.binaryVersionCode} versionName: ${update.binaryVersionName}`);
374 | return update.binaryVersionName === currentVersionName && update.binaryVersionCode === currentVersionCode;
375 | }
376 |
377 | private isUpdateCurrentlyInstalled(update: IAvailableUpdate) {
378 | return this._savedPreferences.currentVersionId === update.versionId;
379 | }
380 |
381 | private async cleanupStaleVersions() {
382 | const updates = this.getStoredUpdates();
383 | const prefs = this._savedPreferences;
384 |
385 | for (const update of updates) {
386 | // Is the version built from a previous binary?
387 | if (!this.isUpdateForCurrentBinary(update) && !(await this._isRunningVersion(update.versionId))) {
388 | console.log(
389 | `Update ${update.versionId} was built for different binary version removing update from device` +
390 | `Update binaryVersionName: ${update.binaryVersionName}, Device binaryVersionName ${prefs.binaryVersionName}` +
391 | `Update binaryVersionCode: ${update.binaryVersionCode}, Device binaryVersionCode ${prefs.binaryVersionCode}`
392 | );
393 |
394 | // This is no longer necessary for this function, but a previous version of the code
395 | // deleted `prefs.currentVersionId` near initialization so other code may rely on still
396 | // deleting `prefs.currentVersionId`.
397 | if (this.isUpdateCurrentlyInstalled(update)) {
398 | delete prefs.currentVersionId;
399 | }
400 |
401 | await this.deleteVersionById(update.versionId);
402 | }
403 | }
404 | }
405 |
406 | private async _isRunningVersion(versionId: string) {
407 | const currentPath = await this._getServerBasePath();
408 | return currentPath.includes(versionId);
409 | }
410 |
411 | private async _getServerBasePath(): Promise {
412 | return new Promise( async (resolve, reject) => {
413 | try {
414 | Ionic.WebView.getServerBasePath(resolve);
415 | } catch (e) {
416 | reject(e);
417 | }
418 | });
419 | }
420 |
421 | private async _cleanSnapshotDir(versionId: string) {
422 | const timer = new Timer('CleanSnapshotDir');
423 | const snapshotDir = this.getSnapshotCacheDir(versionId);
424 | try {
425 | await this._fileManager.remove(snapshotDir);
426 | timer.end();
427 | } catch (e) {
428 | console.log('No directory found for snapshot no need to delete');
429 | timer.end();
430 | }
431 | }
432 |
433 | private async _copyBaseAppDir(versionId: string) {
434 | const timer = new Timer('CopyBaseApp');
435 | await this._fileManager.copyTo({
436 | source: {
437 | path: this.getBundledAppDir(),
438 | directory: 'APPLICATION',
439 | },
440 | target: this.getSnapshotCacheDir(versionId),
441 | });
442 | timer.end();
443 | }
444 |
445 | async getCurrentVersion(): Promise {
446 | const versionId = this._savedPreferences.currentVersionId;
447 | if (typeof versionId === 'string') {
448 | return this.getVersionById(versionId);
449 | }
450 | return;
451 | }
452 |
453 | async getVersionById(versionId: string): Promise {
454 | const update = this._savedPreferences.updates[versionId];
455 | if (!update) {
456 | return;
457 | }
458 | return this._convertToSnapshotInfo(update);
459 | }
460 |
461 | private _convertToSnapshotInfo(update: IAvailableUpdate): ISnapshotInfo {
462 | return {
463 | deploy_uuid: update.versionId,
464 | versionId: update.versionId,
465 | buildId: update.buildId,
466 | channel: update.channel,
467 | binary_version: update.binaryVersionName,
468 | binaryVersion: update.binaryVersionName,
469 | binaryVersionCode: update.binaryVersionCode,
470 | binaryVersionName: update.binaryVersionName
471 | };
472 | }
473 |
474 | async getAvailableVersions(): Promise {
475 | return Object.keys(this._savedPreferences.updates).map(k => this._convertToSnapshotInfo(this._savedPreferences.updates[k]));
476 | }
477 |
478 | async deleteVersionById(versionId: string): Promise {
479 | const prefs = this._savedPreferences;
480 |
481 | delete prefs.updates[versionId];
482 | await this._savePrefs(prefs);
483 |
484 | // delete snapshot directory
485 | await this._cleanSnapshotDir(versionId);
486 |
487 | return true;
488 | }
489 |
490 | private getStoredUpdates() {
491 | // get an array of stored updates
492 | const prefs = this._savedPreferences;
493 | const updates = [];
494 | for (const versionId of Object.keys(prefs.updates)) {
495 | updates.push(prefs.updates[versionId]);
496 | }
497 | return updates;
498 | }
499 |
500 | private async cleanupVersions() {
501 | await this.cleanupStaleVersions();
502 |
503 | const prefs = this._savedPreferences;
504 | // get updates which now have no stale versions
505 | // filter out the current running version
506 | // clean down to Max Updates stored
507 |
508 | const updatesToDelete = this.getStoredUpdates().filter((a) => !this.isUpdateCurrentlyInstalled(a))
509 | .sort((a, b) => a.lastUsed.localeCompare(b.lastUsed))
510 | .reverse()
511 | .slice(prefs.maxVersions);
512 |
513 | for (const update of updatesToDelete) {
514 | await this.deleteVersionById(update.versionId);
515 | }
516 | }
517 |
518 | async sync(syncOptions: ISyncOptions = {}, progress?: CallbackFunction): Promise {
519 | const prefs = this._savedPreferences;
520 |
521 | // TODO: Get API override if present?
522 | const updateMethod = syncOptions.updateMethod || prefs.updateMethod;
523 |
524 | const wrappedProgress = progress ? (complete?: number) => {
525 | progress(complete);
526 | } : undefined;
527 |
528 | await this.checkForUpdate();
529 |
530 | if (prefs.availableUpdate) {
531 | if (prefs.availableUpdate.state === UpdateState.Available) {
532 | await this.downloadUpdate(wrappedProgress);
533 | }
534 | if (prefs.availableUpdate.state === UpdateState.Pending) {
535 | // ignore progress from this since it's trivial
536 | await this.extractUpdate();
537 | }
538 | if (prefs.availableUpdate.state === UpdateState.Ready && updateMethod === UpdateMethod.AUTO) {
539 | await this.reloadApp();
540 | }
541 | }
542 |
543 | if (prefs.currentVersionId && prefs.currentBuildId) {
544 | return {
545 | deploy_uuid: prefs.currentVersionId,
546 | versionId: prefs.currentVersionId,
547 | buildId: prefs.currentBuildId,
548 | channel: prefs.channel,
549 | binary_version: prefs.binaryVersionName,
550 | binaryVersion: prefs.binaryVersionName,
551 | binaryVersionCode: prefs.binaryVersionCode,
552 | binaryVersionName: prefs.binaryVersionName
553 | };
554 | }
555 | return;
556 | }
557 | }
558 |
559 | class FileManager {
560 | async copyTo(options: { source: { directory: string; path: string; } , target: string}) {
561 | return new Promise( (resolve, reject) => {
562 | cordova.exec(resolve, reject, 'IonicCordovaCommon', 'copyTo', [options]);
563 | });
564 | }
565 |
566 | async remove(path: string) {
567 | return new Promise( (resolve, reject) => {
568 | cordova.exec(resolve, reject, 'IonicCordovaCommon', 'remove', [{target: path}]);
569 | });
570 | }
571 |
572 | async downloadAndWriteFile(url: string, path: string) {
573 | return new Promise( (resolve, reject) => {
574 | cordova.exec(resolve, reject, 'IonicCordovaCommon', 'downloadFile', [{url, target: path}]);
575 | });
576 | }
577 | }
578 |
579 |
580 |
581 | class IonicDeploy implements IDeployPluginAPI {
582 | private parent: IPluginBaseAPI;
583 | private delegate: Promise;
584 | private fetchIsAvailable: boolean;
585 | private lastPause = 0;
586 | private minBackgroundDuration = 10;
587 | private disabled = false;
588 |
589 | constructor(parent: IPluginBaseAPI) {
590 | this.parent = parent;
591 | this.delegate = this.initialize();
592 | this.fetchIsAvailable = typeof(fetch) === 'function';
593 | document.addEventListener('deviceready', this.onLoad.bind(this));
594 | }
595 |
596 | async initialize() {
597 | const preferences = await this._initPreferences();
598 | this.minBackgroundDuration = preferences.minBackgroundDuration;
599 | this.disabled = preferences.disabled || !this.fetchIsAvailable;
600 | const appInfo = await this.parent.getAppDetails();
601 | const delegate = new IonicDeployImpl(appInfo, preferences);
602 | // Only initialize start the plugin if fetch is available and DisableDeploy preference is false
603 | if (this.disabled) {
604 | let disabledMessage = 'cordova-plugin-ionic has been disabled.';
605 | if (!this.fetchIsAvailable) {
606 | disabledMessage = 'Fetch is unavailable so ' + disabledMessage;
607 | }
608 | console.warn(disabledMessage);
609 | channel.onIonicProReady.fire();
610 | } else {
611 | await delegate._handleInitialPreferenceState();
612 | }
613 |
614 | return delegate;
615 | }
616 |
617 | async onLoad() {
618 | document.addEventListener('pause', this.onPause.bind(this));
619 | document.addEventListener('resume', this.onResume.bind(this));
620 | await this.onResume();
621 | }
622 |
623 | async onPause() {
624 | this.lastPause = Date.now();
625 | }
626 |
627 | async onResume() {
628 | if (!this.disabled && this.lastPause && this.minBackgroundDuration && Date.now() - this.lastPause > this.minBackgroundDuration * 1000) {
629 | await (await this.delegate)._handleInitialPreferenceState();
630 | }
631 | }
632 |
633 | async _initPreferences(): Promise {
634 | return new Promise(async (resolve, reject) => {
635 | try {
636 | channel.onNativeReady.subscribe(async () => {
637 | // timeout to let browser proxy to init
638 | window.setTimeout(function () {
639 | cordova.exec(async (prefs: ISavedPreferences) => {
640 | resolve(prefs);
641 | }, reject, 'IonicCordovaCommon', 'getPreferences');
642 | }, 0);
643 | });
644 | } catch (e) {
645 | channel.onIonicProReady.fire();
646 | reject(e.message);
647 | }
648 | });
649 | }
650 |
651 | async checkForUpdate(): Promise {
652 | if (!this.disabled) {
653 | return (await this.delegate).checkForUpdate();
654 | }
655 | return {available: false, compatible: false, partial: false};
656 | }
657 |
658 | async configure(config: IDeployConfig): Promise {
659 | if (!this.disabled) return (await this.delegate).configure(config);
660 | }
661 |
662 | async getConfiguration(): Promise {
663 | return new Promise(async (resolve, reject) => {
664 | try {
665 | cordova.exec(async (prefs: ISavedPreferences) => {
666 | if (prefs.availableUpdate) {
667 | delete prefs.availableUpdate;
668 | }
669 | if (prefs.updates) {
670 | delete prefs.updates;
671 | }
672 | resolve(prefs);
673 | }, reject, 'IonicCordovaCommon', 'getPreferences');
674 | } catch (e) {
675 | reject(e.message);
676 | }
677 | });
678 | }
679 |
680 | async deleteVersionById(version: string): Promise {
681 | if (!this.disabled) return (await this.delegate).deleteVersionById(version);
682 | return true;
683 | }
684 |
685 | async downloadUpdate(progress?: CallbackFunction): Promise {
686 | if (!this.disabled) return (await this.delegate).downloadUpdate(progress);
687 | return false;
688 | }
689 |
690 | async extractUpdate(progress?: CallbackFunction): Promise {
691 | if (!this.disabled) return (await this.delegate).extractUpdate(progress);
692 | return false;
693 | }
694 |
695 | async getAvailableVersions(): Promise {
696 | if (!this.disabled) return (await this.delegate).getAvailableVersions();
697 | return [];
698 | }
699 |
700 | async getCurrentVersion(): Promise {
701 | if (!this.disabled) return (await this.delegate).getCurrentVersion();
702 | return;
703 | }
704 |
705 | async getVersionById(versionId: string): Promise {
706 | if (!this.disabled) return (await this.delegate).getVersionById(versionId);
707 | return;
708 | }
709 |
710 | async reloadApp(): Promise {
711 | if (!this.disabled) return (await this.delegate).reloadApp();
712 | return false;
713 | }
714 |
715 | async sync(syncOptions: ISyncOptions = {}, progress?: CallbackFunction): Promise {
716 | if (!this.disabled) return (await this.delegate).sync(syncOptions, progress);
717 | return;
718 | }
719 | }
720 |
721 |
722 | /**
723 | * BASE API
724 | *
725 | * All features of the Ionic Cordova plugin are registered here, along with some low level error tracking features used
726 | * by the monitoring service.
727 | */
728 | class IonicCordova implements IPluginBaseAPI {
729 |
730 | public deploy: IDeployPluginAPI;
731 |
732 | constructor() {
733 | this.deploy = new IonicDeploy(this);
734 | }
735 |
736 |
737 | getAppInfo(success: CallbackFunction, failure: CallbackFunction) {
738 | console.warn('This function has been deprecated in favor of IonicCordova.getAppDetails.');
739 | this.getAppDetails().then(
740 | result => success(result),
741 | err => {
742 | typeof err === 'string' ? failure(err) : failure(err.message);
743 | }
744 | );
745 | }
746 |
747 | async getAppDetails(): Promise {
748 | return new Promise( (resolve, reject) => {
749 | cordova.exec(resolve, reject, 'IonicCordovaCommon', 'getAppInfo');
750 | });
751 | }
752 | }
753 |
754 | class Timer {
755 | name: string;
756 | startTime: Date;
757 | lastTime: Date;
758 | constructor(name: string) {
759 | this.name = name;
760 | this.startTime = new Date();
761 | this.lastTime = new Date();
762 | console.log(`Starting IonicTimer ${this.name}`);
763 | }
764 |
765 | end(extraLog?: string) {
766 | console.log(`Finished IonicTimer ${this.name} in ${(new Date().getTime() - this.startTime.getTime()) / 1000} seconds.`);
767 | if (extraLog) {
768 | console.log(`IonicTimer extra ${extraLog}`);
769 | }
770 | }
771 |
772 | diff(message?: string) {
773 | console.log(`Message: ${message} Diff IonicTimer ${this.name} in ${(new Date().getTime() - this.lastTime.getTime()) / 1000} seconds.`);
774 | this.lastTime = new Date();
775 | }
776 | }
777 |
778 | const instance = new IonicCordova();
779 | export = instance;
780 |
--------------------------------------------------------------------------------
/www/definitions.ts:
--------------------------------------------------------------------------------
1 | import { ICurrentConfig } from './IonicCordova';
2 |
3 | export interface IAvailableUpdate {
4 | binaryVersionName: string;
5 | binaryVersionCode: string;
6 | channel: string;
7 | lastUsed: string;
8 | state: string;
9 | url: string;
10 | versionId: string;
11 | buildId: string;
12 | }
13 |
14 | export interface ISavedPreferences extends ICurrentConfig {
15 | availableUpdate?: IAvailableUpdate;
16 | updates: { [versionId: string]: IAvailableUpdate };
17 | }
18 |
19 | export interface UpdateInfo {
20 | versionId: string;
21 | path: string;
22 | }
23 |
24 | export interface ManifestFileEntry {
25 | integrity: string;
26 | href: string;
27 | size: number;
28 | }
29 |
30 | export interface FetchManifestResp {
31 | manifestJson: ManifestFileEntry[];
32 | fileBaseUrl: string;
33 | }
34 |
--------------------------------------------------------------------------------
/www/guards.ts:
--------------------------------------------------------------------------------
1 | import { IDeployConfig } from './IonicCordova';
2 |
3 | export function isPluginConfig(o: object): o is IDeployConfig {
4 | const obj = o;
5 | const allowedKeys = ['appId', 'channel', 'disabled', 'host', 'maxVersions', 'minBackgroundDuration', 'updateMethod'];
6 | if (!obj) return false;
7 | for (const key in obj) {
8 | if (allowedKeys.indexOf(key) === -1) {
9 | return false;
10 | }
11 | }
12 | return obj &&
13 | (obj.appId === undefined || typeof obj.appId === 'string') &&
14 | (obj.channel === undefined || typeof obj.channel === 'string') &&
15 | (obj.debug === undefined || typeof obj.debug === 'string') &&
16 | (obj.updateMethod === undefined || typeof obj.updateMethod === 'string') &&
17 | (obj.maxVersions === undefined || typeof obj.maxVersions === 'number') &&
18 | (obj.minBackgroundDuration === undefined || typeof obj.minBackgroundDuration === 'number') &&
19 | (obj.host === undefined || typeof obj.host === 'string');
20 | }
21 |
--------------------------------------------------------------------------------
/www/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CallbackFunction,
3 | CheckForUpdateResponse,
4 | ICurrentConfig,
5 | IDeployConfig,
6 | ISnapshotInfo,
7 | ISyncOptions,
8 | } from './IonicCordova';
9 | /**
10 | * @hidden
11 | */
12 | const deviceready = new Promise((resolve, rejects) => {
13 | document.addEventListener('deviceready', () => {
14 | if (window.IonicCordova) {
15 | return resolve(window.IonicCordova.deploy);
16 | }
17 | return rejects('cordova-plugin-ionic not found. Are you sure you installed it?');
18 | });
19 | });
20 |
21 | export class DeployClass implements IDeployPluginAPI {
22 |
23 | async configure(config: IDeployConfig) {
24 | const deploy = await deviceready;
25 | return deploy.configure(config);
26 | }
27 |
28 | async getConfiguration(): Promise {
29 | const deploy = await deviceready;
30 | return deploy.getConfiguration();
31 | }
32 |
33 | async checkForUpdate(): Promise {
34 | const deploy = await deviceready;
35 | return deploy.checkForUpdate();
36 | }
37 |
38 | async downloadUpdate(progress?: CallbackFunction) {
39 | const deploy = await deviceready;
40 | return deploy.downloadUpdate(progress);
41 | }
42 |
43 | async extractUpdate(progress?: CallbackFunction) {
44 | const deploy = await deviceready;
45 | return deploy.extractUpdate(progress);
46 | }
47 |
48 | async reloadApp() {
49 | const deploy = await deviceready;
50 | return deploy.reloadApp();
51 | }
52 |
53 | async sync(options: ISyncOptions, progress?: CallbackFunction) {
54 | const deploy = await deviceready;
55 | return deploy.sync(options, progress);
56 | }
57 |
58 | async getCurrentVersion(): Promise {
59 | const deploy = await deviceready;
60 | return deploy.getCurrentVersion();
61 | }
62 |
63 | async getAvailableVersions() {
64 | const deploy = await deviceready;
65 | return deploy.getAvailableVersions();
66 | }
67 |
68 | async deleteVersionById(versionId: string) {
69 | const deploy = await deviceready;
70 | return deploy.deleteVersionById(versionId);
71 | }
72 |
73 | async getVersionById(versionId: string) {
74 | const deploy = await deviceready;
75 | return deploy.getVersionById(versionId);
76 | }
77 | }
78 |
79 | export const Deploy = new DeployClass();
--------------------------------------------------------------------------------
/www/ngx/index.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { DeployClass } from '../index';
3 |
4 | @Injectable()
5 | export class Deploy extends DeployClass { }
6 |
--------------------------------------------------------------------------------