@capawesome/capacitor-system-webview 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapawesomeCapacitorSystemWebView.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +227 -0
- package/android/build.gradle +60 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/SystemWebView.java +94 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/SystemWebViewPlugin.java +116 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/classes/CustomException.java +20 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/classes/CustomExceptions.java +15 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/classes/options/IsUpdateRequiredOptions.java +21 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/classes/results/GetInfoResult.java +35 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/classes/results/IsUpdateRequiredResult.java +22 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/interfaces/Callback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/interfaces/EmptyCallback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/interfaces/NonEmptyResultCallback.java +7 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/interfaces/Result.java +7 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +190 -0
- package/dist/esm/definitions.d.ts +112 -0
- package/dist/esm/definitions.js +27 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +7 -0
- package/dist/esm/web.js +13 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +54 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +57 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/SystemWebViewPlugin.swift +29 -0
- package/package.json +91 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'CapawesomeCapacitorSystemWebView'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '15.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
17
|
+
end
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robin Genz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/Package.swift
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "CapawesomeCapacitorSystemWebView",
|
|
6
|
+
platforms: [.iOS(.v15)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(
|
|
9
|
+
name: "CapawesomeCapacitorSystemWebView",
|
|
10
|
+
targets: ["SystemWebViewPlugin"])
|
|
11
|
+
],
|
|
12
|
+
dependencies: [
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
|
|
14
|
+
],
|
|
15
|
+
targets: [
|
|
16
|
+
.target(
|
|
17
|
+
name: "SystemWebViewPlugin",
|
|
18
|
+
dependencies: [
|
|
19
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
20
|
+
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
21
|
+
],
|
|
22
|
+
path: "ios/Plugin"),
|
|
23
|
+
.testTarget(
|
|
24
|
+
name: "SystemWebViewPluginTests",
|
|
25
|
+
dependencies: ["SystemWebViewPlugin"],
|
|
26
|
+
path: "ios/PluginTests")
|
|
27
|
+
]
|
|
28
|
+
)
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Capacitor System WebView Plugin
|
|
2
|
+
|
|
3
|
+
Capacitor plugin to detect an outdated Android System WebView and guide users to update it.
|
|
4
|
+
|
|
5
|
+
<div class="capawesome-z29o10a">
|
|
6
|
+
<a href="https://cloud.capawesome.io/" target="_blank">
|
|
7
|
+
<img alt="Deliver Live Updates to your Capacitor app with Capawesome Cloud" src="https://cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
|
|
8
|
+
</a>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🔍 **Detection**: Read the package name and version of the active Android System WebView provider.
|
|
14
|
+
- ⚠️ **Outdated check**: Compare the installed Chromium major version against the minimum your app requires.
|
|
15
|
+
- 🛒 **Update flow**: Send users straight to the Play Store entry of their active WebView provider.
|
|
16
|
+
- 🤝 **Compatibility**: Works alongside the [App Update](https://capawesome.io/docs/sdks/capacitor/app-update/), [Device Info](https://capawesome.io/docs/sdks/capacitor/device-info/) and [Dialog](https://capawesome.io/docs/sdks/capacitor/dialog/) plugins.
|
|
17
|
+
- 🔁 **Up-to-date**: Always supports the latest Capacitor version.
|
|
18
|
+
|
|
19
|
+
Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
|
|
20
|
+
|
|
21
|
+
## Newsletter
|
|
22
|
+
|
|
23
|
+
Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).
|
|
24
|
+
|
|
25
|
+
## Compatibility
|
|
26
|
+
|
|
27
|
+
| Plugin Version | Capacitor Version | Status |
|
|
28
|
+
| -------------- | ----------------- | -------------- |
|
|
29
|
+
| 0.x.x | >=8.x.x | Active support |
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
You can use our **AI-Assisted Setup** to install the plugin.
|
|
34
|
+
Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx skills add capawesome-team/skills --skill capacitor-plugins
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then use the following prompt:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome/capacitor-system-webview` plugin in my project.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install @capawesome/capacitor-system-webview
|
|
50
|
+
npx cap sync
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This plugin is only available on **Android**. On iOS and Web, all methods reject as unimplemented (see [iOS](#ios) below).
|
|
54
|
+
|
|
55
|
+
### Android
|
|
56
|
+
|
|
57
|
+
#### Variables
|
|
58
|
+
|
|
59
|
+
This plugin will use the following project variables (defined in your app's `variables.gradle` file):
|
|
60
|
+
|
|
61
|
+
- `$androidxWebkitVersion` version of `androidx.webkit:webkit` (default: `1.14.0`)
|
|
62
|
+
|
|
63
|
+
### iOS
|
|
64
|
+
|
|
65
|
+
This plugin has **no iOS implementation** and does not need one. On iOS, the `WKWebView` is part of the operating system and is updated together with iOS itself. It cannot be updated separately, so there is nothing for an app to detect or fix.
|
|
66
|
+
|
|
67
|
+
All methods reject as unimplemented on iOS.
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
No configuration required for this plugin.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
The recommended pattern is to check whether an update is required on app start, prompt the user with the [Dialog](https://capawesome.io/docs/sdks/capacitor/dialog/) plugin, and then open the Play Store:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { Dialog } from '@capacitor/dialog';
|
|
79
|
+
import { SystemWebView } from '@capawesome/capacitor-system-webview';
|
|
80
|
+
|
|
81
|
+
const promptForUpdateIfRequired = async () => {
|
|
82
|
+
const { required } = await SystemWebView.isUpdateRequired({
|
|
83
|
+
minMajorVersion: 105,
|
|
84
|
+
});
|
|
85
|
+
if (!required) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const { value } = await Dialog.confirm({
|
|
89
|
+
title: 'Update required',
|
|
90
|
+
message:
|
|
91
|
+
'Your Android System WebView is outdated. Please update it to continue using all features of this app.',
|
|
92
|
+
okButtonTitle: 'Update',
|
|
93
|
+
});
|
|
94
|
+
if (value) {
|
|
95
|
+
await SystemWebView.openAppStore();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const getInfo = async () => {
|
|
100
|
+
const info = await SystemWebView.getInfo();
|
|
101
|
+
return info;
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
> [!NOTE]
|
|
106
|
+
> A WebView update only takes effect the next time the app process is started. The currently running app keeps using the old WebView until it is restarted.
|
|
107
|
+
|
|
108
|
+
## API
|
|
109
|
+
|
|
110
|
+
<docgen-index>
|
|
111
|
+
|
|
112
|
+
* [`getInfo()`](#getinfo)
|
|
113
|
+
* [`isUpdateRequired(...)`](#isupdaterequired)
|
|
114
|
+
* [`openAppStore()`](#openappstore)
|
|
115
|
+
* [Interfaces](#interfaces)
|
|
116
|
+
|
|
117
|
+
</docgen-index>
|
|
118
|
+
|
|
119
|
+
<docgen-api>
|
|
120
|
+
<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
|
|
121
|
+
|
|
122
|
+
### getInfo()
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
getInfo() => Promise<GetInfoResult>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Get information about the active Android System WebView provider.
|
|
129
|
+
|
|
130
|
+
Only available on Android.
|
|
131
|
+
|
|
132
|
+
**Returns:** <code>Promise<<a href="#getinforesult">GetInfoResult</a>></code>
|
|
133
|
+
|
|
134
|
+
**Since:** 0.1.0
|
|
135
|
+
|
|
136
|
+
--------------------
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
### isUpdateRequired(...)
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
isUpdateRequired(options: IsUpdateRequiredOptions) => Promise<IsUpdateRequiredResult>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Check whether the active Android System WebView is older than the minimum
|
|
146
|
+
Chromium major version required by your app.
|
|
147
|
+
|
|
148
|
+
Only available on Android.
|
|
149
|
+
|
|
150
|
+
| Param | Type |
|
|
151
|
+
| ------------- | --------------------------------------------------------------------------- |
|
|
152
|
+
| **`options`** | <code><a href="#isupdaterequiredoptions">IsUpdateRequiredOptions</a></code> |
|
|
153
|
+
|
|
154
|
+
**Returns:** <code>Promise<<a href="#isupdaterequiredresult">IsUpdateRequiredResult</a>></code>
|
|
155
|
+
|
|
156
|
+
**Since:** 0.1.0
|
|
157
|
+
|
|
158
|
+
--------------------
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
### openAppStore()
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
openAppStore() => Promise<void>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Open the Play Store entry of the active Android System WebView provider so
|
|
168
|
+
the user can update it.
|
|
169
|
+
|
|
170
|
+
Only available on Android.
|
|
171
|
+
|
|
172
|
+
**Since:** 0.1.0
|
|
173
|
+
|
|
174
|
+
--------------------
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
### Interfaces
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
#### GetInfoResult
|
|
181
|
+
|
|
182
|
+
| Prop | Type | Description | Since |
|
|
183
|
+
| ------------------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
184
|
+
| **`majorVersion`** | <code>number \| null</code> | The Chromium major version of the active WebView provider. This is the integer before the first dot of the `versionName` (e.g. `126` for `126.0.6478.122`). `null` if the `versionName` is not a Chromium-style version, which can happen with OEM WebView forks. | 0.1.0 |
|
|
185
|
+
| **`packageName`** | <code>string</code> | The package name of the active WebView provider. | 0.1.0 |
|
|
186
|
+
| **`versionName`** | <code>string</code> | The version name of the active WebView provider. | 0.1.0 |
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
#### IsUpdateRequiredResult
|
|
190
|
+
|
|
191
|
+
| Prop | Type | Description | Since |
|
|
192
|
+
| -------------- | -------------------- | ------------------------------------------------------------------------------------------- | ----- |
|
|
193
|
+
| **`required`** | <code>boolean</code> | `true` if the active WebView is older than the required minimum version, otherwise `false`. | 0.1.0 |
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
#### IsUpdateRequiredOptions
|
|
197
|
+
|
|
198
|
+
| Prop | Type | Description | Since |
|
|
199
|
+
| --------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
200
|
+
| **`minMajorVersion`** | <code>number</code> | The minimum Chromium major version your app requires. The active WebView is considered outdated if its `majorVersion` is lower than this value. | 0.1.0 |
|
|
201
|
+
|
|
202
|
+
</docgen-api>
|
|
203
|
+
|
|
204
|
+
## Why Outdated System WebViews Matter
|
|
205
|
+
|
|
206
|
+
On Android, your Capacitor app runs inside the Android System WebView, which is updated independently of the operating system. Many devices run an ancient WebView because the user has disabled automatic updates, is offline, or uses a custom ROM. When that happens, modern web features your app relies on may simply be missing, causing hard-to-diagnose "the app is broken" support tickets.
|
|
207
|
+
|
|
208
|
+
Because "outdated" depends on which web features your app's bundle actually needs, this plugin never hardcodes a threshold. Instead, your app declares the minimum Chromium major version it requires via `isUpdateRequired(...)`.
|
|
209
|
+
|
|
210
|
+
The following table gives a rough idea of when some popular web features became available in Chromium (and therefore the System WebView):
|
|
211
|
+
|
|
212
|
+
| Chromium major | Rough web-feature baseline |
|
|
213
|
+
| -------------- | --------------------------------------------- |
|
|
214
|
+
| 97+ | Array `findLast` / `findLastIndex` |
|
|
215
|
+
| 105+ | CSS `:has()` selector and container queries |
|
|
216
|
+
| 112+ | Native CSS nesting |
|
|
217
|
+
| 114+ | CSS `text-wrap: balance` |
|
|
218
|
+
|
|
219
|
+
Pick the minimum version based on the features your app uses and pass it as `minMajorVersion`.
|
|
220
|
+
|
|
221
|
+
## Changelog
|
|
222
|
+
|
|
223
|
+
See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/system-webview/CHANGELOG.md).
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/system-webview/LICENSE).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
|
+
androidxWebkitVersion = project.hasProperty('androidxWebkitVersion') ? rootProject.ext.androidxWebkitVersion : '1.14.0'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
buildscript {
|
|
10
|
+
repositories {
|
|
11
|
+
google()
|
|
12
|
+
mavenCentral()
|
|
13
|
+
}
|
|
14
|
+
dependencies {
|
|
15
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
apply plugin: 'com.android.library'
|
|
20
|
+
|
|
21
|
+
android {
|
|
22
|
+
namespace = "io.capawesome.capacitorjs.plugins.systemwebview"
|
|
23
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
24
|
+
defaultConfig {
|
|
25
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
26
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
27
|
+
versionCode 1
|
|
28
|
+
versionName "1.0"
|
|
29
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
30
|
+
}
|
|
31
|
+
buildTypes {
|
|
32
|
+
release {
|
|
33
|
+
minifyEnabled false
|
|
34
|
+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
lintOptions {
|
|
38
|
+
abortOnError = false
|
|
39
|
+
}
|
|
40
|
+
compileOptions {
|
|
41
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
42
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
repositories {
|
|
47
|
+
google()
|
|
48
|
+
mavenCentral()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
dependencies {
|
|
53
|
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
54
|
+
implementation project(':capacitor-android')
|
|
55
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
56
|
+
implementation "androidx.webkit:webkit:$androidxWebkitVersion"
|
|
57
|
+
testImplementation "junit:junit:$junitVersion"
|
|
58
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
59
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"></manifest>
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/systemwebview/SystemWebView.java
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.systemwebview;
|
|
2
|
+
|
|
3
|
+
import android.content.ActivityNotFoundException;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
import android.content.pm.PackageInfo;
|
|
7
|
+
import android.net.Uri;
|
|
8
|
+
import androidx.annotation.NonNull;
|
|
9
|
+
import androidx.annotation.Nullable;
|
|
10
|
+
import androidx.webkit.WebViewCompat;
|
|
11
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.CustomExceptions;
|
|
12
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.options.IsUpdateRequiredOptions;
|
|
13
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.results.GetInfoResult;
|
|
14
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.results.IsUpdateRequiredResult;
|
|
15
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.interfaces.EmptyCallback;
|
|
16
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.interfaces.NonEmptyResultCallback;
|
|
17
|
+
|
|
18
|
+
public class SystemWebView {
|
|
19
|
+
|
|
20
|
+
@NonNull
|
|
21
|
+
private final SystemWebViewPlugin plugin;
|
|
22
|
+
|
|
23
|
+
public SystemWebView(@NonNull SystemWebViewPlugin plugin) {
|
|
24
|
+
this.plugin = plugin;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public void getInfo(@NonNull NonEmptyResultCallback<GetInfoResult> callback) throws Exception {
|
|
28
|
+
PackageInfo packageInfo = getCurrentWebViewPackage();
|
|
29
|
+
String versionName = packageInfo.versionName;
|
|
30
|
+
if (versionName == null) {
|
|
31
|
+
throw CustomExceptions.WEB_VIEW_PACKAGE_UNAVAILABLE;
|
|
32
|
+
}
|
|
33
|
+
Integer majorVersion = parseMajorVersion(versionName);
|
|
34
|
+
callback.success(new GetInfoResult(packageInfo.packageName, versionName, majorVersion));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public void isUpdateRequired(
|
|
38
|
+
@NonNull IsUpdateRequiredOptions options,
|
|
39
|
+
@NonNull NonEmptyResultCallback<IsUpdateRequiredResult> callback
|
|
40
|
+
) throws Exception {
|
|
41
|
+
PackageInfo packageInfo = getCurrentWebViewPackage();
|
|
42
|
+
Integer majorVersion = parseMajorVersion(packageInfo.versionName);
|
|
43
|
+
if (majorVersion == null) {
|
|
44
|
+
throw CustomExceptions.VERSION_UNPARSEABLE;
|
|
45
|
+
}
|
|
46
|
+
boolean required = majorVersion < options.getMinMajorVersion();
|
|
47
|
+
callback.success(new IsUpdateRequiredResult(required));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public void openAppStore(@NonNull EmptyCallback callback) throws Exception {
|
|
51
|
+
PackageInfo packageInfo = getCurrentWebViewPackage();
|
|
52
|
+
String packageName = packageInfo.packageName;
|
|
53
|
+
try {
|
|
54
|
+
startAppStoreIntent("market://details?id=" + packageName);
|
|
55
|
+
} catch (ActivityNotFoundException exception) {
|
|
56
|
+
try {
|
|
57
|
+
startAppStoreIntent("https://play.google.com/store/apps/details?id=" + packageName);
|
|
58
|
+
} catch (ActivityNotFoundException fallbackException) {
|
|
59
|
+
throw CustomExceptions.OPEN_FAILED;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
callback.success();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@NonNull
|
|
66
|
+
private PackageInfo getCurrentWebViewPackage() throws Exception {
|
|
67
|
+
Context context = plugin.getContext();
|
|
68
|
+
PackageInfo packageInfo = WebViewCompat.getCurrentWebViewPackage(context);
|
|
69
|
+
if (packageInfo == null) {
|
|
70
|
+
throw CustomExceptions.WEB_VIEW_PACKAGE_UNAVAILABLE;
|
|
71
|
+
}
|
|
72
|
+
return packageInfo;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Nullable
|
|
76
|
+
private Integer parseMajorVersion(@Nullable String versionName) {
|
|
77
|
+
if (versionName == null) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
int separatorIndex = versionName.indexOf('.');
|
|
81
|
+
String majorPart = separatorIndex >= 0 ? versionName.substring(0, separatorIndex) : versionName;
|
|
82
|
+
try {
|
|
83
|
+
return Integer.parseInt(majorPart.trim());
|
|
84
|
+
} catch (NumberFormatException exception) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private void startAppStoreIntent(@NonNull String url) {
|
|
90
|
+
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
91
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
92
|
+
plugin.getContext().startActivity(intent);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.systemwebview;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.getcapacitor.Logger;
|
|
6
|
+
import com.getcapacitor.Plugin;
|
|
7
|
+
import com.getcapacitor.PluginCall;
|
|
8
|
+
import com.getcapacitor.PluginMethod;
|
|
9
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
10
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.CustomException;
|
|
11
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.options.IsUpdateRequiredOptions;
|
|
12
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.results.GetInfoResult;
|
|
13
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.results.IsUpdateRequiredResult;
|
|
14
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.interfaces.EmptyCallback;
|
|
15
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.interfaces.NonEmptyResultCallback;
|
|
16
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.interfaces.Result;
|
|
17
|
+
|
|
18
|
+
@CapacitorPlugin(name = "SystemWebView")
|
|
19
|
+
public class SystemWebViewPlugin extends Plugin {
|
|
20
|
+
|
|
21
|
+
public static final String ERROR_UNKNOWN_ERROR = "An unknown error occurred.";
|
|
22
|
+
public static final String TAG = "SystemWebViewPlugin";
|
|
23
|
+
|
|
24
|
+
private SystemWebView implementation;
|
|
25
|
+
|
|
26
|
+
@Override
|
|
27
|
+
public void load() {
|
|
28
|
+
implementation = new SystemWebView(this);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@PluginMethod
|
|
32
|
+
public void getInfo(PluginCall call) {
|
|
33
|
+
try {
|
|
34
|
+
NonEmptyResultCallback<GetInfoResult> callback = new NonEmptyResultCallback<>() {
|
|
35
|
+
@Override
|
|
36
|
+
public void success(@NonNull GetInfoResult result) {
|
|
37
|
+
resolveCall(call, result);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Override
|
|
41
|
+
public void error(@NonNull Exception exception) {
|
|
42
|
+
rejectCall(call, exception);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
implementation.getInfo(callback);
|
|
46
|
+
} catch (Exception exception) {
|
|
47
|
+
rejectCall(call, exception);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@PluginMethod
|
|
52
|
+
public void isUpdateRequired(PluginCall call) {
|
|
53
|
+
try {
|
|
54
|
+
IsUpdateRequiredOptions options = new IsUpdateRequiredOptions(call);
|
|
55
|
+
NonEmptyResultCallback<IsUpdateRequiredResult> callback = new NonEmptyResultCallback<>() {
|
|
56
|
+
@Override
|
|
57
|
+
public void success(@NonNull IsUpdateRequiredResult result) {
|
|
58
|
+
resolveCall(call, result);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Override
|
|
62
|
+
public void error(@NonNull Exception exception) {
|
|
63
|
+
rejectCall(call, exception);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
implementation.isUpdateRequired(options, callback);
|
|
67
|
+
} catch (Exception exception) {
|
|
68
|
+
rejectCall(call, exception);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@PluginMethod
|
|
73
|
+
public void openAppStore(PluginCall call) {
|
|
74
|
+
try {
|
|
75
|
+
EmptyCallback callback = new EmptyCallback() {
|
|
76
|
+
@Override
|
|
77
|
+
public void success() {
|
|
78
|
+
resolveCall(call);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Override
|
|
82
|
+
public void error(@NonNull Exception exception) {
|
|
83
|
+
rejectCall(call, exception);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
implementation.openAppStore(callback);
|
|
87
|
+
} catch (Exception exception) {
|
|
88
|
+
rejectCall(call, exception);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private void rejectCall(@NonNull PluginCall call, @NonNull Exception exception) {
|
|
93
|
+
String message = exception.getMessage();
|
|
94
|
+
if (message == null) {
|
|
95
|
+
message = ERROR_UNKNOWN_ERROR;
|
|
96
|
+
}
|
|
97
|
+
String code = null;
|
|
98
|
+
if (exception instanceof CustomException) {
|
|
99
|
+
code = ((CustomException) exception).getCode();
|
|
100
|
+
}
|
|
101
|
+
Logger.error(TAG, message, exception);
|
|
102
|
+
call.reject(message, code);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private void resolveCall(@NonNull PluginCall call) {
|
|
106
|
+
call.resolve();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private void resolveCall(@NonNull PluginCall call, @Nullable Result result) {
|
|
110
|
+
if (result == null) {
|
|
111
|
+
call.resolve();
|
|
112
|
+
} else {
|
|
113
|
+
call.resolve(result.toJSObject());
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.systemwebview.classes;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
|
|
6
|
+
public class CustomException extends Exception {
|
|
7
|
+
|
|
8
|
+
@Nullable
|
|
9
|
+
private final String code;
|
|
10
|
+
|
|
11
|
+
public CustomException(@Nullable String code, @NonNull String message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Nullable
|
|
17
|
+
public String getCode() {
|
|
18
|
+
return code;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.systemwebview.classes;
|
|
2
|
+
|
|
3
|
+
public class CustomExceptions {
|
|
4
|
+
|
|
5
|
+
public static final CustomException MIN_MAJOR_VERSION_MISSING = new CustomException(null, "minMajorVersion must be provided.");
|
|
6
|
+
public static final CustomException OPEN_FAILED = new CustomException("OPEN_FAILED", "The Play Store could not be opened.");
|
|
7
|
+
public static final CustomException VERSION_UNPARSEABLE = new CustomException(
|
|
8
|
+
"VERSION_UNPARSEABLE",
|
|
9
|
+
"The WebView version name is not a Chromium-style version and could not be parsed."
|
|
10
|
+
);
|
|
11
|
+
public static final CustomException WEB_VIEW_PACKAGE_UNAVAILABLE = new CustomException(
|
|
12
|
+
"WEB_VIEW_PACKAGE_UNAVAILABLE",
|
|
13
|
+
"The active WebView package could not be determined."
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.systemwebview.classes.options;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.PluginCall;
|
|
4
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.classes.CustomExceptions;
|
|
5
|
+
|
|
6
|
+
public class IsUpdateRequiredOptions {
|
|
7
|
+
|
|
8
|
+
private final int minMajorVersion;
|
|
9
|
+
|
|
10
|
+
public IsUpdateRequiredOptions(PluginCall call) throws Exception {
|
|
11
|
+
Integer minMajorVersion = call.getInt("minMajorVersion");
|
|
12
|
+
if (minMajorVersion == null) {
|
|
13
|
+
throw CustomExceptions.MIN_MAJOR_VERSION_MISSING;
|
|
14
|
+
}
|
|
15
|
+
this.minMajorVersion = minMajorVersion;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public int getMinMajorVersion() {
|
|
19
|
+
return minMajorVersion;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.systemwebview.classes.results;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.getcapacitor.JSObject;
|
|
6
|
+
import io.capawesome.capacitorjs.plugins.systemwebview.interfaces.Result;
|
|
7
|
+
import org.json.JSONObject;
|
|
8
|
+
|
|
9
|
+
public class GetInfoResult implements Result {
|
|
10
|
+
|
|
11
|
+
@Nullable
|
|
12
|
+
private final Integer majorVersion;
|
|
13
|
+
|
|
14
|
+
@NonNull
|
|
15
|
+
private final String packageName;
|
|
16
|
+
|
|
17
|
+
@NonNull
|
|
18
|
+
private final String versionName;
|
|
19
|
+
|
|
20
|
+
public GetInfoResult(@NonNull String packageName, @NonNull String versionName, @Nullable Integer majorVersion) {
|
|
21
|
+
this.packageName = packageName;
|
|
22
|
+
this.versionName = versionName;
|
|
23
|
+
this.majorVersion = majorVersion;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Override
|
|
27
|
+
@NonNull
|
|
28
|
+
public JSObject toJSObject() {
|
|
29
|
+
JSObject result = new JSObject();
|
|
30
|
+
result.put("packageName", packageName);
|
|
31
|
+
result.put("versionName", versionName);
|
|
32
|
+
result.put("majorVersion", majorVersion == null ? JSONObject.NULL : majorVersion);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
}
|