@modos189/nativescript-webview-x-gecko 1.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/LICENSE +201 -0
- package/README.md +135 -0
- package/common.d.ts +1 -0
- package/common.js +2 -0
- package/common.js.map +1 -0
- package/index.android.d.ts +19 -0
- package/index.android.js +90 -0
- package/index.android.js.map +1 -0
- package/index.d.ts +16 -0
- package/index.ios.d.ts +8 -0
- package/index.ios.js +23 -0
- package/index.ios.js.map +1 -0
- package/package.json +42 -0
- package/platforms/android/include.gradle +10 -0
- package/platforms/android/java/com/modos189/webviewxgecko/GeckoPopupHelper.java +459 -0
- package/vue/index.d.ts +4 -0
- package/vue/index.js +7 -0
- package/vue/index.js.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright (c) 2015-2019 Progress Software Corporation
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<!-- ⚠️ This file is generated by tools/scripts/generate-readme.js — do not edit directly. -->
|
|
2
|
+
# @modos189/nativescript-webview-x-gecko
|
|
3
|
+
|
|
4
|
+
A NativeScript WebView plugin using **GeckoView** (Mozilla's Gecko engine) on Android and WKWebView on iOS.
|
|
5
|
+
Exports the same `WebViewX` class as [`@modos189/nativescript-webview-x`](../webview-x/README.md) — swap the engine by changing only the import path.
|
|
6
|
+
|
|
7
|
+
> **iOS note:** App Store guidelines prohibit custom browser engines on iOS.
|
|
8
|
+
> The iOS implementation falls back to WKWebView (identical to `@modos189/nativescript-webview-x`).
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @modos189/nativescript-webview-x-gecko
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The GeckoView Maven repository and AAR dependency are added to your Android project automatically on install.
|
|
17
|
+
|
|
18
|
+
> **Note:** GeckoView requires `minSdkVersion 26`. Make sure your `App_Resources/Android/app.gradle`
|
|
19
|
+
> has `minSdkVersion` set to at least `26`.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```xml
|
|
24
|
+
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
|
|
25
|
+
xmlns:wv="@modos189/nativescript-webview-x-gecko">
|
|
26
|
+
<GridLayout rows="auto, *">
|
|
27
|
+
<TextField row="0" hint="Enter URL" text="{{ url }}" returnPress="{{ onNavigate }}" />
|
|
28
|
+
<wv:WebViewX row="1" src="{{ src }}" />
|
|
29
|
+
</GridLayout>
|
|
30
|
+
</Page>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { WebViewX } from '@modos189/nativescript-webview-x-gecko';
|
|
35
|
+
|
|
36
|
+
const webview = new WebViewX();
|
|
37
|
+
webview.src = 'https://example.com';
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
To switch from `@modos189/nativescript-webview-x`, change only the import — no other code changes needed.
|
|
41
|
+
|
|
42
|
+
## Implemented
|
|
43
|
+
|
|
44
|
+
_Available in both `@modos189/nativescript-webview-x` and `@modos189/nativescript-webview-x-gecko`._
|
|
45
|
+
|
|
46
|
+
### Static properties
|
|
47
|
+
|
|
48
|
+
| Static property | Type | Description |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| `userAgentTransform` | `((defaultUA: string \| null) => string \| null) \| null` | Set once at app startup before any `WebViewX` is created. Applied automatically during native view initialization, before the first URL loads. `defaultUA` is the platform default UA string on Android system WebView, `null` on iOS/GeckoView (unavailable synchronously). Return the desired UA string, or `null` to leave the platform default unchanged. |
|
|
51
|
+
|
|
52
|
+
### Properties
|
|
53
|
+
|
|
54
|
+
| Property | Type | Description |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| `src` | `string` | URL to load (data-binding supported) |
|
|
57
|
+
| `debugMode` | `boolean` | Enable remote WebView debugging |
|
|
58
|
+
| `supportPopups` | `boolean` | Open `window.open()` / `target="_blank"` links in a native popup. Default: `true` |
|
|
59
|
+
|
|
60
|
+
### Methods
|
|
61
|
+
|
|
62
|
+
| Method | Returns | Description |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| `getUserAgentOverride()` | `string \| null` | Return the active UA override, or `null` if none is set (platform default is used) |
|
|
65
|
+
| `setUserAgentOverride(ua: string \| null)` | `void` | Set a custom UA string for this instance; pass `null` or empty string to clear the override and restore the platform default. Applies to subsequent navigations |
|
|
66
|
+
|
|
67
|
+
### Events
|
|
68
|
+
|
|
69
|
+
| Event | Description |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `popupNavigate` | Android: fired on each navigation inside a popup; set `args.cancel = true` to intercept and dismiss the popup (e.g. capture OAuth redirect). `args.url` contains the target URL. |
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
Target API matching `@modos189/nativescript-webview-x`. Items not listed under **Implemented** above are not yet available on Android (iOS uses WKWebView and has the full API). Refer to the [upstream docs](https://github.com/nativescript-community/ui-webview) for details.
|
|
77
|
+
|
|
78
|
+
### Properties
|
|
79
|
+
|
|
80
|
+
| Property | Type | Description |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| `src` | `string` | URL to load |
|
|
83
|
+
| `autoInjectJSBridge` | `boolean` | Inject `window.nsWebViewBridge` on load finished. Default: `true` |
|
|
84
|
+
| `domStorage` | `boolean` | Android: enable DOM Storage API (e.g. `localStorage`) |
|
|
85
|
+
| `databaseStorage` | `boolean` | Android: enable database storage API |
|
|
86
|
+
| `builtInZoomControls` | `boolean` | Android: use built-in zoom mechanisms |
|
|
87
|
+
| `displayZoomControls` | `boolean` | Android: show on-screen zoom controls |
|
|
88
|
+
| `supportZoom` | `boolean` | Android: enable zoom support |
|
|
89
|
+
| `cacheMode` | `string` | Android: `default`, `no_cache`, `cache_first`, or `cache_only` |
|
|
90
|
+
| `debugMode` | `boolean` | Enable Chrome (Android) / Safari (iOS) remote debugger |
|
|
91
|
+
| `scrollBounce` | `boolean` | iOS: scrollView bounce. Default: `true` |
|
|
92
|
+
| `viewPortSize` | `boolean \| string \| ViewPortProperties` | Set viewport metadata after load |
|
|
93
|
+
| `limitsNavigationsToAppBoundDomains` | `boolean` | iOS: enable Service Workers |
|
|
94
|
+
| `supportPopups` | `boolean` | iOS: support `window.open` / `target="_blank"`. Default: `true` |
|
|
95
|
+
| `scrollBarIndicatorVisible` | `boolean` | Show/hide scrollbars |
|
|
96
|
+
|
|
97
|
+
### Methods
|
|
98
|
+
|
|
99
|
+
| Method | Returns | Description |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `loadUrl(src: string)` | `Promise<LoadFinishedEventData>` | Load a URL, resolves when finished |
|
|
102
|
+
| `executeJavaScript(code: string)` | `Promise<any>` | Execute JavaScript and return the result |
|
|
103
|
+
| `executePromise(code: string, timeout?: number)` | `Promise<any>` | Run a promise inside the webview |
|
|
104
|
+
| `getTitle()` | `Promise<string>` | Return `document.title` |
|
|
105
|
+
| `emitToWebView(eventName: string, data: any)` | `void` | Emit an event into the webview |
|
|
106
|
+
| `autoLoadJavaScriptFile(name: string, path: string)` | `void` | Auto-inject a JS file on every `loadFinished` |
|
|
107
|
+
| `autoLoadStyleSheetFile(name: string, path: string, insertBefore?: boolean)` | `void` | Auto-inject a CSS file on every `loadFinished` |
|
|
108
|
+
| `autoExecuteJavaScript(code: string, name: string)` | `void` | Auto-execute a script on every `loadFinished` |
|
|
109
|
+
|
|
110
|
+
### Events
|
|
111
|
+
|
|
112
|
+
| Event | Description |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `loadStarted` | Navigation started |
|
|
115
|
+
| `loadFinished` | Navigation finished. `args.error` is set on failure |
|
|
116
|
+
| `loadProgress` | Android: page load progress (`args.progress: number`) |
|
|
117
|
+
| `shouldOverrideUrlLoading` | Raised before each navigation; set `args.cancel = true` to block |
|
|
118
|
+
| `titleChanged` | `document.title` changed |
|
|
119
|
+
| `webAlert` | `window.alert()` triggered; call `args.callback()` to dismiss |
|
|
120
|
+
| `webConfirm` | `window.confirm()` triggered; call `args.callback(boolean)` to dismiss |
|
|
121
|
+
| `webPrompt` | `window.prompt()` triggered; call `args.callback(string \| null)` to dismiss |
|
|
122
|
+
| `webConsole` | Android: a line was added to the web console |
|
|
123
|
+
|
|
124
|
+
### nsWebViewBridge (inside the WebView)
|
|
125
|
+
|
|
126
|
+
When `autoInjectJSBridge` is `true`, the bridge is available after `DOMContentLoaded`:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
window.nsWebViewBridge.emit('myEvent', { key: 'value' }); // → NativeScript
|
|
130
|
+
window.nsWebViewBridge.on('nativeEvent', (data) => { }); // ← NativeScript
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
Apache License Version 2.0
|
package/common.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/common.js
ADDED
package/common.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/common.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { View, Property } from '@nativescript/core';
|
|
2
|
+
export declare const srcProperty: Property<WebViewX, string>;
|
|
3
|
+
export declare const debugModeProperty: Property<WebViewX, boolean>;
|
|
4
|
+
export declare const supportPopupsProperty: Property<WebViewX, boolean>;
|
|
5
|
+
export declare class WebViewX extends View {
|
|
6
|
+
static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
|
|
7
|
+
static get popupNavigateEvent(): string;
|
|
8
|
+
nativeViewProtected: org.mozilla.geckoview.GeckoView;
|
|
9
|
+
private _session;
|
|
10
|
+
private _popupHelper;
|
|
11
|
+
src: string;
|
|
12
|
+
debugMode: boolean;
|
|
13
|
+
supportPopups: boolean;
|
|
14
|
+
_onPopupNavigate(url: string): boolean;
|
|
15
|
+
createNativeView(): org.mozilla.geckoview.GeckoView;
|
|
16
|
+
disposeNativeView(): void;
|
|
17
|
+
getUserAgentOverride(): string | null;
|
|
18
|
+
setUserAgentOverride(ua: string | null): void;
|
|
19
|
+
}
|
package/index.android.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Application, View, Property, booleanConverter } from '@nativescript/core';
|
|
2
|
+
const POPUP_NAVIGATE_EVENT = 'popupNavigate';
|
|
3
|
+
// Explicit type annotation required to break circular inference with [xProperty.setNative]
|
|
4
|
+
export const srcProperty = new Property({
|
|
5
|
+
name: 'src',
|
|
6
|
+
defaultValue: '',
|
|
7
|
+
});
|
|
8
|
+
export const debugModeProperty = new Property({
|
|
9
|
+
name: 'debugMode',
|
|
10
|
+
defaultValue: false,
|
|
11
|
+
valueConverter: booleanConverter,
|
|
12
|
+
});
|
|
13
|
+
export const supportPopupsProperty = new Property({
|
|
14
|
+
name: 'supportPopups',
|
|
15
|
+
defaultValue: true,
|
|
16
|
+
valueConverter: booleanConverter,
|
|
17
|
+
});
|
|
18
|
+
export class WebViewX extends View {
|
|
19
|
+
constructor() {
|
|
20
|
+
super(...arguments);
|
|
21
|
+
this._session = null;
|
|
22
|
+
this._popupHelper = null;
|
|
23
|
+
}
|
|
24
|
+
static get popupNavigateEvent() {
|
|
25
|
+
return POPUP_NAVIGATE_EVENT;
|
|
26
|
+
}
|
|
27
|
+
_onPopupNavigate(url) {
|
|
28
|
+
const args = {
|
|
29
|
+
eventName: POPUP_NAVIGATE_EVENT,
|
|
30
|
+
url,
|
|
31
|
+
cancel: false,
|
|
32
|
+
};
|
|
33
|
+
this.notify(args);
|
|
34
|
+
return args.cancel === true;
|
|
35
|
+
}
|
|
36
|
+
createNativeView() {
|
|
37
|
+
const runtime = com.modos189.webviewxgecko.GeckoPopupHelper.getRuntime(Application.android.context);
|
|
38
|
+
const session = new org.mozilla.geckoview.GeckoSession();
|
|
39
|
+
if (WebViewX.userAgentTransform) {
|
|
40
|
+
const newUA = WebViewX.userAgentTransform(null);
|
|
41
|
+
if (newUA !== null) {
|
|
42
|
+
session.getSettings().setUserAgentOverride(newUA);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// GeckoPopupHelper sets the NavigationDelegate and manages popup windows.
|
|
46
|
+
// this._context is the Activity context — required for Dialog creation.
|
|
47
|
+
this._popupHelper = new com.modos189.webviewxgecko.GeckoPopupHelper(session, this._context, true);
|
|
48
|
+
const interceptor = new com.modos189.webviewxgecko.GeckoPopupHelper.PopupUrlInterceptor({
|
|
49
|
+
shouldHandleExternally: (url) => {
|
|
50
|
+
return this._onPopupNavigate(url != null ? '' + url : '');
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
this._popupHelper.setUrlInterceptor(interceptor);
|
|
54
|
+
session.open(runtime);
|
|
55
|
+
this._session = session;
|
|
56
|
+
const geckoView = new org.mozilla.geckoview.GeckoView(this._context);
|
|
57
|
+
geckoView.setSession(session);
|
|
58
|
+
return geckoView;
|
|
59
|
+
}
|
|
60
|
+
disposeNativeView() {
|
|
61
|
+
this._popupHelper = null;
|
|
62
|
+
if (this._session) {
|
|
63
|
+
this._session.close();
|
|
64
|
+
this._session = null;
|
|
65
|
+
}
|
|
66
|
+
super.disposeNativeView();
|
|
67
|
+
}
|
|
68
|
+
[srcProperty.setNative](value) {
|
|
69
|
+
if (value && this._session) {
|
|
70
|
+
this._session.loadUri(value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
[debugModeProperty.setNative](value) {
|
|
74
|
+
com.modos189.webviewxgecko.GeckoPopupHelper.setRemoteDebuggingEnabled(!!value);
|
|
75
|
+
}
|
|
76
|
+
getUserAgentOverride() {
|
|
77
|
+
return this._session?.getSettings().getUserAgentOverride() ?? null;
|
|
78
|
+
}
|
|
79
|
+
setUserAgentOverride(ua) {
|
|
80
|
+
this._session?.getSettings().setUserAgentOverride(ua || null);
|
|
81
|
+
}
|
|
82
|
+
[supportPopupsProperty.setNative](value) {
|
|
83
|
+
this._popupHelper?.setSupportPopups(!!value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
WebViewX.userAgentTransform = null;
|
|
87
|
+
srcProperty.register(WebViewX);
|
|
88
|
+
debugModeProperty.register(WebViewX);
|
|
89
|
+
supportPopupsProperty.register(WebViewX);
|
|
90
|
+
//# sourceMappingURL=index.android.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.android.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/index.android.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { View } from '@nativescript/core';
|
|
2
|
+
import { Property } from '@nativescript/core';
|
|
3
|
+
|
|
4
|
+
export declare const srcProperty: Property<WebViewX, string>;
|
|
5
|
+
export declare const debugModeProperty: Property<WebViewX, boolean>;
|
|
6
|
+
export declare const supportPopupsProperty: Property<WebViewX, boolean>;
|
|
7
|
+
|
|
8
|
+
export declare class WebViewX extends View {
|
|
9
|
+
static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
|
|
10
|
+
static readonly popupNavigateEvent: string;
|
|
11
|
+
src: string;
|
|
12
|
+
debugMode: boolean;
|
|
13
|
+
supportPopups: boolean;
|
|
14
|
+
getUserAgentOverride(): string | null;
|
|
15
|
+
setUserAgentOverride(ua: string | null): void;
|
|
16
|
+
}
|
package/index.ios.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from '@nativescript-community/ui-webview/index.ios';
|
|
2
|
+
import { AWebView } from '@nativescript-community/ui-webview/index.ios';
|
|
3
|
+
export declare class WebViewX extends AWebView {
|
|
4
|
+
static userAgentTransform: ((defaultUA: string | null) => string | null) | null;
|
|
5
|
+
initNativeView(): void;
|
|
6
|
+
getUserAgentOverride(): string | null;
|
|
7
|
+
setUserAgentOverride(ua: string | null): void;
|
|
8
|
+
}
|
package/index.ios.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export * from '@nativescript-community/ui-webview/index.ios';
|
|
2
|
+
import { AWebView } from '@nativescript-community/ui-webview/index.ios';
|
|
3
|
+
export class WebViewX extends AWebView {
|
|
4
|
+
initNativeView() {
|
|
5
|
+
super.initNativeView();
|
|
6
|
+
if (WebViewX.userAgentTransform) {
|
|
7
|
+
const newUA = WebViewX.userAgentTransform(null);
|
|
8
|
+
if (newUA !== null && this.nativeViewProtected) {
|
|
9
|
+
this.nativeViewProtected.customUserAgent = newUA;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
getUserAgentOverride() {
|
|
14
|
+
return this.nativeViewProtected?.customUserAgent ?? null;
|
|
15
|
+
}
|
|
16
|
+
setUserAgentOverride(ua) {
|
|
17
|
+
if (this.nativeViewProtected) {
|
|
18
|
+
this.nativeViewProtected.customUserAgent = ua || null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
WebViewX.userAgentTransform = null;
|
|
23
|
+
//# sourceMappingURL=index.ios.js.map
|
package/index.ios.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.ios.js","sourceRoot":"","sources":["../../../packages/webview-x-gecko/index.ios.ts"],"names":[],"mappings":"AAAA,cAAc,8CAA8C,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAC;AAExE,MAAM,OAAO,QAAS,SAAQ,QAAQ;IAGpC,cAAc;QACZ,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,QAAQ,CAAC,kBAAkB,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC/C,IAAI,CAAC,mBAAmB,CAAC,eAAe,GAAG,KAAK,CAAC;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IAED,oBAAoB;QAClB,OAAO,IAAI,CAAC,mBAAmB,EAAE,eAAe,IAAI,IAAI,CAAC;IAC3D,CAAC;IAED,oBAAoB,CAAC,EAAiB;QACpC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,IAAI,CAAC,mBAAmB,CAAC,eAAe,GAAG,EAAE,IAAI,IAAI,CAAC;QACxD,CAAC;IACH,CAAC;;AApBM,2BAAkB,GAAyD,IAAI,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@modos189/nativescript-webview-x-gecko",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Add a plugin description",
|
|
5
|
+
"main": "index",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index",
|
|
9
|
+
"./vue": "./vue/index"
|
|
10
|
+
},
|
|
11
|
+
"nativescript": {
|
|
12
|
+
"platforms": {
|
|
13
|
+
"ios": "6.0.0",
|
|
14
|
+
"android": "6.0.0"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/modos189/nativescript-plugins.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"NativeScript",
|
|
23
|
+
"JavaScript",
|
|
24
|
+
"TypeScript",
|
|
25
|
+
"iOS",
|
|
26
|
+
"Android"
|
|
27
|
+
],
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "modos189",
|
|
30
|
+
"email": "oss@nativescript.org"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/modos189/nativescript-plugins/issues"
|
|
34
|
+
},
|
|
35
|
+
"license": "Apache-2.0",
|
|
36
|
+
"homepage": "https://github.com/modos189/nativescript-plugins",
|
|
37
|
+
"readmeFilename": "README.md",
|
|
38
|
+
"bootstrapper": "@nativescript/plugin-seed",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@nativescript-community/ui-webview": "^1.6.1"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
project.rootProject.allprojects {
|
|
2
|
+
repositories {
|
|
3
|
+
maven { url "https://maven.mozilla.org/maven2/" }
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
implementation "org.mozilla.geckoview:geckoview:149.0.20260403140140"
|
|
9
|
+
implementation "com.google.android.material:material:1.11.0"
|
|
10
|
+
}
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
package com.modos189.webviewxgecko;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.app.Dialog;
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import android.content.ContextWrapper;
|
|
7
|
+
import android.content.DialogInterface;
|
|
8
|
+
import android.graphics.Color;
|
|
9
|
+
import android.graphics.drawable.ColorDrawable;
|
|
10
|
+
import android.graphics.drawable.GradientDrawable;
|
|
11
|
+
import android.os.Bundle;
|
|
12
|
+
import android.os.Handler;
|
|
13
|
+
import android.os.Looper;
|
|
14
|
+
import android.util.TypedValue;
|
|
15
|
+
import android.view.Gravity;
|
|
16
|
+
import android.view.KeyEvent;
|
|
17
|
+
import android.view.MotionEvent;
|
|
18
|
+
import android.view.VelocityTracker;
|
|
19
|
+
import android.view.View;
|
|
20
|
+
import android.view.ViewConfiguration;
|
|
21
|
+
import android.view.ViewGroup;
|
|
22
|
+
import android.view.Window;
|
|
23
|
+
import android.widget.Button;
|
|
24
|
+
import android.widget.FrameLayout;
|
|
25
|
+
import android.widget.LinearLayout;
|
|
26
|
+
import android.widget.TextView;
|
|
27
|
+
import java.lang.ref.WeakReference;
|
|
28
|
+
import java.net.URLDecoder;
|
|
29
|
+
import org.mozilla.geckoview.AllowOrDeny;
|
|
30
|
+
import org.mozilla.geckoview.GeckoResult;
|
|
31
|
+
import org.mozilla.geckoview.GeckoRuntime;
|
|
32
|
+
import org.mozilla.geckoview.GeckoRuntimeSettings;
|
|
33
|
+
import org.mozilla.geckoview.GeckoSession;
|
|
34
|
+
import org.mozilla.geckoview.GeckoView;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Manages GeckoRuntime singleton and handles popup windows (window.open) natively
|
|
38
|
+
* via a slide-up Dialog — no JS bridge involvement in the popup flow.
|
|
39
|
+
*/
|
|
40
|
+
public class GeckoPopupHelper {
|
|
41
|
+
|
|
42
|
+
/** Callback so the host app can intercept popup navigations (e.g. OAuth redirects). */
|
|
43
|
+
public interface PopupUrlInterceptor {
|
|
44
|
+
/** Return true to intercept: the popup is dismissed and the URL is passed to the host. */
|
|
45
|
+
boolean shouldHandleExternally(String url);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private static GeckoRuntime sRuntime;
|
|
49
|
+
|
|
50
|
+
private final GeckoSession session;
|
|
51
|
+
private final WeakReference<Context> contextRef;
|
|
52
|
+
private volatile boolean supportPopups;
|
|
53
|
+
private PopupUrlInterceptor urlInterceptor;
|
|
54
|
+
|
|
55
|
+
public GeckoPopupHelper(GeckoSession session, Context context, boolean supportPopups) {
|
|
56
|
+
this.session = session;
|
|
57
|
+
this.contextRef = new WeakReference<>(context);
|
|
58
|
+
this.supportPopups = supportPopups;
|
|
59
|
+
setupNavigationDelegate();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public void setUrlInterceptor(PopupUrlInterceptor interceptor) {
|
|
63
|
+
this.urlInterceptor = interceptor;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public static GeckoRuntime getRuntime(Context context) {
|
|
67
|
+
if (sRuntime == null) {
|
|
68
|
+
Bundle extras = new Bundle();
|
|
69
|
+
// Allow window.open() without requiring a click event
|
|
70
|
+
extras.putString("pref.dom.disable_open_during_load", "false");
|
|
71
|
+
extras.putString("pref.dom.disable_open_click_delay", "0");
|
|
72
|
+
GeckoRuntimeSettings settings = new GeckoRuntimeSettings.Builder()
|
|
73
|
+
.extras(extras)
|
|
74
|
+
.build();
|
|
75
|
+
sRuntime = GeckoRuntime.create(context.getApplicationContext(), settings);
|
|
76
|
+
}
|
|
77
|
+
return sRuntime;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public static void setRemoteDebuggingEnabled(boolean enabled) {
|
|
81
|
+
if (sRuntime != null) {
|
|
82
|
+
sRuntime.getSettings().setRemoteDebuggingEnabled(enabled);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public void setSupportPopups(boolean value) {
|
|
87
|
+
this.supportPopups = value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private void setupNavigationDelegate() {
|
|
91
|
+
final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
92
|
+
|
|
93
|
+
session.setNavigationDelegate(new GeckoSession.NavigationDelegate() {
|
|
94
|
+
@Override
|
|
95
|
+
public GeckoResult<GeckoSession> onNewSession(final GeckoSession parent, final String uri) {
|
|
96
|
+
if (!supportPopups) return null;
|
|
97
|
+
|
|
98
|
+
final GeckoSession popup = new GeckoSession();
|
|
99
|
+
final PopupUrlInterceptor interceptor = GeckoPopupHelper.this.urlInterceptor;
|
|
100
|
+
mainHandler.post(new Runnable() {
|
|
101
|
+
public void run() {
|
|
102
|
+
Context ctx = contextRef.get();
|
|
103
|
+
if (ctx != null) showPopupDialog(ctx, popup, parent, uri, interceptor);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return GeckoResult.fromValue(popup);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private static Activity getActivity(Context context) {
|
|
112
|
+
while (context instanceof ContextWrapper) {
|
|
113
|
+
if (context instanceof Activity) return (Activity) context;
|
|
114
|
+
context = ((ContextWrapper) context).getBaseContext();
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private static void showPopupDialog(
|
|
120
|
+
Context context,
|
|
121
|
+
final GeckoSession popupSession,
|
|
122
|
+
final GeckoSession mainSession,
|
|
123
|
+
String uri,
|
|
124
|
+
final PopupUrlInterceptor urlInterceptor) {
|
|
125
|
+
|
|
126
|
+
final Activity activity = getActivity(context);
|
|
127
|
+
if (activity == null) return;
|
|
128
|
+
|
|
129
|
+
final float dp = activity.getResources().getDisplayMetrics().density;
|
|
130
|
+
final int toolbarH = (int) (48 * dp);
|
|
131
|
+
final int padH = (int) (20 * dp);
|
|
132
|
+
android.graphics.Rect initFrame = new android.graphics.Rect();
|
|
133
|
+
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(initFrame);
|
|
134
|
+
final int[] sheetH = {initFrame.height() - toolbarH / 2};
|
|
135
|
+
|
|
136
|
+
// Dim overlay on the Activity decor view
|
|
137
|
+
final ViewGroup activityDecor = (ViewGroup) activity.getWindow().getDecorView();
|
|
138
|
+
final View dimView = new View(activity);
|
|
139
|
+
dimView.setBackgroundColor(Color.argb(102, 0, 0, 0));
|
|
140
|
+
dimView.setAlpha(0f);
|
|
141
|
+
activityDecor.addView(dimView, new ViewGroup.LayoutParams(
|
|
142
|
+
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
143
|
+
dimView.animate().alpha(1f).setDuration(350).start();
|
|
144
|
+
|
|
145
|
+
final Dialog dialog = new Dialog(activity);
|
|
146
|
+
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
147
|
+
|
|
148
|
+
final int touchSlop = ViewConfiguration.get(activity).getScaledTouchSlop();
|
|
149
|
+
final float[] dragStartRawY = {0f};
|
|
150
|
+
final float[] interceptDownY = {0f};
|
|
151
|
+
final VelocityTracker[] vt = {null};
|
|
152
|
+
// Set after content is built; safe because it's only invoked after dialog.show().
|
|
153
|
+
final Runnable[] doAnimatedDismiss = {null};
|
|
154
|
+
|
|
155
|
+
// Sheet root — recalculates height on rotation
|
|
156
|
+
final LinearLayout content = new LinearLayout(activity) {
|
|
157
|
+
@Override
|
|
158
|
+
protected void onMeasure(int widthSpec, int heightSpec) {
|
|
159
|
+
// Query the dialog's own window frame; the activity window frame would
|
|
160
|
+
// not reflect keyboard insets inside a separate dialog window.
|
|
161
|
+
android.graphics.Rect fr = new android.graphics.Rect();
|
|
162
|
+
getWindowVisibleDisplayFrame(fr);
|
|
163
|
+
if (fr.height() > 0) sheetH[0] = fr.height() - toolbarH / 2;
|
|
164
|
+
super.onMeasure(widthSpec,
|
|
165
|
+
MeasureSpec.makeMeasureSpec(sheetH[0], MeasureSpec.EXACTLY));
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
content.setOrientation(LinearLayout.VERTICAL);
|
|
169
|
+
GradientDrawable sheetBg = new GradientDrawable();
|
|
170
|
+
sheetBg.setColor(Color.parseColor("#222222"));
|
|
171
|
+
float cornerR = 16 * dp;
|
|
172
|
+
sheetBg.setCornerRadii(new float[]{cornerR, cornerR, cornerR, cornerR, 0, 0, 0, 0});
|
|
173
|
+
content.setBackground(sheetBg);
|
|
174
|
+
|
|
175
|
+
// Toolbar — drag downward to dismiss
|
|
176
|
+
final LinearLayout toolbar = new LinearLayout(activity) {
|
|
177
|
+
private float downY;
|
|
178
|
+
private boolean dragging;
|
|
179
|
+
|
|
180
|
+
@Override
|
|
181
|
+
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
|
182
|
+
switch (ev.getAction()) {
|
|
183
|
+
case MotionEvent.ACTION_DOWN:
|
|
184
|
+
downY = ev.getRawY(); dragging = false; break;
|
|
185
|
+
case MotionEvent.ACTION_MOVE:
|
|
186
|
+
if (!dragging && ev.getRawY() - downY > touchSlop) {
|
|
187
|
+
dragging = true;
|
|
188
|
+
dragStartRawY[0] = ev.getRawY();
|
|
189
|
+
if (vt[0] != null) vt[0].recycle();
|
|
190
|
+
vt[0] = VelocityTracker.obtain();
|
|
191
|
+
vt[0].addMovement(ev);
|
|
192
|
+
content.animate().cancel();
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL:
|
|
196
|
+
dragging = false; break;
|
|
197
|
+
}
|
|
198
|
+
return dragging;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Override
|
|
202
|
+
public boolean onTouchEvent(MotionEvent ev) {
|
|
203
|
+
switch (ev.getAction()) {
|
|
204
|
+
case MotionEvent.ACTION_DOWN:
|
|
205
|
+
downY = ev.getRawY(); dragging = false; return true;
|
|
206
|
+
case MotionEvent.ACTION_MOVE:
|
|
207
|
+
if (!dragging) {
|
|
208
|
+
if (ev.getRawY() - downY > touchSlop) {
|
|
209
|
+
dragging = true;
|
|
210
|
+
dragStartRawY[0] = ev.getRawY();
|
|
211
|
+
if (vt[0] != null) vt[0].recycle();
|
|
212
|
+
vt[0] = VelocityTracker.obtain();
|
|
213
|
+
vt[0].addMovement(ev);
|
|
214
|
+
content.animate().cancel();
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
float delta = ev.getRawY() - dragStartRawY[0];
|
|
219
|
+
if (delta > 0) content.setTranslationY(delta);
|
|
220
|
+
if (vt[0] != null) vt[0].addMovement(ev);
|
|
221
|
+
return true;
|
|
222
|
+
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL:
|
|
223
|
+
if (dragging) {
|
|
224
|
+
dragging = false;
|
|
225
|
+
float totalDelta = ev.getRawY() - dragStartRawY[0];
|
|
226
|
+
float yVelocity = 0;
|
|
227
|
+
if (vt[0] != null) {
|
|
228
|
+
vt[0].addMovement(ev);
|
|
229
|
+
vt[0].computeCurrentVelocity(1000);
|
|
230
|
+
yVelocity = vt[0].getYVelocity();
|
|
231
|
+
vt[0].recycle();
|
|
232
|
+
vt[0] = null;
|
|
233
|
+
}
|
|
234
|
+
if (totalDelta > sheetH[0] * 0.3f || yVelocity > 1000)
|
|
235
|
+
doAnimatedDismiss[0].run();
|
|
236
|
+
else
|
|
237
|
+
content.animate().translationY(0).setDuration(200).start();
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
toolbar.setOrientation(LinearLayout.HORIZONTAL);
|
|
245
|
+
toolbar.setGravity(Gravity.CENTER_VERTICAL);
|
|
246
|
+
content.addView(toolbar, new LinearLayout.LayoutParams(
|
|
247
|
+
LinearLayout.LayoutParams.MATCH_PARENT, toolbarH));
|
|
248
|
+
|
|
249
|
+
final TextView titleView = new TextView(activity);
|
|
250
|
+
String initialHost = "loading...";
|
|
251
|
+
try {
|
|
252
|
+
String h = new java.net.URI(uri).getHost();
|
|
253
|
+
if (h != null && !h.isEmpty()) initialHost = h;
|
|
254
|
+
} catch (Exception ignored) {}
|
|
255
|
+
titleView.setText(initialHost);
|
|
256
|
+
titleView.setTextColor(Color.WHITE);
|
|
257
|
+
titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
|
|
258
|
+
titleView.setIncludeFontPadding(false);
|
|
259
|
+
titleView.setPadding(padH, 0, 0, 0);
|
|
260
|
+
toolbar.addView(titleView, new LinearLayout.LayoutParams(
|
|
261
|
+
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
|
|
262
|
+
|
|
263
|
+
final Button closeBtn = new Button(activity);
|
|
264
|
+
closeBtn.setText("✕");
|
|
265
|
+
closeBtn.setTextColor(Color.WHITE);
|
|
266
|
+
closeBtn.setBackgroundColor(Color.TRANSPARENT);
|
|
267
|
+
closeBtn.setMinWidth(0);
|
|
268
|
+
closeBtn.setMinimumWidth(0);
|
|
269
|
+
closeBtn.setGravity(Gravity.CENTER);
|
|
270
|
+
closeBtn.setPadding(padH / 2, 0, padH, 0);
|
|
271
|
+
toolbar.addView(closeBtn, new LinearLayout.LayoutParams(
|
|
272
|
+
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
273
|
+
LinearLayout.LayoutParams.MATCH_PARENT));
|
|
274
|
+
|
|
275
|
+
// GeckoView wrapper — intercepts downward swipe when scrolled to top
|
|
276
|
+
final GeckoView geckoView = new GeckoView(activity);
|
|
277
|
+
FrameLayout geckoWrapper = new FrameLayout(activity) {
|
|
278
|
+
private boolean intercepting;
|
|
279
|
+
|
|
280
|
+
@Override
|
|
281
|
+
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
|
282
|
+
switch (ev.getAction()) {
|
|
283
|
+
case MotionEvent.ACTION_DOWN:
|
|
284
|
+
interceptDownY[0] = ev.getRawY(); intercepting = false; break;
|
|
285
|
+
case MotionEvent.ACTION_MOVE:
|
|
286
|
+
if (!intercepting) {
|
|
287
|
+
float dy = ev.getRawY() - interceptDownY[0];
|
|
288
|
+
if (dy > touchSlop && geckoView.getScrollY() == 0) {
|
|
289
|
+
intercepting = true;
|
|
290
|
+
dragStartRawY[0] = ev.getRawY();
|
|
291
|
+
if (vt[0] != null) vt[0].recycle();
|
|
292
|
+
vt[0] = VelocityTracker.obtain();
|
|
293
|
+
vt[0].addMovement(ev);
|
|
294
|
+
content.animate().cancel();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
298
|
+
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL:
|
|
299
|
+
intercepting = false; break;
|
|
300
|
+
}
|
|
301
|
+
return intercepting;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@Override
|
|
305
|
+
public boolean onTouchEvent(MotionEvent ev) {
|
|
306
|
+
if (!intercepting) return false;
|
|
307
|
+
switch (ev.getAction()) {
|
|
308
|
+
case MotionEvent.ACTION_MOVE:
|
|
309
|
+
float delta = ev.getRawY() - dragStartRawY[0];
|
|
310
|
+
if (delta > 0) content.setTranslationY(delta);
|
|
311
|
+
if (vt[0] != null) vt[0].addMovement(ev);
|
|
312
|
+
return true;
|
|
313
|
+
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL:
|
|
314
|
+
intercepting = false;
|
|
315
|
+
float totalDelta = ev.getRawY() - dragStartRawY[0];
|
|
316
|
+
float yVelocity = 0;
|
|
317
|
+
if (vt[0] != null) {
|
|
318
|
+
vt[0].addMovement(ev);
|
|
319
|
+
vt[0].computeCurrentVelocity(1000);
|
|
320
|
+
yVelocity = vt[0].getYVelocity();
|
|
321
|
+
vt[0].recycle();
|
|
322
|
+
vt[0] = null;
|
|
323
|
+
}
|
|
324
|
+
if (totalDelta > sheetH[0] * 0.3f || yVelocity > 1000)
|
|
325
|
+
doAnimatedDismiss[0].run();
|
|
326
|
+
else
|
|
327
|
+
content.animate().translationY(0).setDuration(200).start();
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
geckoWrapper.addView(geckoView, new FrameLayout.LayoutParams(
|
|
334
|
+
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
|
335
|
+
content.addView(geckoWrapper, new LinearLayout.LayoutParams(
|
|
336
|
+
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f));
|
|
337
|
+
|
|
338
|
+
// Full-screen transparent root so the dialog window is MATCH_PARENT × MATCH_PARENT
|
|
339
|
+
FrameLayout dialogRoot = new FrameLayout(activity);
|
|
340
|
+
FrameLayout.LayoutParams contentLp = new FrameLayout.LayoutParams(
|
|
341
|
+
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
|
|
342
|
+
contentLp.gravity = Gravity.BOTTOM;
|
|
343
|
+
dialogRoot.addView(content, contentLp);
|
|
344
|
+
dialog.setContentView(dialogRoot);
|
|
345
|
+
|
|
346
|
+
Window window = dialog.getWindow();
|
|
347
|
+
if (window != null) {
|
|
348
|
+
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
|
349
|
+
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
|
350
|
+
window.setDimAmount(0f);
|
|
351
|
+
// Resize the dialog window to the visible area above the keyboard
|
|
352
|
+
window.setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
356
|
+
|
|
357
|
+
doAnimatedDismiss[0] = new Runnable() {
|
|
358
|
+
@Override public void run() {
|
|
359
|
+
float current = content.getTranslationY();
|
|
360
|
+
long duration = Math.max(80L, (long) (250 * (1f - current / sheetH[0])));
|
|
361
|
+
dimView.animate().alpha(0f).setDuration(duration).start();
|
|
362
|
+
content.animate()
|
|
363
|
+
.translationY(sheetH[0])
|
|
364
|
+
.setDuration(duration)
|
|
365
|
+
.withEndAction(new Runnable() {
|
|
366
|
+
@Override public void run() { dialog.dismiss(); }
|
|
367
|
+
})
|
|
368
|
+
.start();
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
final Runnable onClose = doAnimatedDismiss[0];
|
|
373
|
+
|
|
374
|
+
closeBtn.setOnClickListener(new View.OnClickListener() {
|
|
375
|
+
@Override public void onClick(View v) { doAnimatedDismiss[0].run(); }
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
|
379
|
+
@Override public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) {
|
|
380
|
+
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
|
|
381
|
+
doAnimatedDismiss[0].run();
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// Tracks whether the popup closed itself (gecko-bridge or window.close).
|
|
389
|
+
// If not, notify the main session so it can clean up.
|
|
390
|
+
final boolean[] closedByPopup = {false};
|
|
391
|
+
|
|
392
|
+
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
|
393
|
+
@Override public void onDismiss(DialogInterface d) {
|
|
394
|
+
activityDecor.removeView(dimView);
|
|
395
|
+
if (!closedByPopup[0]) {
|
|
396
|
+
mainSession.loadUri("javascript:window.__geckoPopupReturn && "
|
|
397
|
+
+ "window.__geckoPopupReturn(null)");
|
|
398
|
+
}
|
|
399
|
+
popupSession.close();
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Gecko delegates — navigation interception, gecko-bridge, title update, close
|
|
404
|
+
popupSession.setNavigationDelegate(new GeckoSession.NavigationDelegate() {
|
|
405
|
+
@Override
|
|
406
|
+
public GeckoResult<AllowOrDeny> onLoadRequest(
|
|
407
|
+
GeckoSession s, GeckoSession.NavigationDelegate.LoadRequest req) {
|
|
408
|
+
if (req.uri == null) return null;
|
|
409
|
+
|
|
410
|
+
if (!req.uri.startsWith("gecko-bridge://") && urlInterceptor != null
|
|
411
|
+
&& urlInterceptor.shouldHandleExternally(req.uri)) {
|
|
412
|
+
mainHandler.post(onClose);
|
|
413
|
+
return GeckoResult.fromValue(AllowOrDeny.DENY);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (req.uri.startsWith("gecko-bridge://return")) {
|
|
417
|
+
try {
|
|
418
|
+
String[] parts = req.uri.split("\\?data=", 2);
|
|
419
|
+
String json = parts.length > 1
|
|
420
|
+
? URLDecoder.decode(parts[1], "UTF-8")
|
|
421
|
+
: "null";
|
|
422
|
+
mainSession.loadUri("javascript:window.__geckoPopupReturn && "
|
|
423
|
+
+ "window.__geckoPopupReturn(" + json + ")");
|
|
424
|
+
} catch (Exception ignored) {}
|
|
425
|
+
closedByPopup[0] = true;
|
|
426
|
+
mainHandler.post(onClose);
|
|
427
|
+
return GeckoResult.fromValue(AllowOrDeny.DENY);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Update title bar host on navigation
|
|
431
|
+
try {
|
|
432
|
+
String h = new java.net.URI(req.uri).getHost();
|
|
433
|
+
if (h != null && !h.isEmpty()) {
|
|
434
|
+
final String host = h;
|
|
435
|
+
mainHandler.post(new Runnable() {
|
|
436
|
+
public void run() { titleView.setText(host); }
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
} catch (Exception ignored) {}
|
|
440
|
+
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
popupSession.setContentDelegate(new GeckoSession.ContentDelegate() {
|
|
446
|
+
@Override
|
|
447
|
+
public void onCloseRequest(GeckoSession session) {
|
|
448
|
+
closedByPopup[0] = true;
|
|
449
|
+
mainHandler.post(onClose);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Open session and show with slide-up entrance
|
|
454
|
+
geckoView.setSession(popupSession);
|
|
455
|
+
dialog.show();
|
|
456
|
+
content.setTranslationY(sheetH[0]);
|
|
457
|
+
content.animate().translationY(0).setDuration(350).start();
|
|
458
|
+
}
|
|
459
|
+
}
|
package/vue/index.d.ts
ADDED
package/vue/index.js
ADDED
package/vue/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/webview-x-gecko/vue/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG;IACb,OAAO,CAAC,GAAG;QACT,GAAG,CAAC,eAAe,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC;IACjE,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|