feat: estructura base OpenSpec + Skills + arquitectura limpia Capacitor+Dextie
This commit is contained in:
21
node_modules/@capacitor/android/LICENSE
generated
vendored
Normal file
21
node_modules/@capacitor/android/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-present Drifty Co.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
96
node_modules/@capacitor/android/capacitor/build.gradle
generated
vendored
Normal file
96
node_modules/@capacitor/android/capacitor/build.gradle
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
ext {
|
||||
androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.8.0'
|
||||
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
|
||||
androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.2.0'
|
||||
androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.12.0'
|
||||
androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.6.2'
|
||||
androidxWebkitVersion = project.hasProperty('androidxWebkitVersion') ? rootProject.ext.androidxWebkitVersion : '1.9.0'
|
||||
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
||||
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5'
|
||||
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1'
|
||||
cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '10.1.1'
|
||||
}
|
||||
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '1.9.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.2.1'
|
||||
|
||||
if (System.getenv("CAP_PUBLISH") == "true") {
|
||||
classpath 'io.github.gradle-nexus:publish-plugin:1.3.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc).all { enabled = false }
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
if (System.getenv("CAP_PUBLISH") == "true") {
|
||||
apply plugin: 'io.github.gradle-nexus.publish-plugin'
|
||||
apply from: file('../scripts/publish-root.gradle')
|
||||
apply from: file('../scripts/publish-module.gradle')
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "com.getcapacitor.android"
|
||||
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
||||
defaultConfig {
|
||||
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
|
||||
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
baseline file("lint-baseline.xml")
|
||||
abortOnError true
|
||||
warningsAsErrors true
|
||||
lintConfig file('lint.xml')
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
publishing {
|
||||
singleVariant("release")
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation platform("org.jetbrains.kotlin:kotlin-bom:$kotlin_version")
|
||||
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
||||
implementation "androidx.core:core:$androidxCoreVersion"
|
||||
implementation "androidx.activity:activity:$androidxActivityVersion"
|
||||
implementation "androidx.fragment:fragment:$androidxFragmentVersion"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
|
||||
implementation "androidx.webkit:webkit:$androidxWebkitVersion"
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
||||
implementation "org.apache.cordova:framework:$cordovaAndroidVersion"
|
||||
testImplementation 'org.json:json:20231013'
|
||||
testImplementation 'org.mockito:mockito-inline:5.2.0'
|
||||
}
|
||||
|
||||
136
node_modules/@capacitor/android/capacitor/lint-baseline.xml
generated
vendored
Normal file
136
node_modules/@capacitor/android/capacitor/lint-baseline.xml
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="5" by="lint 4.1.1" client="gradle" variant="all" version="4.1.1">
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
|
||||
errorLine1=" String msg = String.format("
|
||||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/BridgeWebChromeClient.java"
|
||||
line="474"
|
||||
column="26"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
|
||||
errorLine1=" return mask.toUpperCase().equals(string.toUpperCase());"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/util/HostMask.java"
|
||||
line="110"
|
||||
column="29"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
|
||||
errorLine1=" return mask.toUpperCase().equals(string.toUpperCase());"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/util/HostMask.java"
|
||||
line="110"
|
||||
column="57"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
|
||||
errorLine1=" switch (spinnerStyle.toLowerCase()) {"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/Splash.java"
|
||||
line="127"
|
||||
column="38"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
|
||||
errorLine1=" if (header.getKey().equalsIgnoreCase("Accept") && header.getValue().toLowerCase().contains("text/html")) {"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/WebViewLocalServer.java"
|
||||
line="327"
|
||||
column="89"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SimpleDateFormat"
|
||||
message="To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, or `getTimeInstance()`, or use `new SimpleDateFormat(String template, Locale locale)` with for example `Locale.US` for ASCII dates."
|
||||
errorLine1=" String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/BridgeWebChromeClient.java"
|
||||
line="511"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SimpleDateFormat"
|
||||
message="To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, or `getTimeInstance()`, or use `new SimpleDateFormat(String template, Locale locale)` with for example `Locale.US` for ASCII dates."
|
||||
errorLine1=" DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/PluginResult.java"
|
||||
line="44"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SetJavaScriptEnabled"
|
||||
message="Using `setJavaScriptEnabled` can introduce XSS vulnerabilities into your application, review carefully"
|
||||
errorLine1=" settings.setJavaScriptEnabled(true);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/Bridge.java"
|
||||
line="384"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Recycle"
|
||||
message="This `TypedArray` should be recycled after use with `#recycle()`"
|
||||
errorLine1=" TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.bridge_fragment);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/BridgeFragment.java"
|
||||
line="84"
|
||||
column="32"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="StaticFieldLeak"
|
||||
message="Do not place Android context classes in static fields; this is a memory leak"
|
||||
errorLine1=" private static ImageView splashImage;"
|
||||
errorLine2=" ~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/Splash.java"
|
||||
line="41"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="StaticFieldLeak"
|
||||
message="Do not place Android context classes in static fields; this is a memory leak"
|
||||
errorLine1=" private static ProgressBar spinnerBar;"
|
||||
errorLine2=" ~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/Splash.java"
|
||||
line="42"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Overdraw"
|
||||
message="Possible overdraw: Root element paints background `#F0FF1414` with a theme that also paints a background (inferred theme is `@android:style/Theme.Holo`)"
|
||||
errorLine1=" android:background="#F0FF1414""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/fragment_bridge.xml"
|
||||
line="5"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
</issues>
|
||||
9
node_modules/@capacitor/android/capacitor/lint.xml
generated
vendored
Normal file
9
node_modules/@capacitor/android/capacitor/lint.xml
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<issue id="GradleDependency" severity="ignore" />
|
||||
<issue id="AndroidGradlePluginVersion" severity="ignore" />
|
||||
<issue id="DiscouragedApi">
|
||||
<ignore path="src/main/java/com/getcapacitor/plugin/util/AssetUtil.java" />
|
||||
</issue>
|
||||
<issue id="ObsoleteSdkInt" severity="informational" />
|
||||
</lint>
|
||||
28
node_modules/@capacitor/android/capacitor/proguard-rules.pro
generated
vendored
Normal file
28
node_modules/@capacitor/android/capacitor/proguard-rules.pro
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Rules for Capacitor v3 plugins and annotations
|
||||
-keep @com.getcapacitor.annotation.CapacitorPlugin public class * {
|
||||
@com.getcapacitor.annotation.PermissionCallback <methods>;
|
||||
@com.getcapacitor.annotation.ActivityCallback <methods>;
|
||||
@com.getcapacitor.annotation.Permission <methods>;
|
||||
@com.getcapacitor.PluginMethod public <methods>;
|
||||
}
|
||||
|
||||
-keep public class * extends com.getcapacitor.Plugin { *; }
|
||||
|
||||
# Rules for Capacitor v2 plugins and annotations
|
||||
# These are deprecated but can still be used with Capacitor for now
|
||||
-keep @com.getcapacitor.NativePlugin public class * {
|
||||
@com.getcapacitor.PluginMethod public <methods>;
|
||||
}
|
||||
|
||||
# Rules for Cordova plugins
|
||||
-keep public class * extends org.apache.cordova.* {
|
||||
public <methods>;
|
||||
public <fields>;
|
||||
}
|
||||
3
node_modules/@capacitor/android/capacitor/src/main/AndroidManifest.xml
generated
vendored
Normal file
3
node_modules/@capacitor/android/capacitor/src/main/AndroidManifest.xml
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
</manifest>
|
||||
1034
node_modules/@capacitor/android/capacitor/src/main/assets/native-bridge.js
generated
vendored
Normal file
1034
node_modules/@capacitor/android/capacitor/src/main/assets/native-bridge.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
94
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/AndroidProtocolHandler.java
generated
vendored
Executable file
94
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/AndroidProtocolHandler.java
generated
vendored
Executable file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.util.TypedValue;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class AndroidProtocolHandler {
|
||||
|
||||
private Context context;
|
||||
|
||||
public AndroidProtocolHandler(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public InputStream openAsset(String path) throws IOException {
|
||||
return context.getAssets().open(path, AssetManager.ACCESS_STREAMING);
|
||||
}
|
||||
|
||||
public InputStream openResource(Uri uri) {
|
||||
assert uri.getPath() != null;
|
||||
// The path must be of the form ".../asset_type/asset_name.ext".
|
||||
List<String> pathSegments = uri.getPathSegments();
|
||||
String assetType = pathSegments.get(pathSegments.size() - 2);
|
||||
String assetName = pathSegments.get(pathSegments.size() - 1);
|
||||
|
||||
// Drop the file extension.
|
||||
assetName = assetName.split("\\.")[0];
|
||||
try {
|
||||
// Use the application context for resolving the resource package name so that we do
|
||||
// not use the browser's own resources. Note that if 'context' here belongs to the
|
||||
// test suite, it does not have a separate application context. In that case we use
|
||||
// the original context object directly.
|
||||
if (context.getApplicationContext() != null) {
|
||||
context = context.getApplicationContext();
|
||||
}
|
||||
int fieldId = getFieldId(context, assetType, assetName);
|
||||
int valueType = getValueType(context, fieldId);
|
||||
if (valueType == TypedValue.TYPE_STRING) {
|
||||
return context.getResources().openRawResource(fieldId);
|
||||
} else {
|
||||
Logger.error("Asset not of type string: " + uri);
|
||||
}
|
||||
} catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {
|
||||
Logger.error("Unable to open resource URL: " + uri, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getFieldId(Context context, String assetType, String assetName)
|
||||
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
|
||||
Class<?> d = context.getClassLoader().loadClass(context.getPackageName() + ".R$" + assetType);
|
||||
java.lang.reflect.Field field = d.getField(assetName);
|
||||
return field.getInt(null);
|
||||
}
|
||||
|
||||
public InputStream openFile(String filePath) throws IOException {
|
||||
String realPath = filePath.replace(Bridge.CAPACITOR_FILE_START, "");
|
||||
File localFile = new File(realPath);
|
||||
return new FileInputStream(localFile);
|
||||
}
|
||||
|
||||
public InputStream openContentUrl(Uri uri) throws IOException {
|
||||
Integer port = uri.getPort();
|
||||
String baseUrl = uri.getScheme() + "://" + uri.getHost();
|
||||
if (port != -1) {
|
||||
baseUrl += ":" + port;
|
||||
}
|
||||
String realPath = uri.toString().replace(baseUrl + Bridge.CAPACITOR_CONTENT_START, "content:/");
|
||||
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openInputStream(Uri.parse(realPath));
|
||||
} catch (SecurityException e) {
|
||||
Logger.error("Unable to open content URL: " + uri, e);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
private static int getValueType(Context context, int fieldId) {
|
||||
TypedValue value = new TypedValue();
|
||||
context.getResources().getValue(fieldId, value, true);
|
||||
return value.type;
|
||||
}
|
||||
}
|
||||
61
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/App.java
generated
vendored
Normal file
61
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/App.java
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class App {
|
||||
|
||||
/**
|
||||
* Interface for callbacks when app status changes.
|
||||
*/
|
||||
public interface AppStatusChangeListener {
|
||||
void onAppStatusChanged(Boolean isActive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for callbacks when app is restored with pending plugin call.
|
||||
*/
|
||||
public interface AppRestoredListener {
|
||||
void onAppRestored(PluginResult result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AppStatusChangeListener statusChangeListener;
|
||||
|
||||
@Nullable
|
||||
private AppRestoredListener appRestoredListener;
|
||||
|
||||
private boolean isActive = false;
|
||||
|
||||
public boolean isActive() {
|
||||
return isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object to receive callbacks.
|
||||
* @param listener
|
||||
*/
|
||||
public void setStatusChangeListener(@Nullable AppStatusChangeListener listener) {
|
||||
this.statusChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object to receive callbacks.
|
||||
* @param listener
|
||||
*/
|
||||
public void setAppRestoredListener(@Nullable AppRestoredListener listener) {
|
||||
this.appRestoredListener = listener;
|
||||
}
|
||||
|
||||
protected void fireRestoredResult(PluginResult result) {
|
||||
if (appRestoredListener != null) {
|
||||
appRestoredListener.onAppRestored(result);
|
||||
}
|
||||
}
|
||||
|
||||
public void fireStatusChange(boolean isActive) {
|
||||
this.isActive = isActive;
|
||||
if (statusChangeListener != null) {
|
||||
statusChangeListener.onAppStatusChanged(isActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/AppUUID.java
generated
vendored
Normal file
65
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/AppUUID.java
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class AppUUID {
|
||||
|
||||
private static final String KEY = "CapacitorAppUUID";
|
||||
|
||||
public static String getAppUUID(AppCompatActivity activity) throws Exception {
|
||||
assertAppUUID(activity);
|
||||
return readUUID(activity);
|
||||
}
|
||||
|
||||
public static void regenerateAppUUID(AppCompatActivity activity) throws Exception {
|
||||
try {
|
||||
String uuid = generateUUID();
|
||||
writeUUID(activity, uuid);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new Exception("Capacitor App UUID could not be generated.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertAppUUID(AppCompatActivity activity) throws Exception {
|
||||
String uuid = readUUID(activity);
|
||||
if (uuid.equals("")) {
|
||||
regenerateAppUUID(activity);
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateUUID() throws NoSuchAlgorithmException {
|
||||
MessageDigest salt = MessageDigest.getInstance("SHA-256");
|
||||
salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
|
||||
return bytesToHex(salt.digest());
|
||||
}
|
||||
|
||||
private static String readUUID(AppCompatActivity activity) {
|
||||
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
|
||||
return sharedPref.getString(KEY, "");
|
||||
}
|
||||
|
||||
private static void writeUUID(AppCompatActivity activity, String uuid) {
|
||||
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPref.edit();
|
||||
editor.putString(KEY, uuid);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] hexChars = new byte[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
1595
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/Bridge.java
generated
vendored
Normal file
1595
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/Bridge.java
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
212
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java
generated
vendored
Normal file
212
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import com.getcapacitor.android.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BridgeActivity extends AppCompatActivity {
|
||||
|
||||
protected Bridge bridge;
|
||||
protected boolean keepRunning = true;
|
||||
protected CapConfig config;
|
||||
|
||||
protected int activityDepth = 0;
|
||||
protected List<Class<? extends Plugin>> initialPlugins = new ArrayList<>();
|
||||
protected final Bridge.Builder bridgeBuilder = new Bridge.Builder(this);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
bridgeBuilder.setInstanceState(savedInstanceState);
|
||||
getApplication().setTheme(R.style.AppTheme_NoActionBar);
|
||||
setTheme(R.style.AppTheme_NoActionBar);
|
||||
try {
|
||||
setContentView(R.layout.bridge_layout_main);
|
||||
} catch (Exception ex) {
|
||||
setContentView(R.layout.no_webview);
|
||||
return;
|
||||
}
|
||||
|
||||
PluginManager loader = new PluginManager(getAssets());
|
||||
|
||||
try {
|
||||
bridgeBuilder.addPlugins(loader.loadPluginClasses());
|
||||
} catch (PluginLoadException ex) {
|
||||
Logger.error("Error loading plugins.", ex);
|
||||
}
|
||||
|
||||
this.load();
|
||||
}
|
||||
|
||||
protected void load() {
|
||||
Logger.debug("Starting BridgeActivity");
|
||||
|
||||
bridge = bridgeBuilder.addPlugins(initialPlugins).setConfig(config).create();
|
||||
|
||||
this.keepRunning = bridge.shouldKeepRunning();
|
||||
this.onNewIntent(getIntent());
|
||||
}
|
||||
|
||||
public void registerPlugin(Class<? extends Plugin> plugin) {
|
||||
bridgeBuilder.addPlugin(plugin);
|
||||
}
|
||||
|
||||
public void registerPlugins(List<Class<? extends Plugin>> plugins) {
|
||||
bridgeBuilder.addPlugins(plugins);
|
||||
}
|
||||
|
||||
public Bridge getBridge() {
|
||||
return this.bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
bridge.saveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
activityDepth++;
|
||||
if (this.bridge != null) {
|
||||
this.bridge.onStart();
|
||||
Logger.debug("App started");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestart() {
|
||||
super.onRestart();
|
||||
this.bridge.onRestart();
|
||||
Logger.debug("App restarted");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (bridge != null) {
|
||||
bridge.getApp().fireStatusChange(true);
|
||||
this.bridge.onResume();
|
||||
Logger.debug("App resumed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (bridge != null) {
|
||||
this.bridge.onPause();
|
||||
Logger.debug("App paused");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
if (bridge != null) {
|
||||
activityDepth = Math.max(0, activityDepth - 1);
|
||||
if (activityDepth == 0) {
|
||||
bridge.getApp().fireStatusChange(false);
|
||||
}
|
||||
|
||||
this.bridge.onStop();
|
||||
Logger.debug("App stopped");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (this.bridge != null) {
|
||||
this.bridge.onDestroy();
|
||||
Logger.debug("App destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
this.bridge.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles permission request results.
|
||||
*
|
||||
* Capacitor is backwards compatible such that plugins using legacy permission request codes
|
||||
* may coexist with plugins using the AndroidX Activity v1.2 permission callback flow introduced
|
||||
* in Capacitor 3.0.
|
||||
*
|
||||
* In this method, plugins are checked first for ownership of the legacy permission request code.
|
||||
* If the {@link Bridge#onRequestPermissionsResult(int, String[], int[])} method indicates it has
|
||||
* handled the permission, then the permission callback will be considered complete. Otherwise,
|
||||
* the permission will be handled using the AndroidX Activity flow.
|
||||
*
|
||||
* @param requestCode the request code associated with the permission request
|
||||
* @param permissions the Android permission strings requested
|
||||
* @param grantResults the status result of the permission request
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
if (this.bridge == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bridge.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles activity results.
|
||||
*
|
||||
* Capacitor is backwards compatible such that plugins using legacy activity result codes
|
||||
* may coexist with plugins using the AndroidX Activity v1.2 activity callback flow introduced
|
||||
* in Capacitor 3.0.
|
||||
*
|
||||
* In this method, plugins are checked first for ownership of the legacy request code. If the
|
||||
* {@link Bridge#onActivityResult(int, int, Intent)} method indicates it has handled the activity
|
||||
* result, then the callback will be considered complete. Otherwise, the result will be handled
|
||||
* using the AndroidX Activiy flow.
|
||||
*
|
||||
* @param requestCode the request code associated with the activity result
|
||||
* @param resultCode the result code
|
||||
* @param data any data included with the activity result
|
||||
*/
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (this.bridge == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bridge.onActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
if (this.bridge == null || intent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bridge.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
if (this.bridge == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bridge.onConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
||||
134
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeFragment.java
generated
vendored
Normal file
134
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeFragment.java
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import com.getcapacitor.android.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
* Use the {@link BridgeFragment#newInstance} factory method to
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
public class BridgeFragment extends Fragment {
|
||||
|
||||
private static final String ARG_START_DIR = "startDir";
|
||||
|
||||
protected Bridge bridge;
|
||||
protected boolean keepRunning = true;
|
||||
|
||||
private final List<Class<? extends Plugin>> initialPlugins = new ArrayList<>();
|
||||
private CapConfig config = null;
|
||||
|
||||
private final List<WebViewListener> webViewListeners = new ArrayList<>();
|
||||
|
||||
public BridgeFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this factory method to create a new instance of
|
||||
* this fragment using the provided parameters.
|
||||
*
|
||||
* @param startDir the directory to serve content from
|
||||
* @return A new instance of fragment BridgeFragment.
|
||||
*/
|
||||
public static BridgeFragment newInstance(String startDir) {
|
||||
BridgeFragment fragment = new BridgeFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_START_DIR, startDir);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public void addPlugin(Class<? extends Plugin> plugin) {
|
||||
this.initialPlugins.add(plugin);
|
||||
}
|
||||
|
||||
public void setConfig(CapConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public Bridge getBridge() {
|
||||
return bridge;
|
||||
}
|
||||
|
||||
public void addWebViewListener(WebViewListener webViewListener) {
|
||||
webViewListeners.add(webViewListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the WebView and create the Bridge
|
||||
*/
|
||||
protected void load(Bundle savedInstanceState) {
|
||||
Logger.debug("Loading Bridge with BridgeFragment");
|
||||
|
||||
Bundle args = getArguments();
|
||||
String startDir = null;
|
||||
|
||||
if (args != null) {
|
||||
startDir = getArguments().getString(ARG_START_DIR);
|
||||
}
|
||||
|
||||
bridge =
|
||||
new Bridge.Builder(this)
|
||||
.setInstanceState(savedInstanceState)
|
||||
.setPlugins(initialPlugins)
|
||||
.setConfig(config)
|
||||
.addWebViewListeners(webViewListeners)
|
||||
.create();
|
||||
|
||||
if (startDir != null) {
|
||||
bridge.setServerAssetPath(startDir);
|
||||
}
|
||||
|
||||
this.keepRunning = bridge.shouldKeepRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
|
||||
super.onInflate(context, attrs, savedInstanceState);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.bridge_fragment);
|
||||
CharSequence c = a.getString(R.styleable.bridge_fragment_start_dir);
|
||||
|
||||
if (c != null) {
|
||||
String startDir = c.toString();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_START_DIR, startDir);
|
||||
setArguments(args);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_bridge, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
this.load(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (this.bridge != null) {
|
||||
this.bridge.onDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
510
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java
generated
vendored
Normal file
510
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java
generated
vendored
Normal file
@@ -0,0 +1,510 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.view.View;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.GeolocationPermissions;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.webkit.PermissionRequest;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.EditText;
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.core.content.FileProvider;
|
||||
import com.getcapacitor.util.PermissionHelper;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Custom WebChromeClient handler, required for showing dialogs, confirms, etc. in our
|
||||
* WebView instance.
|
||||
*/
|
||||
public class BridgeWebChromeClient extends WebChromeClient {
|
||||
|
||||
private interface PermissionListener {
|
||||
void onPermissionSelect(Boolean isGranted);
|
||||
}
|
||||
|
||||
private interface ActivityResultListener {
|
||||
void onActivityResult(ActivityResult result);
|
||||
}
|
||||
|
||||
private ActivityResultLauncher permissionLauncher;
|
||||
private ActivityResultLauncher activityLauncher;
|
||||
private PermissionListener permissionListener;
|
||||
private ActivityResultListener activityListener;
|
||||
|
||||
private Bridge bridge;
|
||||
|
||||
public BridgeWebChromeClient(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
|
||||
ActivityResultCallback<Map<String, Boolean>> permissionCallback = (Map<String, Boolean> isGranted) -> {
|
||||
if (permissionListener != null) {
|
||||
boolean granted = true;
|
||||
for (Map.Entry<String, Boolean> permission : isGranted.entrySet()) {
|
||||
if (!permission.getValue()) granted = false;
|
||||
}
|
||||
permissionListener.onPermissionSelect(granted);
|
||||
}
|
||||
};
|
||||
|
||||
permissionLauncher = bridge.registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), permissionCallback);
|
||||
activityLauncher =
|
||||
bridge.registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (activityListener != null) {
|
||||
activityListener.onActivityResult(result);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render web content in `view`.
|
||||
*
|
||||
* Both this method and {@link #onHideCustomView()} are required for
|
||||
* rendering web content in full screen.
|
||||
*
|
||||
* @see <a href="https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback)">onShowCustomView() docs</a>
|
||||
*/
|
||||
@Override
|
||||
public void onShowCustomView(View view, CustomViewCallback callback) {
|
||||
callback.onCustomViewHidden();
|
||||
super.onShowCustomView(view, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render web content in the original Web View again.
|
||||
*
|
||||
* Do not remove this method--@see #onShowCustomView(View, CustomViewCallback).
|
||||
*/
|
||||
@Override
|
||||
public void onHideCustomView() {
|
||||
super.onHideCustomView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionRequest(final PermissionRequest request) {
|
||||
boolean isRequestPermissionRequired = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M;
|
||||
|
||||
List<String> permissionList = new ArrayList<>();
|
||||
if (Arrays.asList(request.getResources()).contains("android.webkit.resource.VIDEO_CAPTURE")) {
|
||||
permissionList.add(Manifest.permission.CAMERA);
|
||||
}
|
||||
if (Arrays.asList(request.getResources()).contains("android.webkit.resource.AUDIO_CAPTURE")) {
|
||||
permissionList.add(Manifest.permission.MODIFY_AUDIO_SETTINGS);
|
||||
permissionList.add(Manifest.permission.RECORD_AUDIO);
|
||||
}
|
||||
if (!permissionList.isEmpty() && isRequestPermissionRequired) {
|
||||
String[] permissions = permissionList.toArray(new String[0]);
|
||||
permissionListener =
|
||||
isGranted -> {
|
||||
if (isGranted) {
|
||||
request.grant(request.getResources());
|
||||
} else {
|
||||
request.deny();
|
||||
}
|
||||
};
|
||||
permissionLauncher.launch(permissions);
|
||||
} else {
|
||||
request.grant(request.getResources());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the browser alert modal
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
||||
if (bridge.getActivity().isFinishing()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
|
||||
builder
|
||||
.setMessage(message)
|
||||
.setPositiveButton(
|
||||
"OK",
|
||||
(dialog, buttonIndex) -> {
|
||||
dialog.dismiss();
|
||||
result.confirm();
|
||||
}
|
||||
)
|
||||
.setOnCancelListener(
|
||||
dialog -> {
|
||||
dialog.dismiss();
|
||||
result.cancel();
|
||||
}
|
||||
);
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
dialog.show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the browser confirm modal
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
||||
if (bridge.getActivity().isFinishing()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
|
||||
|
||||
builder
|
||||
.setMessage(message)
|
||||
.setPositiveButton(
|
||||
"OK",
|
||||
(dialog, buttonIndex) -> {
|
||||
dialog.dismiss();
|
||||
result.confirm();
|
||||
}
|
||||
)
|
||||
.setNegativeButton(
|
||||
"Cancel",
|
||||
(dialog, buttonIndex) -> {
|
||||
dialog.dismiss();
|
||||
result.cancel();
|
||||
}
|
||||
)
|
||||
.setOnCancelListener(
|
||||
dialog -> {
|
||||
dialog.dismiss();
|
||||
result.cancel();
|
||||
}
|
||||
);
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
dialog.show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the browser prompt modal
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param defaultValue
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
|
||||
if (bridge.getActivity().isFinishing()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
|
||||
final EditText input = new EditText(view.getContext());
|
||||
|
||||
builder
|
||||
.setMessage(message)
|
||||
.setView(input)
|
||||
.setPositiveButton(
|
||||
"OK",
|
||||
(dialog, buttonIndex) -> {
|
||||
dialog.dismiss();
|
||||
|
||||
String inputText1 = input.getText().toString().trim();
|
||||
result.confirm(inputText1);
|
||||
}
|
||||
)
|
||||
.setNegativeButton(
|
||||
"Cancel",
|
||||
(dialog, buttonIndex) -> {
|
||||
dialog.dismiss();
|
||||
result.cancel();
|
||||
}
|
||||
)
|
||||
.setOnCancelListener(
|
||||
dialog -> {
|
||||
dialog.dismiss();
|
||||
result.cancel();
|
||||
}
|
||||
);
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
dialog.show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the browser geolocation permission prompt
|
||||
* @param origin
|
||||
* @param callback
|
||||
*/
|
||||
@Override
|
||||
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
Logger.debug("onGeolocationPermissionsShowPrompt: DOING IT HERE FOR ORIGIN: " + origin);
|
||||
final String[] geoPermissions = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION };
|
||||
|
||||
if (!PermissionHelper.hasPermissions(bridge.getContext(), geoPermissions)) {
|
||||
permissionListener =
|
||||
isGranted -> {
|
||||
if (isGranted) {
|
||||
callback.invoke(origin, true, false);
|
||||
} else {
|
||||
final String[] coarsePermission = { Manifest.permission.ACCESS_COARSE_LOCATION };
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
|
||||
PermissionHelper.hasPermissions(bridge.getContext(), coarsePermission)
|
||||
) {
|
||||
callback.invoke(origin, true, false);
|
||||
} else {
|
||||
callback.invoke(origin, false, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
permissionLauncher.launch(geoPermissions);
|
||||
} else {
|
||||
// permission is already granted
|
||||
callback.invoke(origin, true, false);
|
||||
Logger.debug("onGeolocationPermissionsShowPrompt: has required permission");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShowFileChooser(
|
||||
WebView webView,
|
||||
final ValueCallback<Uri[]> filePathCallback,
|
||||
final FileChooserParams fileChooserParams
|
||||
) {
|
||||
List<String> acceptTypes = Arrays.asList(fileChooserParams.getAcceptTypes());
|
||||
boolean captureEnabled = fileChooserParams.isCaptureEnabled();
|
||||
boolean capturePhoto = captureEnabled && acceptTypes.contains("image/*");
|
||||
final boolean captureVideo = captureEnabled && acceptTypes.contains("video/*");
|
||||
if ((capturePhoto || captureVideo)) {
|
||||
if (isMediaCaptureSupported()) {
|
||||
showMediaCaptureOrFilePicker(filePathCallback, fileChooserParams, captureVideo);
|
||||
} else {
|
||||
permissionListener =
|
||||
isGranted -> {
|
||||
if (isGranted) {
|
||||
showMediaCaptureOrFilePicker(filePathCallback, fileChooserParams, captureVideo);
|
||||
} else {
|
||||
Logger.warn(Logger.tags("FileChooser"), "Camera permission not granted");
|
||||
filePathCallback.onReceiveValue(null);
|
||||
}
|
||||
};
|
||||
final String[] camPermission = { Manifest.permission.CAMERA };
|
||||
permissionLauncher.launch(camPermission);
|
||||
}
|
||||
} else {
|
||||
showFilePicker(filePathCallback, fileChooserParams);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isMediaCaptureSupported() {
|
||||
String[] permissions = { Manifest.permission.CAMERA };
|
||||
return (
|
||||
PermissionHelper.hasPermissions(bridge.getContext(), permissions) ||
|
||||
!PermissionHelper.hasDefinedPermission(bridge.getContext(), Manifest.permission.CAMERA)
|
||||
);
|
||||
}
|
||||
|
||||
private void showMediaCaptureOrFilePicker(ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams, boolean isVideo) {
|
||||
// TODO: add support for video capture on Android M and older
|
||||
// On Android M and lower the VIDEO_CAPTURE_INTENT (e.g.: intent.getData())
|
||||
// returns a file:// URI instead of the expected content:// URI.
|
||||
// So we disable it for now because it requires a bit more work
|
||||
boolean isVideoCaptureSupported = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N;
|
||||
boolean shown = false;
|
||||
if (isVideo && isVideoCaptureSupported) {
|
||||
shown = showVideoCapturePicker(filePathCallback);
|
||||
} else {
|
||||
shown = showImageCapturePicker(filePathCallback);
|
||||
}
|
||||
if (!shown) {
|
||||
Logger.warn(Logger.tags("FileChooser"), "Media capture intent could not be launched. Falling back to default file picker.");
|
||||
showFilePicker(filePathCallback, fileChooserParams);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
private boolean showImageCapturePicker(final ValueCallback<Uri[]> filePathCallback) {
|
||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
if (takePictureIntent.resolveActivity(bridge.getActivity().getPackageManager()) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Uri imageFileUri;
|
||||
try {
|
||||
imageFileUri = createImageFileUri();
|
||||
} catch (Exception ex) {
|
||||
Logger.error("Unable to create temporary media capture file: " + ex.getMessage());
|
||||
return false;
|
||||
}
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
|
||||
activityListener =
|
||||
activityResult -> {
|
||||
Uri[] result = null;
|
||||
if (activityResult.getResultCode() == Activity.RESULT_OK) {
|
||||
result = new Uri[] { imageFileUri };
|
||||
}
|
||||
filePathCallback.onReceiveValue(result);
|
||||
};
|
||||
activityLauncher.launch(takePictureIntent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
private boolean showVideoCapturePicker(final ValueCallback<Uri[]> filePathCallback) {
|
||||
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
if (takeVideoIntent.resolveActivity(bridge.getActivity().getPackageManager()) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
activityListener =
|
||||
activityResult -> {
|
||||
Uri[] result = null;
|
||||
if (activityResult.getResultCode() == Activity.RESULT_OK) {
|
||||
result = new Uri[] { activityResult.getData().getData() };
|
||||
}
|
||||
filePathCallback.onReceiveValue(result);
|
||||
};
|
||||
activityLauncher.launch(takeVideoIntent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showFilePicker(final ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
||||
Intent intent = fileChooserParams.createIntent();
|
||||
if (fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE) {
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||
}
|
||||
if (fileChooserParams.getAcceptTypes().length > 1 || intent.getType().startsWith(".")) {
|
||||
String[] validTypes = getValidTypes(fileChooserParams.getAcceptTypes());
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, validTypes);
|
||||
if (intent.getType().startsWith(".")) {
|
||||
intent.setType(validTypes[0]);
|
||||
}
|
||||
}
|
||||
try {
|
||||
activityListener =
|
||||
activityResult -> {
|
||||
Uri[] result;
|
||||
Intent resultIntent = activityResult.getData();
|
||||
if (activityResult.getResultCode() == Activity.RESULT_OK && resultIntent.getClipData() != null) {
|
||||
final int numFiles = resultIntent.getClipData().getItemCount();
|
||||
result = new Uri[numFiles];
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
result[i] = resultIntent.getClipData().getItemAt(i).getUri();
|
||||
}
|
||||
} else {
|
||||
result = WebChromeClient.FileChooserParams.parseResult(activityResult.getResultCode(), resultIntent);
|
||||
}
|
||||
filePathCallback.onReceiveValue(result);
|
||||
};
|
||||
activityLauncher.launch(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
filePathCallback.onReceiveValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getValidTypes(String[] currentTypes) {
|
||||
List<String> validTypes = new ArrayList<>();
|
||||
MimeTypeMap mtm = MimeTypeMap.getSingleton();
|
||||
for (String mime : currentTypes) {
|
||||
if (mime.startsWith(".")) {
|
||||
String extension = mime.substring(1);
|
||||
String extensionMime = mtm.getMimeTypeFromExtension(extension);
|
||||
if (extensionMime != null && !validTypes.contains(extensionMime)) {
|
||||
validTypes.add(extensionMime);
|
||||
}
|
||||
} else if (!validTypes.contains(mime)) {
|
||||
validTypes.add(mime);
|
||||
}
|
||||
}
|
||||
Object[] validObj = validTypes.toArray();
|
||||
return Arrays.copyOf(validObj, validObj.length, String[].class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
|
||||
String tag = Logger.tags("Console");
|
||||
if (consoleMessage.message() != null && isValidMsg(consoleMessage.message())) {
|
||||
String msg = String.format(
|
||||
"File: %s - Line %d - Msg: %s",
|
||||
consoleMessage.sourceId(),
|
||||
consoleMessage.lineNumber(),
|
||||
consoleMessage.message()
|
||||
);
|
||||
String level = consoleMessage.messageLevel().name();
|
||||
if ("ERROR".equalsIgnoreCase(level)) {
|
||||
Logger.error(tag, msg, null);
|
||||
} else if ("WARNING".equalsIgnoreCase(level)) {
|
||||
Logger.warn(tag, msg);
|
||||
} else if ("TIP".equalsIgnoreCase(level)) {
|
||||
Logger.debug(tag, msg);
|
||||
} else {
|
||||
Logger.info(tag, msg);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isValidMsg(String msg) {
|
||||
return !(
|
||||
msg.contains("%cresult %c") ||
|
||||
(msg.contains("%cnative %c")) ||
|
||||
msg.equalsIgnoreCase("[object Object]") ||
|
||||
msg.equalsIgnoreCase("console.groupEnd")
|
||||
);
|
||||
}
|
||||
|
||||
private Uri createImageFileUri() throws IOException {
|
||||
Activity activity = bridge.getActivity();
|
||||
File photoFile = createImageFile(activity);
|
||||
return FileProvider.getUriForFile(activity, bridge.getContext().getPackageName() + ".fileprovider", photoFile);
|
||||
}
|
||||
|
||||
private File createImageFile(Activity activity) throws IOException {
|
||||
// Create an image file name
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String imageFileName = "JPEG_" + timeStamp + "_";
|
||||
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||
|
||||
return File.createTempFile(imageFileName, ".jpg", storageDir);
|
||||
}
|
||||
}
|
||||
111
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java
generated
vendored
Normal file
111
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.WebResourceError;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import java.util.List;
|
||||
|
||||
public class BridgeWebViewClient extends WebViewClient {
|
||||
|
||||
private Bridge bridge;
|
||||
|
||||
public BridgeWebViewClient(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return bridge.getLocalServer().shouldInterceptRequest(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
Uri url = request.getUrl();
|
||||
return bridge.launchIntent(url);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
return bridge.launchIntent(Uri.parse(url));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
|
||||
|
||||
if (webViewListeners != null && view.getProgress() == 100) {
|
||||
for (WebViewListener listener : bridge.getWebViewListeners()) {
|
||||
listener.onPageLoaded(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
||||
super.onReceivedError(view, request, error);
|
||||
|
||||
List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
|
||||
if (webViewListeners != null) {
|
||||
for (WebViewListener listener : bridge.getWebViewListeners()) {
|
||||
listener.onReceivedError(view);
|
||||
}
|
||||
}
|
||||
|
||||
String errorPath = bridge.getErrorUrl();
|
||||
if (errorPath != null && request.isForMainFrame()) {
|
||||
view.loadUrl(errorPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
super.onPageStarted(view, url, favicon);
|
||||
bridge.reset();
|
||||
List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
|
||||
|
||||
if (webViewListeners != null) {
|
||||
for (WebViewListener listener : bridge.getWebViewListeners()) {
|
||||
listener.onPageStarted(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
|
||||
super.onReceivedHttpError(view, request, errorResponse);
|
||||
|
||||
List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
|
||||
if (webViewListeners != null) {
|
||||
for (WebViewListener listener : bridge.getWebViewListeners()) {
|
||||
listener.onReceivedHttpError(view);
|
||||
}
|
||||
}
|
||||
|
||||
String errorPath = bridge.getErrorUrl();
|
||||
if (errorPath != null && request.isForMainFrame()) {
|
||||
view.loadUrl(errorPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
|
||||
super.onRenderProcessGone(view, detail);
|
||||
boolean result = false;
|
||||
|
||||
List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
|
||||
if (webViewListeners != null) {
|
||||
for (WebViewListener listener : bridge.getWebViewListeners()) {
|
||||
result = listener.onRenderProcessGone(view, detail) || result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
683
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java
generated
vendored
Normal file
683
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java
generated
vendored
Normal file
@@ -0,0 +1,683 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import static com.getcapacitor.Bridge.CAPACITOR_HTTPS_SCHEME;
|
||||
import static com.getcapacitor.Bridge.DEFAULT_ANDROID_WEBVIEW_VERSION;
|
||||
import static com.getcapacitor.Bridge.DEFAULT_HUAWEI_WEBVIEW_VERSION;
|
||||
import static com.getcapacitor.Bridge.MINIMUM_ANDROID_WEBVIEW_VERSION;
|
||||
import static com.getcapacitor.Bridge.MINIMUM_HUAWEI_WEBVIEW_VERSION;
|
||||
import static com.getcapacitor.FileUtils.readFileFromAssets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.AssetManager;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.getcapacitor.util.JSONUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Represents the configuration options for Capacitor
|
||||
*/
|
||||
public class CapConfig {
|
||||
|
||||
private static final String LOG_BEHAVIOR_NONE = "none";
|
||||
private static final String LOG_BEHAVIOR_DEBUG = "debug";
|
||||
private static final String LOG_BEHAVIOR_PRODUCTION = "production";
|
||||
|
||||
// Server Config
|
||||
private boolean html5mode = true;
|
||||
private String serverUrl;
|
||||
private String hostname = "localhost";
|
||||
private String androidScheme = CAPACITOR_HTTPS_SCHEME;
|
||||
private String[] allowNavigation;
|
||||
|
||||
// Android Config
|
||||
private String overriddenUserAgentString;
|
||||
private String appendedUserAgentString;
|
||||
private String backgroundColor;
|
||||
private boolean allowMixedContent = false;
|
||||
private boolean captureInput = false;
|
||||
private boolean webContentsDebuggingEnabled = false;
|
||||
private boolean loggingEnabled = true;
|
||||
private boolean initialFocus = true;
|
||||
private boolean useLegacyBridge = false;
|
||||
private int minWebViewVersion = DEFAULT_ANDROID_WEBVIEW_VERSION;
|
||||
private int minHuaweiWebViewVersion = DEFAULT_HUAWEI_WEBVIEW_VERSION;
|
||||
private String errorPath;
|
||||
private boolean zoomableWebView = false;
|
||||
|
||||
// Embedded
|
||||
private String startPath;
|
||||
|
||||
// Plugins
|
||||
private Map<String, PluginConfig> pluginsConfiguration = null;
|
||||
|
||||
// Config Object JSON (legacy)
|
||||
private JSONObject configJSON = new JSONObject();
|
||||
|
||||
/**
|
||||
* Constructs an empty config file.
|
||||
*/
|
||||
private CapConfig() {}
|
||||
|
||||
/**
|
||||
* Get an instance of the Config file object.
|
||||
* @deprecated use {@link #loadDefault(Context)} to load an instance of the Config object
|
||||
* from the capacitor.config.json file, or use the {@link CapConfig.Builder} to construct
|
||||
* a CapConfig for embedded use.
|
||||
*
|
||||
* @param assetManager The AssetManager used to load the config file
|
||||
* @param config JSON describing a configuration to use
|
||||
*/
|
||||
@Deprecated
|
||||
public CapConfig(AssetManager assetManager, JSONObject config) {
|
||||
if (config != null) {
|
||||
this.configJSON = config;
|
||||
} else {
|
||||
// Load the capacitor.config.json
|
||||
loadConfigFromAssets(assetManager, null);
|
||||
}
|
||||
|
||||
deserializeConfig(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Capacitor Configuration from config.json file.
|
||||
*
|
||||
* @param context The context.
|
||||
* @return A loaded config file, if successful.
|
||||
*/
|
||||
public static CapConfig loadDefault(Context context) {
|
||||
CapConfig config = new CapConfig();
|
||||
|
||||
if (context == null) {
|
||||
Logger.error("Capacitor Config could not be created from file. Context must not be null.");
|
||||
return config;
|
||||
}
|
||||
|
||||
config.loadConfigFromAssets(context.getAssets(), null);
|
||||
config.deserializeConfig(context);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Capacitor Configuration from config.json file within the app assets.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param path A path relative to the root assets directory.
|
||||
* @return A loaded config file, if successful.
|
||||
*/
|
||||
public static CapConfig loadFromAssets(Context context, String path) {
|
||||
CapConfig config = new CapConfig();
|
||||
|
||||
if (context == null) {
|
||||
Logger.error("Capacitor Config could not be created from file. Context must not be null.");
|
||||
return config;
|
||||
}
|
||||
|
||||
config.loadConfigFromAssets(context.getAssets(), path);
|
||||
config.deserializeConfig(context);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Capacitor Configuration from config.json file within the app file-space.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param path A path relative to the root of the app file-space.
|
||||
* @return A loaded config file, if successful.
|
||||
*/
|
||||
public static CapConfig loadFromFile(Context context, String path) {
|
||||
CapConfig config = new CapConfig();
|
||||
|
||||
if (context == null) {
|
||||
Logger.error("Capacitor Config could not be created from file. Context must not be null.");
|
||||
return config;
|
||||
}
|
||||
|
||||
config.loadConfigFromFile(path);
|
||||
config.deserializeConfig(context);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Capacitor Configuration using ConfigBuilder.
|
||||
*
|
||||
* @param builder A config builder initialized with values
|
||||
*/
|
||||
private CapConfig(Builder builder) {
|
||||
// Server Config
|
||||
this.html5mode = builder.html5mode;
|
||||
this.serverUrl = builder.serverUrl;
|
||||
this.hostname = builder.hostname;
|
||||
|
||||
if (this.validateScheme(builder.androidScheme)) {
|
||||
this.androidScheme = builder.androidScheme;
|
||||
}
|
||||
|
||||
this.allowNavigation = builder.allowNavigation;
|
||||
|
||||
// Android Config
|
||||
this.overriddenUserAgentString = builder.overriddenUserAgentString;
|
||||
this.appendedUserAgentString = builder.appendedUserAgentString;
|
||||
this.backgroundColor = builder.backgroundColor;
|
||||
this.allowMixedContent = builder.allowMixedContent;
|
||||
this.captureInput = builder.captureInput;
|
||||
this.webContentsDebuggingEnabled = builder.webContentsDebuggingEnabled;
|
||||
this.loggingEnabled = builder.loggingEnabled;
|
||||
this.initialFocus = builder.initialFocus;
|
||||
this.useLegacyBridge = builder.useLegacyBridge;
|
||||
this.minWebViewVersion = builder.minWebViewVersion;
|
||||
this.minHuaweiWebViewVersion = builder.minHuaweiWebViewVersion;
|
||||
this.errorPath = builder.errorPath;
|
||||
this.zoomableWebView = builder.zoomableWebView;
|
||||
|
||||
// Embedded
|
||||
this.startPath = builder.startPath;
|
||||
|
||||
// Plugins Config
|
||||
this.pluginsConfiguration = builder.pluginsConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a Capacitor Configuration JSON file into a Capacitor Configuration object.
|
||||
* An optional path string can be provided to look for the config in a subdirectory path.
|
||||
*/
|
||||
private void loadConfigFromAssets(AssetManager assetManager, String path) {
|
||||
if (path == null) {
|
||||
path = "";
|
||||
} else {
|
||||
// Add slash at the end to form a proper file path if going deeper in assets dir
|
||||
if (path.charAt(path.length() - 1) != '/') {
|
||||
path = path + "/";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String jsonString = readFileFromAssets(assetManager, path + "capacitor.config.json");
|
||||
configJSON = new JSONObject(jsonString);
|
||||
} catch (IOException ex) {
|
||||
Logger.error("Unable to load capacitor.config.json. Run npx cap copy first", ex);
|
||||
} catch (JSONException ex) {
|
||||
Logger.error("Unable to parse capacitor.config.json. Make sure it's valid json", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a Capacitor Configuration JSON file into a Capacitor Configuration object.
|
||||
* An optional path string can be provided to look for the config in a subdirectory path.
|
||||
*/
|
||||
private void loadConfigFromFile(String path) {
|
||||
if (path == null) {
|
||||
path = "";
|
||||
} else {
|
||||
// Add slash at the end to form a proper file path if going deeper in assets dir
|
||||
if (path.charAt(path.length() - 1) != '/') {
|
||||
path = path + "/";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
File configFile = new File(path + "capacitor.config.json");
|
||||
String jsonString = FileUtils.readFileFromDisk(configFile);
|
||||
configJSON = new JSONObject(jsonString);
|
||||
} catch (JSONException ex) {
|
||||
Logger.error("Unable to parse capacitor.config.json. Make sure it's valid json", ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.error("Unable to load capacitor.config.json.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the config from JSON into a Capacitor Configuration object.
|
||||
*/
|
||||
private void deserializeConfig(@Nullable Context context) {
|
||||
boolean isDebug = context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
|
||||
|
||||
// Server
|
||||
html5mode = JSONUtils.getBoolean(configJSON, "server.html5mode", html5mode);
|
||||
serverUrl = JSONUtils.getString(configJSON, "server.url", null);
|
||||
hostname = JSONUtils.getString(configJSON, "server.hostname", hostname);
|
||||
errorPath = JSONUtils.getString(configJSON, "server.errorPath", null);
|
||||
|
||||
String configSchema = JSONUtils.getString(configJSON, "server.androidScheme", androidScheme);
|
||||
if (this.validateScheme(configSchema)) {
|
||||
androidScheme = configSchema;
|
||||
}
|
||||
|
||||
allowNavigation = JSONUtils.getArray(configJSON, "server.allowNavigation", null);
|
||||
|
||||
// Android
|
||||
overriddenUserAgentString =
|
||||
JSONUtils.getString(configJSON, "android.overrideUserAgent", JSONUtils.getString(configJSON, "overrideUserAgent", null));
|
||||
appendedUserAgentString =
|
||||
JSONUtils.getString(configJSON, "android.appendUserAgent", JSONUtils.getString(configJSON, "appendUserAgent", null));
|
||||
backgroundColor =
|
||||
JSONUtils.getString(configJSON, "android.backgroundColor", JSONUtils.getString(configJSON, "backgroundColor", null));
|
||||
allowMixedContent =
|
||||
JSONUtils.getBoolean(
|
||||
configJSON,
|
||||
"android.allowMixedContent",
|
||||
JSONUtils.getBoolean(configJSON, "allowMixedContent", allowMixedContent)
|
||||
);
|
||||
minWebViewVersion = JSONUtils.getInt(configJSON, "android.minWebViewVersion", DEFAULT_ANDROID_WEBVIEW_VERSION);
|
||||
minHuaweiWebViewVersion = JSONUtils.getInt(configJSON, "android.minHuaweiWebViewVersion", DEFAULT_HUAWEI_WEBVIEW_VERSION);
|
||||
captureInput = JSONUtils.getBoolean(configJSON, "android.captureInput", captureInput);
|
||||
useLegacyBridge = JSONUtils.getBoolean(configJSON, "android.useLegacyBridge", useLegacyBridge);
|
||||
webContentsDebuggingEnabled = JSONUtils.getBoolean(configJSON, "android.webContentsDebuggingEnabled", isDebug);
|
||||
zoomableWebView = JSONUtils.getBoolean(configJSON, "android.zoomEnabled", JSONUtils.getBoolean(configJSON, "zoomEnabled", false));
|
||||
|
||||
String logBehavior = JSONUtils.getString(
|
||||
configJSON,
|
||||
"android.loggingBehavior",
|
||||
JSONUtils.getString(configJSON, "loggingBehavior", LOG_BEHAVIOR_DEBUG)
|
||||
);
|
||||
switch (logBehavior.toLowerCase(Locale.ROOT)) {
|
||||
case LOG_BEHAVIOR_PRODUCTION:
|
||||
loggingEnabled = true;
|
||||
break;
|
||||
case LOG_BEHAVIOR_NONE:
|
||||
loggingEnabled = false;
|
||||
break;
|
||||
default: // LOG_BEHAVIOR_DEBUG
|
||||
loggingEnabled = isDebug;
|
||||
}
|
||||
|
||||
initialFocus = JSONUtils.getBoolean(configJSON, "android.initialFocus", initialFocus);
|
||||
|
||||
// Plugins
|
||||
pluginsConfiguration = deserializePluginsConfig(JSONUtils.getObject(configJSON, "plugins"));
|
||||
}
|
||||
|
||||
private boolean validateScheme(String scheme) {
|
||||
List<String> invalidSchemes = Arrays.asList("file", "ftp", "ftps", "ws", "wss", "about", "blob", "data");
|
||||
if (invalidSchemes.contains(scheme)) {
|
||||
Logger.warn(scheme + " is not an allowed scheme. Defaulting to https.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Non-http(s) schemes are not allowed to modify the URL path as of Android Webview 117
|
||||
if (!scheme.equals("http") && !scheme.equals("https")) {
|
||||
Logger.warn(
|
||||
"Using a non-standard scheme: " + scheme + " for Android. This is known to cause issues as of Android Webview 117."
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isHTML5Mode() {
|
||||
return html5mode;
|
||||
}
|
||||
|
||||
public String getServerUrl() {
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
public String getErrorPath() {
|
||||
return errorPath;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public String getStartPath() {
|
||||
return startPath;
|
||||
}
|
||||
|
||||
public String getAndroidScheme() {
|
||||
return androidScheme;
|
||||
}
|
||||
|
||||
public String[] getAllowNavigation() {
|
||||
return allowNavigation;
|
||||
}
|
||||
|
||||
public String getOverriddenUserAgentString() {
|
||||
return overriddenUserAgentString;
|
||||
}
|
||||
|
||||
public String getAppendedUserAgentString() {
|
||||
return appendedUserAgentString;
|
||||
}
|
||||
|
||||
public String getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
public boolean isMixedContentAllowed() {
|
||||
return allowMixedContent;
|
||||
}
|
||||
|
||||
public boolean isInputCaptured() {
|
||||
return captureInput;
|
||||
}
|
||||
|
||||
public boolean isWebContentsDebuggingEnabled() {
|
||||
return webContentsDebuggingEnabled;
|
||||
}
|
||||
|
||||
public boolean isZoomableWebView() {
|
||||
return zoomableWebView;
|
||||
}
|
||||
|
||||
public boolean isLoggingEnabled() {
|
||||
return loggingEnabled;
|
||||
}
|
||||
|
||||
public boolean isInitialFocus() {
|
||||
return initialFocus;
|
||||
}
|
||||
|
||||
public boolean isUsingLegacyBridge() {
|
||||
return useLegacyBridge;
|
||||
}
|
||||
|
||||
public int getMinWebViewVersion() {
|
||||
if (minWebViewVersion < MINIMUM_ANDROID_WEBVIEW_VERSION) {
|
||||
Logger.warn("Specified minimum webview version is too low, defaulting to " + MINIMUM_ANDROID_WEBVIEW_VERSION);
|
||||
return MINIMUM_ANDROID_WEBVIEW_VERSION;
|
||||
}
|
||||
|
||||
return minWebViewVersion;
|
||||
}
|
||||
|
||||
public int getMinHuaweiWebViewVersion() {
|
||||
if (minHuaweiWebViewVersion < MINIMUM_HUAWEI_WEBVIEW_VERSION) {
|
||||
Logger.warn("Specified minimum Huawei webview version is too low, defaulting to " + MINIMUM_HUAWEI_WEBVIEW_VERSION);
|
||||
return MINIMUM_HUAWEI_WEBVIEW_VERSION;
|
||||
}
|
||||
|
||||
return minHuaweiWebViewVersion;
|
||||
}
|
||||
|
||||
public PluginConfig getPluginConfiguration(String pluginId) {
|
||||
PluginConfig pluginConfig = pluginsConfiguration.get(pluginId);
|
||||
if (pluginConfig == null) {
|
||||
pluginConfig = new PluginConfig(new JSONObject());
|
||||
}
|
||||
|
||||
return pluginConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON object value from the Capacitor config.
|
||||
* @deprecated use {@link PluginConfig#getObject(String)} to access plugin config values.
|
||||
* For main Capacitor config values, use the appropriate getter.
|
||||
*
|
||||
* @param key A key to fetch from the config
|
||||
* @return The value from the config, if exists. Null if not
|
||||
*/
|
||||
@Deprecated
|
||||
public JSONObject getObject(String key) {
|
||||
try {
|
||||
return configJSON.getJSONObject(key);
|
||||
} catch (Exception ex) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string value from the Capacitor config.
|
||||
* @deprecated use {@link PluginConfig#getString(String, String)} to access plugin config
|
||||
* values. For main Capacitor config values, use the appropriate getter.
|
||||
*
|
||||
* @param key A key to fetch from the config
|
||||
* @return The value from the config, if exists. Null if not
|
||||
*/
|
||||
@Deprecated
|
||||
public String getString(String key) {
|
||||
return JSONUtils.getString(configJSON, key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string value from the Capacitor config.
|
||||
* @deprecated use {@link PluginConfig#getString(String, String)} to access plugin config
|
||||
* values. For main Capacitor config values, use the appropriate getter.
|
||||
*
|
||||
* @param key A key to fetch from the config
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
@Deprecated
|
||||
public String getString(String key, String defaultValue) {
|
||||
return JSONUtils.getString(configJSON, key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value from the Capacitor config.
|
||||
* @deprecated use {@link PluginConfig#getBoolean(String, boolean)} to access plugin config
|
||||
* values. For main Capacitor config values, use the appropriate getter.
|
||||
*
|
||||
* @param key A key to fetch from the config
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
return JSONUtils.getBoolean(configJSON, key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an integer value from the Capacitor config.
|
||||
* @deprecated use {@link PluginConfig#getInt(String, int)} to access the plugin config
|
||||
* values. For main Capacitor config values, use the appropriate getter.
|
||||
*
|
||||
* @param key A key to fetch from the config
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
@Deprecated
|
||||
public int getInt(String key, int defaultValue) {
|
||||
return JSONUtils.getInt(configJSON, key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string array value from the Capacitor config.
|
||||
* @deprecated use {@link PluginConfig#getArray(String)} to access the plugin config
|
||||
* values. For main Capacitor config values, use the appropriate getter.
|
||||
*
|
||||
* @param key A key to fetch from the config
|
||||
* @return The value from the config, if exists. Null if not
|
||||
*/
|
||||
@Deprecated
|
||||
public String[] getArray(String key) {
|
||||
return JSONUtils.getArray(configJSON, key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string array value from the Capacitor config.
|
||||
* @deprecated use {@link PluginConfig#getArray(String, String[])} to access the plugin
|
||||
* config values. For main Capacitor config values, use the appropriate getter.
|
||||
*
|
||||
* @param key A key to fetch from the config
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
@Deprecated
|
||||
public String[] getArray(String key, String[] defaultValue) {
|
||||
return JSONUtils.getArray(configJSON, key, defaultValue);
|
||||
}
|
||||
|
||||
private static Map<String, PluginConfig> deserializePluginsConfig(JSONObject pluginsConfig) {
|
||||
Map<String, PluginConfig> pluginsMap = new HashMap<>();
|
||||
|
||||
// return an empty map if there is no pluginsConfig json
|
||||
if (pluginsConfig == null) {
|
||||
return pluginsMap;
|
||||
}
|
||||
|
||||
Iterator<String> pluginIds = pluginsConfig.keys();
|
||||
|
||||
while (pluginIds.hasNext()) {
|
||||
String pluginId = pluginIds.next();
|
||||
JSONObject value = null;
|
||||
|
||||
try {
|
||||
value = pluginsConfig.getJSONObject(pluginId);
|
||||
PluginConfig pluginConfig = new PluginConfig(value);
|
||||
pluginsMap.put(pluginId, pluginConfig);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return pluginsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Capacitor Configuration in code
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private Context context;
|
||||
|
||||
// Server Config Values
|
||||
private boolean html5mode = true;
|
||||
private String serverUrl;
|
||||
private String errorPath;
|
||||
private String hostname = "localhost";
|
||||
private String androidScheme = CAPACITOR_HTTPS_SCHEME;
|
||||
private String[] allowNavigation;
|
||||
|
||||
// Android Config Values
|
||||
private String overriddenUserAgentString;
|
||||
private String appendedUserAgentString;
|
||||
private String backgroundColor;
|
||||
private boolean allowMixedContent = false;
|
||||
private boolean captureInput = false;
|
||||
private Boolean webContentsDebuggingEnabled = null;
|
||||
private boolean loggingEnabled = true;
|
||||
private boolean initialFocus = false;
|
||||
private boolean useLegacyBridge = false;
|
||||
private int minWebViewVersion = DEFAULT_ANDROID_WEBVIEW_VERSION;
|
||||
private int minHuaweiWebViewVersion = DEFAULT_HUAWEI_WEBVIEW_VERSION;
|
||||
private boolean zoomableWebView = false;
|
||||
|
||||
// Embedded
|
||||
private String startPath = null;
|
||||
|
||||
// Plugins Config Object
|
||||
private Map<String, PluginConfig> pluginsConfiguration = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs a new CapConfig Builder.
|
||||
*
|
||||
* @param context The context
|
||||
*/
|
||||
public Builder(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Capacitor Config from the builder.
|
||||
*
|
||||
* @return A new Capacitor Config
|
||||
*/
|
||||
public CapConfig create() {
|
||||
if (webContentsDebuggingEnabled == null) {
|
||||
webContentsDebuggingEnabled = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
|
||||
}
|
||||
|
||||
return new CapConfig(this);
|
||||
}
|
||||
|
||||
public Builder setPluginsConfiguration(JSONObject pluginsConfiguration) {
|
||||
this.pluginsConfiguration = deserializePluginsConfig(pluginsConfiguration);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setHTML5mode(boolean html5mode) {
|
||||
this.html5mode = html5mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setServerUrl(String serverUrl) {
|
||||
this.serverUrl = serverUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setErrorPath(String errorPath) {
|
||||
this.errorPath = errorPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStartPath(String path) {
|
||||
this.startPath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAndroidScheme(String androidScheme) {
|
||||
this.androidScheme = androidScheme;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAllowNavigation(String[] allowNavigation) {
|
||||
this.allowNavigation = allowNavigation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setOverriddenUserAgentString(String overriddenUserAgentString) {
|
||||
this.overriddenUserAgentString = overriddenUserAgentString;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAppendedUserAgentString(String appendedUserAgentString) {
|
||||
this.appendedUserAgentString = appendedUserAgentString;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setBackgroundColor(String backgroundColor) {
|
||||
this.backgroundColor = backgroundColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAllowMixedContent(boolean allowMixedContent) {
|
||||
this.allowMixedContent = allowMixedContent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCaptureInput(boolean captureInput) {
|
||||
this.captureInput = captureInput;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUseLegacyBridge(boolean useLegacyBridge) {
|
||||
this.useLegacyBridge = useLegacyBridge;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setWebContentsDebuggingEnabled(boolean webContentsDebuggingEnabled) {
|
||||
this.webContentsDebuggingEnabled = webContentsDebuggingEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setZoomableWebView(boolean zoomableWebView) {
|
||||
this.zoomableWebView = zoomableWebView;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setLoggingEnabled(boolean enabled) {
|
||||
this.loggingEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setInitialFocus(boolean focus) {
|
||||
this.initialFocus = focus;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java
generated
vendored
Normal file
52
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class CapacitorWebView extends WebView {
|
||||
|
||||
private BaseInputConnection capInputConnection;
|
||||
private Bridge bridge;
|
||||
|
||||
public CapacitorWebView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setBridge(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
CapConfig config;
|
||||
if (bridge != null) {
|
||||
config = bridge.getConfig();
|
||||
} else {
|
||||
config = CapConfig.loadDefault(getContext());
|
||||
}
|
||||
|
||||
boolean captureInput = config.isInputCaptured();
|
||||
if (captureInput) {
|
||||
if (capInputConnection == null) {
|
||||
capInputConnection = new BaseInputConnection(this, false);
|
||||
}
|
||||
return capInputConnection;
|
||||
}
|
||||
return super.onCreateInputConnection(outAttrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (event.getAction() == KeyEvent.ACTION_MULTIPLE) {
|
||||
evaluateJavascript("document.activeElement.value = document.activeElement.value + '" + event.getCharacters() + "';", null);
|
||||
return false;
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
}
|
||||
303
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java
generated
vendored
Normal file
303
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java
generated
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Portions adopted from react-native-image-crop-picker
|
||||
*
|
||||
* MIT License
|
||||
|
||||
* Copyright (c) 2017 Ivan Pusic
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.OpenableColumns;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Common File utilities, such as resolve content URIs and
|
||||
* creating portable web paths from low-level files
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
private static String CapacitorFileScheme = Bridge.CAPACITOR_FILE_START;
|
||||
|
||||
public enum Type {
|
||||
IMAGE("image");
|
||||
|
||||
private String type;
|
||||
|
||||
Type(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPortablePath(Context c, String host, Uri u) {
|
||||
String path = getFileUrlForUri(c, u);
|
||||
if (path.startsWith("file://")) {
|
||||
path = path.replace("file://", "");
|
||||
}
|
||||
return host + Bridge.CAPACITOR_FILE_START + path;
|
||||
}
|
||||
|
||||
public static String getFileUrlForUri(final Context context, final Uri uri) {
|
||||
// DocumentProvider
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return legacyPrimaryPath(split[1]);
|
||||
} else {
|
||||
final int splitIndex = docId.indexOf(':', 1);
|
||||
final String tag = docId.substring(0, splitIndex);
|
||||
final String path = docId.substring(splitIndex + 1);
|
||||
|
||||
String nonPrimaryVolume = getPathToNonPrimaryVolume(context, tag);
|
||||
if (nonPrimaryVolume != null) {
|
||||
String result = nonPrimaryVolume + "/" + path;
|
||||
File file = new File(result);
|
||||
if (file.exists() && file.canRead()) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
// MediaProvider
|
||||
else if (isMediaDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[] { split[1] };
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
}
|
||||
// MediaStore (and general)
|
||||
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
// Return the remote address
|
||||
if (isGooglePhotosUri(uri)) return uri.getLastPathSegment();
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
// File
|
||||
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static String legacyPrimaryPath(String pathPart) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + pathPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a plaintext file from the assets directory.
|
||||
*
|
||||
* @param assetManager Used to open the file.
|
||||
* @param fileName The path of the file to read.
|
||||
* @return The contents of the file path.
|
||||
* @throws IOException Thrown if any issues reading the provided file path.
|
||||
*/
|
||||
static String readFileFromAssets(AssetManager assetManager, String fileName) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open(fileName)))) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a plaintext file from within the app disk space.
|
||||
*
|
||||
* @param file The file to read.
|
||||
* @return The contents of the file path.
|
||||
* @throws IOException Thrown if any issues reading the provided file path.
|
||||
*/
|
||||
static String readFileFromDisk(File file) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||
String path = null;
|
||||
Cursor cursor = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = { column };
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int index = cursor.getColumnIndexOrThrow(column);
|
||||
path = cursor.getString(index);
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return getCopyFilePath(uri, context);
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
if (path == null) {
|
||||
return getCopyFilePath(uri, context);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static String getCopyFilePath(Uri uri, Context context) {
|
||||
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
cursor.moveToFirst();
|
||||
String name = (cursor.getString(nameIndex));
|
||||
String fileName = sanitizeFilename(name);
|
||||
File file = new File(context.getFilesDir(), fileName);
|
||||
try {
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(uri);
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
int read = 0;
|
||||
int maxBufferSize = 1024 * 1024;
|
||||
int bufferSize = Math.min(inputStream.available(), maxBufferSize);
|
||||
final byte[] buffers = new byte[bufferSize];
|
||||
while ((read = inputStream.read(buffers)) != -1) {
|
||||
outputStream.write(buffers, 0, read);
|
||||
}
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
return file.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
private static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
private static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
private static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is Google Photos.
|
||||
*/
|
||||
private static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
private static String getPathToNonPrimaryVolume(Context context, String tag) {
|
||||
File[] volumes = context.getExternalCacheDirs();
|
||||
if (volumes != null) {
|
||||
for (File volume : volumes) {
|
||||
if (volume != null) {
|
||||
String path = volume.getAbsolutePath();
|
||||
if (path != null) {
|
||||
int index = path.indexOf(tag);
|
||||
if (index != -1) {
|
||||
return path.substring(0, index) + tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String sanitizeFilename(String displayName) {
|
||||
String[] badCharacters = new String[] { "..", "/" };
|
||||
String[] segments = displayName.split("/");
|
||||
String fileName = segments[segments.length - 1];
|
||||
for (String suspString : badCharacters) {
|
||||
fileName = fileName.replace(suspString, "_");
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
8
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/InvalidPluginException.java
generated
vendored
Normal file
8
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/InvalidPluginException.java
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
class InvalidPluginException extends Exception {
|
||||
|
||||
public InvalidPluginException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
16
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/InvalidPluginMethodException.java
generated
vendored
Normal file
16
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/InvalidPluginMethodException.java
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
class InvalidPluginMethodException extends Exception {
|
||||
|
||||
public InvalidPluginMethodException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public InvalidPluginMethodException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
public InvalidPluginMethodException(String s, Throwable t) {
|
||||
super(s, t);
|
||||
}
|
||||
}
|
||||
51
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSArray.java
generated
vendored
Normal file
51
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSArray.java
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class JSArray extends JSONArray {
|
||||
|
||||
public JSArray() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JSArray(String json) throws JSONException {
|
||||
super(json);
|
||||
}
|
||||
|
||||
public JSArray(Collection copyFrom) {
|
||||
super(copyFrom);
|
||||
}
|
||||
|
||||
public JSArray(Object array) throws JSONException {
|
||||
super(array);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E> List<E> toList() throws JSONException {
|
||||
List<E> items = new ArrayList<>();
|
||||
Object o = null;
|
||||
for (int i = 0; i < this.length(); i++) {
|
||||
o = this.get(i);
|
||||
try {
|
||||
items.add((E) this.get(i));
|
||||
} catch (Exception ex) {
|
||||
throw new JSONException("Not all items are instances of the given type");
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JSArray without throwing a error
|
||||
*/
|
||||
public static JSArray from(Object array) {
|
||||
try {
|
||||
return new JSArray(array);
|
||||
} catch (JSONException ex) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
193
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSExport.java
generated
vendored
Normal file
193
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSExport.java
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import static com.getcapacitor.FileUtils.readFileFromAssets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class JSExport {
|
||||
|
||||
private static String CATCHALL_OPTIONS_PARAM = "_options";
|
||||
private static String CALLBACK_PARAM = "_callback";
|
||||
|
||||
public static String getGlobalJS(Context context, boolean loggingEnabled, boolean isDebug) {
|
||||
return "window.Capacitor = { DEBUG: " + isDebug + ", isLoggingEnabled: " + loggingEnabled + ", Plugins: {} };";
|
||||
}
|
||||
|
||||
public static String getCordovaJS(Context context) {
|
||||
String fileContent = "";
|
||||
try {
|
||||
fileContent = readFileFromAssets(context.getAssets(), "public/cordova.js");
|
||||
} catch (IOException ex) {
|
||||
Logger.error("Unable to read public/cordova.js file, Cordova plugins will not work");
|
||||
}
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
public static String getCordovaPluginsFileJS(Context context) {
|
||||
String fileContent = "";
|
||||
try {
|
||||
fileContent = readFileFromAssets(context.getAssets(), "public/cordova_plugins.js");
|
||||
} catch (IOException ex) {
|
||||
Logger.error("Unable to read public/cordova_plugins.js file, Cordova plugins will not work");
|
||||
}
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
public static String getPluginJS(Collection<PluginHandle> plugins) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
JSONArray pluginArray = new JSONArray();
|
||||
|
||||
lines.add("// Begin: Capacitor Plugin JS");
|
||||
for (PluginHandle plugin : plugins) {
|
||||
lines.add(
|
||||
"(function(w) {\n" +
|
||||
"var a = (w.Capacitor = w.Capacitor || {});\n" +
|
||||
"var p = (a.Plugins = a.Plugins || {});\n" +
|
||||
"var t = (p['" +
|
||||
plugin.getId() +
|
||||
"'] = {});\n" +
|
||||
"t.addListener = function(eventName, callback) {\n" +
|
||||
" return w.Capacitor.addListener('" +
|
||||
plugin.getId() +
|
||||
"', eventName, callback);\n" +
|
||||
"}"
|
||||
);
|
||||
Collection<PluginMethodHandle> methods = plugin.getMethods();
|
||||
for (PluginMethodHandle method : methods) {
|
||||
if (method.getName().equals("addListener") || method.getName().equals("removeListener")) {
|
||||
// Don't export add/remove listener, we do that automatically above as they are "special snowflakes"
|
||||
continue;
|
||||
}
|
||||
lines.add(generateMethodJS(plugin, method));
|
||||
}
|
||||
|
||||
lines.add("})(window);\n");
|
||||
pluginArray.put(createPluginHeader(plugin));
|
||||
}
|
||||
|
||||
return TextUtils.join("\n", lines) + "\nwindow.Capacitor.PluginHeaders = " + pluginArray.toString() + ";";
|
||||
}
|
||||
|
||||
public static String getCordovaPluginJS(Context context) {
|
||||
return getFilesContent(context, "public/plugins");
|
||||
}
|
||||
|
||||
public static String getFilesContent(Context context, String path) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try {
|
||||
String[] content = context.getAssets().list(path);
|
||||
if (content.length > 0) {
|
||||
for (String file : content) {
|
||||
if (!file.endsWith(".map")) {
|
||||
builder.append(getFilesContent(context, path + "/" + file));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return readFileFromAssets(context.getAssets(), path);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.warn("Unable to read file at path " + path);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static JSONObject createPluginHeader(PluginHandle plugin) {
|
||||
JSONObject pluginObj = new JSONObject();
|
||||
Collection<PluginMethodHandle> methods = plugin.getMethods();
|
||||
try {
|
||||
String id = plugin.getId();
|
||||
JSONArray methodArray = new JSONArray();
|
||||
pluginObj.put("name", id);
|
||||
|
||||
for (PluginMethodHandle method : methods) {
|
||||
methodArray.put(createPluginMethodHeader(method));
|
||||
}
|
||||
|
||||
pluginObj.put("methods", methodArray);
|
||||
} catch (JSONException e) {
|
||||
// ignore
|
||||
}
|
||||
return pluginObj;
|
||||
}
|
||||
|
||||
private static JSONObject createPluginMethodHeader(PluginMethodHandle method) {
|
||||
JSONObject methodObj = new JSONObject();
|
||||
|
||||
try {
|
||||
methodObj.put("name", method.getName());
|
||||
if (!method.getReturnType().equals(PluginMethod.RETURN_NONE)) {
|
||||
methodObj.put("rtype", method.getReturnType());
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return methodObj;
|
||||
}
|
||||
|
||||
public static String getBridgeJS(Context context) throws JSExportException {
|
||||
return getFilesContent(context, "native-bridge.js");
|
||||
}
|
||||
|
||||
private static String generateMethodJS(PluginHandle plugin, PluginMethodHandle method) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
// Add the catch all param that will take a full javascript object to pass to the plugin
|
||||
args.add(CATCHALL_OPTIONS_PARAM);
|
||||
|
||||
String returnType = method.getReturnType();
|
||||
if (returnType.equals(PluginMethod.RETURN_CALLBACK)) {
|
||||
args.add(CALLBACK_PARAM);
|
||||
}
|
||||
|
||||
// Create the method function declaration
|
||||
lines.add("t['" + method.getName() + "'] = function(" + TextUtils.join(", ", args) + ") {");
|
||||
|
||||
switch (returnType) {
|
||||
case PluginMethod.RETURN_NONE:
|
||||
lines.add(
|
||||
"return w.Capacitor.nativeCallback('" +
|
||||
plugin.getId() +
|
||||
"', '" +
|
||||
method.getName() +
|
||||
"', " +
|
||||
CATCHALL_OPTIONS_PARAM +
|
||||
")"
|
||||
);
|
||||
break;
|
||||
case PluginMethod.RETURN_PROMISE:
|
||||
lines.add(
|
||||
"return w.Capacitor.nativePromise('" + plugin.getId() + "', '" + method.getName() + "', " + CATCHALL_OPTIONS_PARAM + ")"
|
||||
);
|
||||
break;
|
||||
case PluginMethod.RETURN_CALLBACK:
|
||||
lines.add(
|
||||
"return w.Capacitor.nativeCallback('" +
|
||||
plugin.getId() +
|
||||
"', '" +
|
||||
method.getName() +
|
||||
"', " +
|
||||
CATCHALL_OPTIONS_PARAM +
|
||||
", " +
|
||||
CALLBACK_PARAM +
|
||||
")"
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// TODO: Do something here?
|
||||
}
|
||||
|
||||
lines.add("}");
|
||||
|
||||
return TextUtils.join("\n", lines);
|
||||
}
|
||||
}
|
||||
16
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSExportException.java
generated
vendored
Normal file
16
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSExportException.java
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
public class JSExportException extends Exception {
|
||||
|
||||
public JSExportException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public JSExportException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
public JSExportException(String s, Throwable t) {
|
||||
super(s, t);
|
||||
}
|
||||
}
|
||||
107
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSInjector.java
generated
vendored
Normal file
107
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSInjector.java
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* JSInject is responsible for returning Capacitor's core
|
||||
* runtime JS and any plugin JS back into HTML page responses
|
||||
* to the client.
|
||||
*/
|
||||
class JSInjector {
|
||||
|
||||
private String globalJS;
|
||||
private String bridgeJS;
|
||||
private String pluginJS;
|
||||
private String cordovaJS;
|
||||
private String cordovaPluginsJS;
|
||||
private String cordovaPluginsFileJS;
|
||||
private String localUrlJS;
|
||||
|
||||
public JSInjector(
|
||||
String globalJS,
|
||||
String bridgeJS,
|
||||
String pluginJS,
|
||||
String cordovaJS,
|
||||
String cordovaPluginsJS,
|
||||
String cordovaPluginsFileJS,
|
||||
String localUrlJS
|
||||
) {
|
||||
this.globalJS = globalJS;
|
||||
this.bridgeJS = bridgeJS;
|
||||
this.pluginJS = pluginJS;
|
||||
this.cordovaJS = cordovaJS;
|
||||
this.cordovaPluginsJS = cordovaPluginsJS;
|
||||
this.cordovaPluginsFileJS = cordovaPluginsFileJS;
|
||||
this.localUrlJS = localUrlJS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates injectable JS content.
|
||||
* This may be used in other forms of injecting that aren't using an InputStream.
|
||||
* @return
|
||||
*/
|
||||
public String getScriptString() {
|
||||
return (
|
||||
globalJS +
|
||||
"\n\n" +
|
||||
localUrlJS +
|
||||
"\n\n" +
|
||||
bridgeJS +
|
||||
"\n\n" +
|
||||
pluginJS +
|
||||
"\n\n" +
|
||||
cordovaJS +
|
||||
"\n\n" +
|
||||
cordovaPluginsFileJS +
|
||||
"\n\n" +
|
||||
cordovaPluginsJS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an InputStream from the web server, prepend it with
|
||||
* our JS stream
|
||||
* @param responseStream
|
||||
* @return
|
||||
*/
|
||||
public InputStream getInjectedStream(InputStream responseStream) {
|
||||
String js = "<script type=\"text/javascript\">" + getScriptString() + "</script>";
|
||||
String html = this.readAssetStream(responseStream);
|
||||
|
||||
// Insert the js string at the position after <head> or before </head> using StringBuilder
|
||||
StringBuilder modifiedHtml = new StringBuilder(html);
|
||||
if (html.contains("<head>")) {
|
||||
modifiedHtml.insert(html.indexOf("<head>") + "<head>".length(), "\n" + js + "\n");
|
||||
html = modifiedHtml.toString();
|
||||
} else if (html.contains("</head>")) {
|
||||
modifiedHtml.insert(html.indexOf("</head>"), "\n" + js + "\n");
|
||||
html = modifiedHtml.toString();
|
||||
} else {
|
||||
Logger.error("Unable to inject Capacitor, Plugins won't work");
|
||||
}
|
||||
return new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private String readAssetStream(InputStream stream) {
|
||||
try {
|
||||
final int bufferSize = 1024;
|
||||
final char[] buffer = new char[bufferSize];
|
||||
final StringBuilder out = new StringBuilder();
|
||||
Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
|
||||
for (;;) {
|
||||
int rsz = in.read(buffer, 0, buffer.length);
|
||||
if (rsz < 0) break;
|
||||
out.append(buffer, 0, rsz);
|
||||
}
|
||||
return out.toString();
|
||||
} catch (Exception e) {
|
||||
Logger.error("Unable to process HTML asset file. This is a fatal error", e);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
164
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSObject.java
generated
vendored
Normal file
164
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSObject.java
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* A wrapper around JSONObject that isn't afraid to do simple
|
||||
* JSON put operations without having to throw an exception
|
||||
* for every little thing jeez
|
||||
*/
|
||||
public class JSObject extends JSONObject {
|
||||
|
||||
public JSObject() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JSObject(String json) throws JSONException {
|
||||
super(json);
|
||||
}
|
||||
|
||||
public JSObject(JSONObject obj, String[] names) throws JSONException {
|
||||
super(obj, names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a pathetic JSONObject into a JSObject
|
||||
* @param obj
|
||||
*/
|
||||
public static JSObject fromJSONObject(JSONObject obj) throws JSONException {
|
||||
Iterator<String> keysIter = obj.keys();
|
||||
List<String> keys = new ArrayList<>();
|
||||
while (keysIter.hasNext()) {
|
||||
keys.add(keysIter.next());
|
||||
}
|
||||
|
||||
return new JSObject(obj, keys.toArray(new String[keys.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getString(String key) {
|
||||
return getString(key, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getString(String key, @Nullable String defaultValue) {
|
||||
try {
|
||||
String value = super.getString(key);
|
||||
if (!super.isNull(key)) {
|
||||
return value;
|
||||
}
|
||||
} catch (JSONException ex) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getInteger(String key) {
|
||||
return getInteger(key, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getInteger(String key, @Nullable Integer defaultValue) {
|
||||
try {
|
||||
return super.getInt(key);
|
||||
} catch (JSONException e) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getBoolean(String key, @Nullable Boolean defaultValue) {
|
||||
try {
|
||||
return super.getBoolean(key);
|
||||
} catch (JSONException e) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch boolean from jsonObject
|
||||
*/
|
||||
@Nullable
|
||||
public Boolean getBool(String key) {
|
||||
return getBoolean(key, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JSObject getJSObject(String name) {
|
||||
try {
|
||||
return getJSObject(name, null);
|
||||
} catch (JSONException e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JSObject getJSObject(String name, @Nullable JSObject defaultValue) throws JSONException {
|
||||
try {
|
||||
Object obj = get(name);
|
||||
if (obj instanceof JSONObject) {
|
||||
Iterator<String> keysIter = ((JSONObject) obj).keys();
|
||||
List<String> keys = new ArrayList<>();
|
||||
while (keysIter.hasNext()) {
|
||||
keys.add(keysIter.next());
|
||||
}
|
||||
|
||||
return new JSObject((JSONObject) obj, keys.toArray(new String[keys.size()]));
|
||||
}
|
||||
} catch (JSONException ex) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSObject put(String key, boolean value) {
|
||||
try {
|
||||
super.put(key, value);
|
||||
} catch (JSONException ex) {}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSObject put(String key, int value) {
|
||||
try {
|
||||
super.put(key, value);
|
||||
} catch (JSONException ex) {}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSObject put(String key, long value) {
|
||||
try {
|
||||
super.put(key, value);
|
||||
} catch (JSONException ex) {}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSObject put(String key, double value) {
|
||||
try {
|
||||
super.put(key, value);
|
||||
} catch (JSONException ex) {}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSObject put(String key, Object value) {
|
||||
try {
|
||||
super.put(key, value);
|
||||
} catch (JSONException ex) {}
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObject put(String key, String value) {
|
||||
try {
|
||||
super.put(key, value);
|
||||
} catch (JSONException ex) {}
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObject putSafe(String key, Object value) throws JSONException {
|
||||
return (JSObject) super.put(key, value);
|
||||
}
|
||||
}
|
||||
65
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSValue.java
generated
vendored
Normal file
65
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/JSValue.java
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
/**
|
||||
* Represents a single user-data value of any type on the capacitor PluginCall object.
|
||||
*/
|
||||
public class JSValue {
|
||||
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* @param call The capacitor plugin call, used for accessing the value safely.
|
||||
* @param name The name of the property to access.
|
||||
*/
|
||||
public JSValue(PluginCall call, String name) {
|
||||
this.value = this.toValue(call, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the coerced but uncasted underlying value.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getValue().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying value as a JSObject, or throwing if it cannot.
|
||||
*
|
||||
* @throws JSONException If the underlying value is not a JSObject.
|
||||
*/
|
||||
public JSObject toJSObject() throws JSONException {
|
||||
if (this.value instanceof JSObject) return (JSObject) this.value;
|
||||
throw new JSONException("JSValue could not be coerced to JSObject.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying value as a JSArray, or throwing if it cannot.
|
||||
*
|
||||
* @throws JSONException If the underlying value is not a JSArray.
|
||||
*/
|
||||
public JSArray toJSArray() throws JSONException {
|
||||
if (this.value instanceof JSArray) return (JSArray) this.value;
|
||||
throw new JSONException("JSValue could not be coerced to JSArray.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying value this object represents, coercing it into a capacitor-friendly object if supported.
|
||||
*/
|
||||
private Object toValue(PluginCall call, String name) {
|
||||
Object value = null;
|
||||
value = call.getArray(name, null);
|
||||
if (value != null) return value;
|
||||
value = call.getObject(name, null);
|
||||
if (value != null) return value;
|
||||
value = call.getString(name, null);
|
||||
if (value != null) return value;
|
||||
return call.getData().opt(name);
|
||||
}
|
||||
}
|
||||
103
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/Logger.java
generated
vendored
Normal file
103
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/Logger.java
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
public class Logger {
|
||||
|
||||
public static final String LOG_TAG_CORE = "Capacitor";
|
||||
public static CapConfig config;
|
||||
|
||||
private static Logger instance;
|
||||
|
||||
private static Logger getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Logger();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void init(CapConfig config) {
|
||||
Logger.getInstance().loadConfig(config);
|
||||
}
|
||||
|
||||
private void loadConfig(CapConfig config) {
|
||||
Logger.config = config;
|
||||
}
|
||||
|
||||
public static String tags(String... subtags) {
|
||||
if (subtags != null && subtags.length > 0) {
|
||||
return LOG_TAG_CORE + "/" + TextUtils.join("/", subtags);
|
||||
}
|
||||
|
||||
return LOG_TAG_CORE;
|
||||
}
|
||||
|
||||
public static void verbose(String message) {
|
||||
verbose(LOG_TAG_CORE, message);
|
||||
}
|
||||
|
||||
public static void verbose(String tag, String message) {
|
||||
if (!shouldLog()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.v(tag, message);
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
debug(LOG_TAG_CORE, message);
|
||||
}
|
||||
|
||||
public static void debug(String tag, String message) {
|
||||
if (!shouldLog()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(tag, message);
|
||||
}
|
||||
|
||||
public static void info(String message) {
|
||||
info(LOG_TAG_CORE, message);
|
||||
}
|
||||
|
||||
public static void info(String tag, String message) {
|
||||
if (!shouldLog()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(tag, message);
|
||||
}
|
||||
|
||||
public static void warn(String message) {
|
||||
warn(LOG_TAG_CORE, message);
|
||||
}
|
||||
|
||||
public static void warn(String tag, String message) {
|
||||
if (!shouldLog()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.w(tag, message);
|
||||
}
|
||||
|
||||
public static void error(String message) {
|
||||
error(LOG_TAG_CORE, message, null);
|
||||
}
|
||||
|
||||
public static void error(String message, Throwable e) {
|
||||
error(LOG_TAG_CORE, message, e);
|
||||
}
|
||||
|
||||
public static void error(String tag, String message, Throwable e) {
|
||||
if (!shouldLog()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.e(tag, message, e);
|
||||
}
|
||||
|
||||
public static boolean shouldLog() {
|
||||
return config == null || config.isLoggingEnabled();
|
||||
}
|
||||
}
|
||||
159
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java
generated
vendored
Normal file
159
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/MessageHandler.java
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.WebView;
|
||||
import androidx.webkit.JavaScriptReplyProxy;
|
||||
import androidx.webkit.WebViewCompat;
|
||||
import androidx.webkit.WebViewFeature;
|
||||
import org.apache.cordova.PluginManager;
|
||||
|
||||
/**
|
||||
* MessageHandler handles messages from the WebView, dispatching them
|
||||
* to plugins.
|
||||
*/
|
||||
public class MessageHandler {
|
||||
|
||||
private Bridge bridge;
|
||||
private WebView webView;
|
||||
private PluginManager cordovaPluginManager;
|
||||
private JavaScriptReplyProxy javaScriptReplyProxy;
|
||||
|
||||
public MessageHandler(Bridge bridge, WebView webView, PluginManager cordovaPluginManager) {
|
||||
this.bridge = bridge;
|
||||
this.webView = webView;
|
||||
this.cordovaPluginManager = cordovaPluginManager;
|
||||
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && !bridge.getConfig().isUsingLegacyBridge()) {
|
||||
WebViewCompat.WebMessageListener capListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
|
||||
if (isMainFrame) {
|
||||
postMessage(message.getData());
|
||||
javaScriptReplyProxy = replyProxy;
|
||||
} else {
|
||||
Logger.warn("Plugin execution is allowed in Main Frame only");
|
||||
}
|
||||
};
|
||||
try {
|
||||
WebViewCompat.addWebMessageListener(webView, "androidBridge", bridge.getAllowedOriginRules(), capListener);
|
||||
} catch (Exception ex) {
|
||||
webView.addJavascriptInterface(this, "androidBridge");
|
||||
}
|
||||
} else {
|
||||
webView.addJavascriptInterface(this, "androidBridge");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main message handler that will be called from JavaScript
|
||||
* to send a message to the native bridge.
|
||||
* @param jsonStr
|
||||
*/
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public void postMessage(String jsonStr) {
|
||||
try {
|
||||
JSObject postData = new JSObject(jsonStr);
|
||||
|
||||
String type = postData.getString("type");
|
||||
|
||||
boolean typeIsNotNull = type != null;
|
||||
boolean isCordovaPlugin = typeIsNotNull && type.equals("cordova");
|
||||
boolean isJavaScriptError = typeIsNotNull && type.equals("js.error");
|
||||
|
||||
String callbackId = postData.getString("callbackId");
|
||||
|
||||
if (isCordovaPlugin) {
|
||||
String service = postData.getString("service");
|
||||
String action = postData.getString("action");
|
||||
String actionArgs = postData.getString("actionArgs");
|
||||
|
||||
Logger.verbose(
|
||||
Logger.tags("Plugin"),
|
||||
"To native (Cordova plugin): callbackId: " +
|
||||
callbackId +
|
||||
", service: " +
|
||||
service +
|
||||
", action: " +
|
||||
action +
|
||||
", actionArgs: " +
|
||||
actionArgs
|
||||
);
|
||||
|
||||
this.callCordovaPluginMethod(callbackId, service, action, actionArgs);
|
||||
} else if (isJavaScriptError) {
|
||||
Logger.error("JavaScript Error: " + jsonStr);
|
||||
} else {
|
||||
String pluginId = postData.getString("pluginId");
|
||||
String methodName = postData.getString("methodName");
|
||||
JSObject methodData = postData.getJSObject("options", new JSObject());
|
||||
|
||||
Logger.verbose(
|
||||
Logger.tags("Plugin"),
|
||||
"To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName
|
||||
);
|
||||
|
||||
this.callPluginMethod(callbackId, pluginId, methodName, methodData);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.error("Post message error:", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendResponseMessage(PluginCall call, PluginResult successResult, PluginResult errorResult) {
|
||||
try {
|
||||
PluginResult data = new PluginResult();
|
||||
data.put("save", call.isKeptAlive());
|
||||
data.put("callbackId", call.getCallbackId());
|
||||
data.put("pluginId", call.getPluginId());
|
||||
data.put("methodName", call.getMethodName());
|
||||
|
||||
boolean pluginResultInError = errorResult != null;
|
||||
if (pluginResultInError) {
|
||||
data.put("success", false);
|
||||
data.put("error", errorResult);
|
||||
Logger.debug("Sending plugin error: " + data.toString());
|
||||
} else {
|
||||
data.put("success", true);
|
||||
if (successResult != null) {
|
||||
data.put("data", successResult);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isValidCallbackId = !call.getCallbackId().equals(PluginCall.CALLBACK_ID_DANGLING);
|
||||
if (isValidCallbackId) {
|
||||
if (bridge.getConfig().isUsingLegacyBridge()) {
|
||||
legacySendResponseMessage(data);
|
||||
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && javaScriptReplyProxy != null) {
|
||||
javaScriptReplyProxy.postMessage(data.toString());
|
||||
} else {
|
||||
legacySendResponseMessage(data);
|
||||
}
|
||||
} else {
|
||||
bridge.getApp().fireRestoredResult(data);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.error("sendResponseMessage: error: " + ex);
|
||||
}
|
||||
if (!call.isKeptAlive()) {
|
||||
call.release(bridge);
|
||||
}
|
||||
}
|
||||
|
||||
private void legacySendResponseMessage(PluginResult data) {
|
||||
final String runScript = "window.Capacitor.fromNative(" + data.toString() + ")";
|
||||
final WebView webView = this.webView;
|
||||
webView.post(() -> webView.evaluateJavascript(runScript, null));
|
||||
}
|
||||
|
||||
private void callPluginMethod(String callbackId, String pluginId, String methodName, JSObject methodData) {
|
||||
PluginCall call = new PluginCall(this, pluginId, callbackId, methodName, methodData);
|
||||
bridge.callPluginMethod(pluginId, methodName, call);
|
||||
}
|
||||
|
||||
private void callCordovaPluginMethod(String callbackId, String service, String action, String actionArgs) {
|
||||
bridge.execute(
|
||||
() -> {
|
||||
cordovaPluginManager.exec(service, action, callbackId, actionArgs);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
37
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/NativePlugin.java
generated
vendored
Normal file
37
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/NativePlugin.java
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Base annotation for all Plugins
|
||||
* @deprecated
|
||||
* <p> Use {@link CapacitorPlugin} instead
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Deprecated
|
||||
public @interface NativePlugin {
|
||||
/**
|
||||
* Request codes this plugin uses and responds to, in order to tie
|
||||
* Android events back the plugin to handle
|
||||
*/
|
||||
int[] requestCodes() default {};
|
||||
|
||||
/**
|
||||
* Permissions this plugin needs, in order to make permission requests
|
||||
* easy if the plugin only needs basic permission prompting
|
||||
*/
|
||||
String[] permissions() default {};
|
||||
|
||||
/**
|
||||
* The request code to use when automatically requesting permissions
|
||||
*/
|
||||
int permissionRequestCode() default 9000;
|
||||
|
||||
/**
|
||||
* A custom name for the plugin, otherwise uses the
|
||||
* simple class name.
|
||||
*/
|
||||
String name() default "";
|
||||
}
|
||||
31
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PermissionState.java
generated
vendored
Normal file
31
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PermissionState.java
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Represents the state of a permission
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public enum PermissionState {
|
||||
GRANTED("granted"),
|
||||
DENIED("denied"),
|
||||
PROMPT("prompt"),
|
||||
PROMPT_WITH_RATIONALE("prompt-with-rationale");
|
||||
|
||||
private String state;
|
||||
|
||||
PermissionState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public static PermissionState byState(String state) {
|
||||
state = state.toUpperCase(Locale.ROOT).replace('-', '_');
|
||||
return valueOf(state);
|
||||
}
|
||||
}
|
||||
1047
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/Plugin.java
generated
vendored
Normal file
1047
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/Plugin.java
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
440
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginCall.java
generated
vendored
Normal file
440
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginCall.java
generated
vendored
Normal file
@@ -0,0 +1,440 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Wraps a call from the web layer to native
|
||||
*/
|
||||
public class PluginCall {
|
||||
|
||||
/**
|
||||
* A special callback id that indicates there is no matching callback
|
||||
* on the client to associate any PluginCall results back to. This is used
|
||||
* in the case of an app resuming with saved instance data, for example.
|
||||
*/
|
||||
public static final String CALLBACK_ID_DANGLING = "-1";
|
||||
|
||||
private final MessageHandler msgHandler;
|
||||
private final String pluginId;
|
||||
private final String callbackId;
|
||||
private final String methodName;
|
||||
private final JSObject data;
|
||||
|
||||
private boolean keepAlive = false;
|
||||
|
||||
/**
|
||||
* Indicates that this PluginCall was released, and should no longer be used
|
||||
*/
|
||||
@Deprecated
|
||||
private boolean isReleased = false;
|
||||
|
||||
public PluginCall(MessageHandler msgHandler, String pluginId, String callbackId, String methodName, JSObject data) {
|
||||
this.msgHandler = msgHandler;
|
||||
this.pluginId = pluginId;
|
||||
this.callbackId = callbackId;
|
||||
this.methodName = methodName;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void successCallback(PluginResult successResult) {
|
||||
if (CALLBACK_ID_DANGLING.equals(this.callbackId)) {
|
||||
// don't send back response if the callbackId was "-1"
|
||||
return;
|
||||
}
|
||||
|
||||
this.msgHandler.sendResponseMessage(this, successResult, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #resolve(JSObject data)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void success(JSObject data) {
|
||||
PluginResult result = new PluginResult(data);
|
||||
this.msgHandler.sendResponseMessage(this, result, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #resolve()}
|
||||
*/
|
||||
@Deprecated
|
||||
public void success() {
|
||||
this.resolve(new JSObject());
|
||||
}
|
||||
|
||||
public void resolve(JSObject data) {
|
||||
PluginResult result = new PluginResult(data);
|
||||
this.msgHandler.sendResponseMessage(this, result, null);
|
||||
}
|
||||
|
||||
public void resolve() {
|
||||
this.msgHandler.sendResponseMessage(this, null, null);
|
||||
}
|
||||
|
||||
public void errorCallback(String msg) {
|
||||
PluginResult errorResult = new PluginResult();
|
||||
|
||||
try {
|
||||
errorResult.put("message", msg);
|
||||
} catch (Exception jsonEx) {
|
||||
Logger.error(Logger.tags("Plugin"), jsonEx.toString(), null);
|
||||
}
|
||||
|
||||
this.msgHandler.sendResponseMessage(this, null, errorResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #reject(String msg, Exception ex)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void error(String msg, Exception ex) {
|
||||
reject(msg, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #reject(String msg, String code, Exception ex)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void error(String msg, String code, Exception ex) {
|
||||
reject(msg, code, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #reject(String msg)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void error(String msg) {
|
||||
reject(msg);
|
||||
}
|
||||
|
||||
public void reject(String msg, String code, Exception ex, JSObject data) {
|
||||
PluginResult errorResult = new PluginResult();
|
||||
|
||||
if (ex != null) {
|
||||
Logger.error(Logger.tags("Plugin"), msg, ex);
|
||||
}
|
||||
|
||||
try {
|
||||
errorResult.put("message", msg);
|
||||
errorResult.put("code", code);
|
||||
if (null != data) {
|
||||
errorResult.put("data", data);
|
||||
}
|
||||
} catch (Exception jsonEx) {
|
||||
Logger.error(Logger.tags("Plugin"), jsonEx.getMessage(), jsonEx);
|
||||
}
|
||||
|
||||
this.msgHandler.sendResponseMessage(this, null, errorResult);
|
||||
}
|
||||
|
||||
public void reject(String msg, Exception ex, JSObject data) {
|
||||
reject(msg, null, ex, data);
|
||||
}
|
||||
|
||||
public void reject(String msg, String code, JSObject data) {
|
||||
reject(msg, code, null, data);
|
||||
}
|
||||
|
||||
public void reject(String msg, String code, Exception ex) {
|
||||
reject(msg, code, ex, null);
|
||||
}
|
||||
|
||||
public void reject(String msg, JSObject data) {
|
||||
reject(msg, null, null, data);
|
||||
}
|
||||
|
||||
public void reject(String msg, Exception ex) {
|
||||
reject(msg, null, ex, null);
|
||||
}
|
||||
|
||||
public void reject(String msg, String code) {
|
||||
reject(msg, code, null, null);
|
||||
}
|
||||
|
||||
public void reject(String msg) {
|
||||
reject(msg, null, null, null);
|
||||
}
|
||||
|
||||
public void unimplemented() {
|
||||
unimplemented("not implemented");
|
||||
}
|
||||
|
||||
public void unimplemented(String msg) {
|
||||
reject(msg, "UNIMPLEMENTED", null, null);
|
||||
}
|
||||
|
||||
public void unavailable() {
|
||||
unavailable("not available");
|
||||
}
|
||||
|
||||
public void unavailable(String msg) {
|
||||
reject(msg, "UNAVAILABLE", null, null);
|
||||
}
|
||||
|
||||
public String getPluginId() {
|
||||
return this.pluginId;
|
||||
}
|
||||
|
||||
public String getCallbackId() {
|
||||
return this.callbackId;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return this.methodName;
|
||||
}
|
||||
|
||||
public JSObject getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getString(String name) {
|
||||
return this.getString(name, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getString(String name, @Nullable String defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getInt(String name) {
|
||||
return this.getInt(name, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getInt(String name, @Nullable Integer defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getLong(String name) {
|
||||
return this.getLong(name, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getLong(String name, @Nullable Long defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Float getFloat(String name) {
|
||||
return this.getFloat(name, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Float getFloat(String name, @Nullable Float defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof Float) {
|
||||
return (Float) value;
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
return ((Double) value).floatValue();
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return ((Integer) value).floatValue();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double getDouble(String name) {
|
||||
return this.getDouble(name, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double getDouble(String name, @Nullable Double defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof Double) {
|
||||
return (Double) value;
|
||||
}
|
||||
if (value instanceof Float) {
|
||||
return ((Float) value).doubleValue();
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return ((Integer) value).doubleValue();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getBoolean(String name) {
|
||||
return this.getBoolean(name, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getBoolean(String name, @Nullable Boolean defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public JSObject getObject(String name) {
|
||||
return this.getObject(name, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JSObject getObject(String name, JSObject defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof JSONObject) {
|
||||
try {
|
||||
return JSObject.fromJSONObject((JSONObject) value);
|
||||
} catch (JSONException ex) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public JSArray getArray(String name) {
|
||||
return this.getArray(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSONArray and turn it into a JSArray
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public JSArray getArray(String name, JSArray defaultValue) {
|
||||
Object value = this.data.opt(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (value instanceof JSONArray) {
|
||||
try {
|
||||
JSONArray valueArray = (JSONArray) value;
|
||||
List<Object> items = new ArrayList<>();
|
||||
for (int i = 0; i < valueArray.length(); i++) {
|
||||
items.add(valueArray.get(i));
|
||||
}
|
||||
return new JSArray(items.toArray());
|
||||
} catch (JSONException ex) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name of the option to check
|
||||
* @return boolean indicating if the plugin call has an option for the provided name.
|
||||
* @deprecated Presence of a key should not be considered significant.
|
||||
* Use typed accessors to check the value instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean hasOption(String name) {
|
||||
return this.data.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the Bridge should cache this call in order to call
|
||||
* it again later. For example, the addListener system uses this to
|
||||
* continuously call the call's callback (😆).
|
||||
* @deprecated use {@link #setKeepAlive(Boolean)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void save() {
|
||||
setKeepAlive(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the Bridge should cache this call in order to call
|
||||
* it again later. For example, the addListener system uses this to
|
||||
* continuously call the call's callback.
|
||||
*
|
||||
* @param keepAlive whether to keep the callback saved
|
||||
*/
|
||||
public void setKeepAlive(Boolean keepAlive) {
|
||||
this.keepAlive = keepAlive;
|
||||
}
|
||||
|
||||
public void release(Bridge bridge) {
|
||||
this.keepAlive = false;
|
||||
bridge.releaseCall(this);
|
||||
this.isReleased = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #isKeptAlive()}
|
||||
* @return true if the plugin call is kept alive
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isSaved() {
|
||||
return isKeptAlive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the keepAlive value of the plugin call
|
||||
* @return true if the plugin call is kept alive
|
||||
*/
|
||||
public boolean isKeptAlive() {
|
||||
return keepAlive;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isReleased() {
|
||||
return isReleased;
|
||||
}
|
||||
|
||||
class PluginCallDataTypeException extends Exception {
|
||||
|
||||
PluginCallDataTypeException(String m) {
|
||||
super(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
116
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginConfig.java
generated
vendored
Normal file
116
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginConfig.java
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import com.getcapacitor.util.JSONUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Represents the configuration options for plugins used by Capacitor
|
||||
*/
|
||||
public class PluginConfig {
|
||||
|
||||
/**
|
||||
* The object containing plugin config values.
|
||||
*/
|
||||
private final JSONObject config;
|
||||
|
||||
/**
|
||||
* Constructs a PluginsConfig with the provided JSONObject value.
|
||||
*
|
||||
* @param config A plugin configuration expressed as a JSON Object
|
||||
*/
|
||||
PluginConfig(JSONObject config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string value for a plugin in the Capacitor config.
|
||||
*
|
||||
* @param configKey The key of the value to retrieve
|
||||
* @return The value from the config, if exists. Null if not
|
||||
*/
|
||||
public String getString(String configKey) {
|
||||
return getString(configKey, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string value for a plugin in the Capacitor config.
|
||||
*
|
||||
* @param configKey The key of the value to retrieve
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
public String getString(String configKey, String defaultValue) {
|
||||
return JSONUtils.getString(config, configKey, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value for a plugin in the Capacitor config.
|
||||
*
|
||||
* @param configKey The key of the value to retrieve
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
public boolean getBoolean(String configKey, boolean defaultValue) {
|
||||
return JSONUtils.getBoolean(config, configKey, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an integer value for a plugin in the Capacitor config.
|
||||
*
|
||||
* @param configKey The key of the value to retrieve
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
public int getInt(String configKey, int defaultValue) {
|
||||
return JSONUtils.getInt(config, configKey, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string array value for a plugin in the Capacitor config.
|
||||
*
|
||||
* @param configKey The key of the value to retrieve
|
||||
* @return The value from the config, if exists. Null if not
|
||||
*/
|
||||
public String[] getArray(String configKey) {
|
||||
return getArray(configKey, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string array value for a plugin in the Capacitor config.
|
||||
*
|
||||
* @param configKey The key of the value to retrieve
|
||||
* @param defaultValue A default value to return if the key does not exist in the config
|
||||
* @return The value from the config, if key exists. Default value returned if not
|
||||
*/
|
||||
public String[] getArray(String configKey, String[] defaultValue) {
|
||||
return JSONUtils.getArray(config, configKey, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON object value for a plugin in the Capacitor config.
|
||||
*
|
||||
* @param configKey The key of the value to retrieve
|
||||
* @return The value from the config, if exists. Null if not
|
||||
*/
|
||||
public JSONObject getObject(String configKey) {
|
||||
return JSONUtils.getObject(config, configKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the PluginConfig is empty.
|
||||
*
|
||||
* @return true if the plugin config has no entries
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return config.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSON Object containing the config of the the provided plugin ID.
|
||||
*
|
||||
* @return The config for that plugin
|
||||
*/
|
||||
public JSONObject getConfigJSON() {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
160
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginHandle.java
generated
vendored
Normal file
160
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginHandle.java
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* PluginHandle is an instance of a plugin that has been registered
|
||||
* and indexed. Think of it as a Plugin instance with extra metadata goodies
|
||||
*/
|
||||
public class PluginHandle {
|
||||
|
||||
private final Bridge bridge;
|
||||
private final Class<? extends Plugin> pluginClass;
|
||||
|
||||
private final Map<String, PluginMethodHandle> pluginMethods = new HashMap<>();
|
||||
|
||||
private final String pluginId;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private NativePlugin legacyPluginAnnotation;
|
||||
|
||||
private CapacitorPlugin pluginAnnotation;
|
||||
|
||||
private Plugin instance;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private PluginHandle(Class<? extends Plugin> clazz, Bridge bridge) throws InvalidPluginException {
|
||||
this.bridge = bridge;
|
||||
this.pluginClass = clazz;
|
||||
|
||||
CapacitorPlugin pluginAnnotation = pluginClass.getAnnotation(CapacitorPlugin.class);
|
||||
if (pluginAnnotation == null) {
|
||||
// Check for legacy plugin annotation, @NativePlugin
|
||||
NativePlugin legacyPluginAnnotation = pluginClass.getAnnotation(NativePlugin.class);
|
||||
if (legacyPluginAnnotation == null) {
|
||||
throw new InvalidPluginException("No @CapacitorPlugin annotation found for plugin " + pluginClass.getName());
|
||||
}
|
||||
|
||||
if (!legacyPluginAnnotation.name().equals("")) {
|
||||
this.pluginId = legacyPluginAnnotation.name();
|
||||
} else {
|
||||
this.pluginId = pluginClass.getSimpleName();
|
||||
}
|
||||
|
||||
this.legacyPluginAnnotation = legacyPluginAnnotation;
|
||||
} else {
|
||||
if (!pluginAnnotation.name().equals("")) {
|
||||
this.pluginId = pluginAnnotation.name();
|
||||
} else {
|
||||
this.pluginId = pluginClass.getSimpleName();
|
||||
}
|
||||
|
||||
this.pluginAnnotation = pluginAnnotation;
|
||||
}
|
||||
|
||||
this.indexMethods(clazz);
|
||||
}
|
||||
|
||||
public PluginHandle(Bridge bridge, Class<? extends Plugin> pluginClass) throws InvalidPluginException, PluginLoadException {
|
||||
this(pluginClass, bridge);
|
||||
this.load();
|
||||
}
|
||||
|
||||
public PluginHandle(Bridge bridge, Plugin plugin) throws InvalidPluginException {
|
||||
this(plugin.getClass(), bridge);
|
||||
this.loadInstance(plugin);
|
||||
}
|
||||
|
||||
public Class<? extends Plugin> getPluginClass() {
|
||||
return pluginClass;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.pluginId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public NativePlugin getLegacyPluginAnnotation() {
|
||||
return this.legacyPluginAnnotation;
|
||||
}
|
||||
|
||||
public CapacitorPlugin getPluginAnnotation() {
|
||||
return this.pluginAnnotation;
|
||||
}
|
||||
|
||||
public Plugin getInstance() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
public Collection<PluginMethodHandle> getMethods() {
|
||||
return this.pluginMethods.values();
|
||||
}
|
||||
|
||||
public Plugin load() throws PluginLoadException {
|
||||
if (this.instance != null) {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
try {
|
||||
this.instance = this.pluginClass.getDeclaredConstructor().newInstance();
|
||||
return this.loadInstance(instance);
|
||||
} catch (Exception ex) {
|
||||
throw new PluginLoadException("Unable to load plugin instance. Ensure plugin is publicly accessible");
|
||||
}
|
||||
}
|
||||
|
||||
public Plugin loadInstance(Plugin plugin) {
|
||||
this.instance = plugin;
|
||||
this.instance.setPluginHandle(this);
|
||||
this.instance.setBridge(this.bridge);
|
||||
this.instance.load();
|
||||
this.instance.initializeActivityLaunchers();
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a method on a plugin.
|
||||
* @param methodName the name of the method to call
|
||||
* @param call the constructed PluginCall with parameters from the caller
|
||||
* @throws InvalidPluginMethodException if no method was found on that plugin
|
||||
*/
|
||||
public void invoke(String methodName, PluginCall call)
|
||||
throws PluginLoadException, InvalidPluginMethodException, InvocationTargetException, IllegalAccessException {
|
||||
if (this.instance == null) {
|
||||
// Can throw PluginLoadException
|
||||
this.load();
|
||||
}
|
||||
|
||||
PluginMethodHandle methodMeta = pluginMethods.get(methodName);
|
||||
if (methodMeta == null) {
|
||||
throw new InvalidPluginMethodException("No method " + methodName + " found for plugin " + pluginClass.getName());
|
||||
}
|
||||
|
||||
methodMeta.getMethod().invoke(this.instance, call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Index all the known callable methods for a plugin for faster
|
||||
* invocation later
|
||||
*/
|
||||
private void indexMethods(Class<? extends Plugin> plugin) {
|
||||
//Method[] methods = pluginClass.getDeclaredMethods();
|
||||
Method[] methods = pluginClass.getMethods();
|
||||
|
||||
for (Method methodReflect : methods) {
|
||||
PluginMethod method = methodReflect.getAnnotation(PluginMethod.class);
|
||||
|
||||
if (method == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PluginMethodHandle methodMeta = new PluginMethodHandle(methodReflect, method);
|
||||
pluginMethods.put(methodReflect.getName(), methodMeta);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginInvocationException.java
generated
vendored
Normal file
16
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginInvocationException.java
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
class PluginInvocationException extends Exception {
|
||||
|
||||
public PluginInvocationException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public PluginInvocationException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
public PluginInvocationException(String s, Throwable t) {
|
||||
super(s, t);
|
||||
}
|
||||
}
|
||||
19
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginLoadException.java
generated
vendored
Normal file
19
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginLoadException.java
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
/**
|
||||
* Thrown when a plugin fails to instantiate
|
||||
*/
|
||||
public class PluginLoadException extends Exception {
|
||||
|
||||
public PluginLoadException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public PluginLoadException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
public PluginLoadException(String s, Throwable t) {
|
||||
super(s, t);
|
||||
}
|
||||
}
|
||||
56
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginManager.java
generated
vendored
Normal file
56
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginManager.java
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class PluginManager {
|
||||
|
||||
private final AssetManager assetManager;
|
||||
|
||||
public PluginManager(AssetManager assetManager) {
|
||||
this.assetManager = assetManager;
|
||||
}
|
||||
|
||||
public List<Class<? extends Plugin>> loadPluginClasses() throws PluginLoadException {
|
||||
JSONArray pluginsJSON = parsePluginsJSON();
|
||||
ArrayList<Class<? extends Plugin>> pluginList = new ArrayList<>();
|
||||
|
||||
try {
|
||||
for (int i = 0, size = pluginsJSON.length(); i < size; i++) {
|
||||
JSONObject pluginJSON = pluginsJSON.getJSONObject(i);
|
||||
String classPath = pluginJSON.getString("classpath");
|
||||
Class<?> c = Class.forName(classPath);
|
||||
pluginList.add(c.asSubclass(Plugin.class));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new PluginLoadException("Could not parse capacitor.plugins.json as JSON");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new PluginLoadException("Could not find class by class path: " + e.getMessage());
|
||||
}
|
||||
|
||||
return pluginList;
|
||||
}
|
||||
|
||||
private JSONArray parsePluginsJSON() throws PluginLoadException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open("capacitor.plugins.json")))) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
String jsonString = builder.toString();
|
||||
return new JSONArray(jsonString);
|
||||
} catch (IOException e) {
|
||||
throw new PluginLoadException("Could not load capacitor.plugins.json");
|
||||
} catch (JSONException e) {
|
||||
throw new PluginLoadException("Could not parse capacitor.plugins.json as JSON");
|
||||
}
|
||||
}
|
||||
}
|
||||
15
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginMethod.java
generated
vendored
Normal file
15
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginMethod.java
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PluginMethod {
|
||||
String RETURN_PROMISE = "promise";
|
||||
|
||||
String RETURN_CALLBACK = "callback";
|
||||
|
||||
String RETURN_NONE = "none";
|
||||
|
||||
String returnType() default RETURN_PROMISE;
|
||||
}
|
||||
33
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginMethodHandle.java
generated
vendored
Normal file
33
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginMethodHandle.java
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class PluginMethodHandle {
|
||||
|
||||
// The reflect method reference
|
||||
private final Method method;
|
||||
// The name of the method
|
||||
private final String name;
|
||||
// The return type of the method (see PluginMethod for constants)
|
||||
private final String returnType;
|
||||
|
||||
public PluginMethodHandle(Method method, PluginMethod methodDecorator) {
|
||||
this.method = method;
|
||||
|
||||
this.name = method.getName();
|
||||
|
||||
this.returnType = methodDecorator.returnType();
|
||||
}
|
||||
|
||||
public String getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
84
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginResult.java
generated
vendored
Normal file
84
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/PluginResult.java
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Wraps a result for web from calling a native plugin.
|
||||
*/
|
||||
public class PluginResult {
|
||||
|
||||
private final JSObject json;
|
||||
|
||||
public PluginResult() {
|
||||
this(new JSObject());
|
||||
}
|
||||
|
||||
public PluginResult(JSObject json) {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public PluginResult put(String name, boolean value) {
|
||||
return this.jsonPut(name, value);
|
||||
}
|
||||
|
||||
public PluginResult put(String name, double value) {
|
||||
return this.jsonPut(name, value);
|
||||
}
|
||||
|
||||
public PluginResult put(String name, int value) {
|
||||
return this.jsonPut(name, value);
|
||||
}
|
||||
|
||||
public PluginResult put(String name, long value) {
|
||||
return this.jsonPut(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a date as an ISO string
|
||||
*/
|
||||
public PluginResult put(String name, Date value) {
|
||||
TimeZone tz = TimeZone.getTimeZone("UTC");
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
|
||||
df.setTimeZone(tz);
|
||||
return this.jsonPut(name, df.format(value));
|
||||
}
|
||||
|
||||
public PluginResult put(String name, Object value) {
|
||||
return this.jsonPut(name, value);
|
||||
}
|
||||
|
||||
public PluginResult put(String name, PluginResult value) {
|
||||
return this.jsonPut(name, value.json);
|
||||
}
|
||||
|
||||
PluginResult jsonPut(String name, Object value) {
|
||||
try {
|
||||
this.json.put(name, value);
|
||||
} catch (Exception ex) {
|
||||
Logger.error(Logger.tags("Plugin"), "", ex);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.json.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin metadata and information about the result, if it succeeded the data, or error information if it didn't.
|
||||
* This is used for appRestoredResult, as it's technically a raw data response from a plugin.
|
||||
* @return the raw data response from the plugin.
|
||||
*/
|
||||
public JSObject getWrappedResult() {
|
||||
JSObject ret = new JSObject();
|
||||
ret.put("pluginId", this.json.getString("pluginId"));
|
||||
ret.put("methodName", this.json.getString("methodName"));
|
||||
ret.put("success", this.json.getBoolean("success", false));
|
||||
ret.put("data", this.json.getJSObject("data"));
|
||||
ret.put("error", this.json.getJSObject("error"));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
37
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/ProcessedRoute.java
generated
vendored
Normal file
37
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/ProcessedRoute.java
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
/**
|
||||
* An data class used in conjunction with RouteProcessor.
|
||||
*
|
||||
* @see com.getcapacitor.RouteProcessor
|
||||
*/
|
||||
public class ProcessedRoute {
|
||||
|
||||
private String path;
|
||||
private boolean isAsset;
|
||||
private boolean ignoreAssetPath;
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public boolean isAsset() {
|
||||
return isAsset;
|
||||
}
|
||||
|
||||
public void setAsset(boolean asset) {
|
||||
isAsset = asset;
|
||||
}
|
||||
|
||||
public boolean isIgnoreAssetPath() {
|
||||
return ignoreAssetPath;
|
||||
}
|
||||
|
||||
public void setIgnoreAssetPath(boolean ignoreAssetPath) {
|
||||
this.ignoreAssetPath = ignoreAssetPath;
|
||||
}
|
||||
}
|
||||
8
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/RouteProcessor.java
generated
vendored
Normal file
8
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/RouteProcessor.java
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
/**
|
||||
* An interface used in the processing of routes
|
||||
*/
|
||||
public interface RouteProcessor {
|
||||
ProcessedRoute process(String basePath, String path);
|
||||
}
|
||||
25
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/ServerPath.java
generated
vendored
Normal file
25
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/ServerPath.java
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
public class ServerPath {
|
||||
|
||||
public enum PathType {
|
||||
BASE_PATH,
|
||||
ASSET_PATH
|
||||
}
|
||||
|
||||
private final PathType type;
|
||||
private final String path;
|
||||
|
||||
public ServerPath(PathType type, String path) {
|
||||
this.type = type;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public PathType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
180
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/UriMatcher.java
generated
vendored
Executable file
180
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/UriMatcher.java
generated
vendored
Executable file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
//package com.google.webviewlocalserver.third_party.android;
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.net.Uri;
|
||||
import com.getcapacitor.util.HostMask;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UriMatcher {
|
||||
|
||||
/**
|
||||
* Creates the root node of the URI tree.
|
||||
*
|
||||
* @param code the code to match for the root URI
|
||||
*/
|
||||
public UriMatcher(Object code) {
|
||||
mCode = code;
|
||||
mWhich = -1;
|
||||
mChildren = new ArrayList<>();
|
||||
mText = null;
|
||||
}
|
||||
|
||||
private UriMatcher() {
|
||||
mCode = null;
|
||||
mWhich = -1;
|
||||
mChildren = new ArrayList<>();
|
||||
mText = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a URI to match, and the code to return when this URI is
|
||||
* matched. URI nodes may be exact match string, the token "*"
|
||||
* that matches any text, or the token "#" that matches only
|
||||
* numbers.
|
||||
* <p>
|
||||
* Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
|
||||
* this method will accept a leading slash in the path.
|
||||
*
|
||||
* @param authority the authority to match
|
||||
* @param path the path to match. * may be used as a wild card for
|
||||
* any text, and # may be used as a wild card for numbers.
|
||||
* @param code the code that is returned when a URI is matched
|
||||
* against the given components. Must be positive.
|
||||
*/
|
||||
public void addURI(String scheme, String authority, String path, Object code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("Code can't be null");
|
||||
}
|
||||
|
||||
String[] tokens = null;
|
||||
if (path != null) {
|
||||
String newPath = path;
|
||||
// Strip leading slash if present.
|
||||
if (!path.isEmpty() && path.charAt(0) == '/') {
|
||||
newPath = path.substring(1);
|
||||
}
|
||||
tokens = PATH_SPLIT_PATTERN.split(newPath);
|
||||
}
|
||||
|
||||
int numTokens = tokens != null ? tokens.length : 0;
|
||||
UriMatcher node = this;
|
||||
for (int i = -2; i < numTokens; i++) {
|
||||
String token;
|
||||
if (i == -2) token = scheme; else if (i == -1) token = authority; else token = tokens[i];
|
||||
ArrayList<UriMatcher> children = node.mChildren;
|
||||
int numChildren = children.size();
|
||||
UriMatcher child;
|
||||
int j;
|
||||
for (j = 0; j < numChildren; j++) {
|
||||
child = children.get(j);
|
||||
if (token.equals(child.mText)) {
|
||||
node = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == numChildren) {
|
||||
// Child not found, create it
|
||||
child = new UriMatcher();
|
||||
if (i == -1 && token.contains("*")) {
|
||||
child.mWhich = MASK;
|
||||
} else if (token.equals("**")) {
|
||||
child.mWhich = REST;
|
||||
} else if (token.equals("*")) {
|
||||
child.mWhich = TEXT;
|
||||
} else {
|
||||
child.mWhich = EXACT;
|
||||
}
|
||||
child.mText = token;
|
||||
node.mChildren.add(child);
|
||||
node = child;
|
||||
}
|
||||
}
|
||||
node.mCode = code;
|
||||
}
|
||||
|
||||
static final Pattern PATH_SPLIT_PATTERN = Pattern.compile("/");
|
||||
|
||||
/**
|
||||
* Try to match against the path in a url.
|
||||
*
|
||||
* @param uri The url whose path we will match against.
|
||||
* @return The code for the matched node (added using addURI),
|
||||
* or null if there is no matched node.
|
||||
*/
|
||||
public Object match(Uri uri) {
|
||||
final List<String> pathSegments = uri.getPathSegments();
|
||||
final int li = pathSegments.size();
|
||||
|
||||
UriMatcher node = this;
|
||||
|
||||
if (li == 0 && uri.getAuthority() == null) {
|
||||
return this.mCode;
|
||||
}
|
||||
|
||||
for (int i = -2; i < li; i++) {
|
||||
String u;
|
||||
if (i == -2) u = uri.getScheme(); else if (i == -1) u = uri.getAuthority(); else u = pathSegments.get(i);
|
||||
ArrayList<UriMatcher> list = node.mChildren;
|
||||
if (list == null) {
|
||||
break;
|
||||
}
|
||||
node = null;
|
||||
int lj = list.size();
|
||||
for (int j = 0; j < lj; j++) {
|
||||
UriMatcher n = list.get(j);
|
||||
which_switch:switch (n.mWhich) {
|
||||
case MASK:
|
||||
if (HostMask.Parser.parse(n.mText).matches(u)) {
|
||||
node = n;
|
||||
}
|
||||
break;
|
||||
case EXACT:
|
||||
if (n.mText.equals(u)) {
|
||||
node = n;
|
||||
}
|
||||
break;
|
||||
case TEXT:
|
||||
node = n;
|
||||
break;
|
||||
case REST:
|
||||
return n.mCode;
|
||||
}
|
||||
if (node != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return node.mCode;
|
||||
}
|
||||
|
||||
private static final int EXACT = 0;
|
||||
private static final int TEXT = 1;
|
||||
private static final int REST = 2;
|
||||
private static final int MASK = 3;
|
||||
|
||||
private Object mCode;
|
||||
private int mWhich;
|
||||
private String mText;
|
||||
private ArrayList<UriMatcher> mChildren;
|
||||
}
|
||||
57
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewListener.java
generated
vendored
Normal file
57
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewListener.java
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package com.getcapacitor;
|
||||
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* Provides callbacks associated with the {@link BridgeWebViewClient}
|
||||
*/
|
||||
public abstract class WebViewListener {
|
||||
|
||||
/**
|
||||
* Callback for page load event.
|
||||
*
|
||||
* @param webView The WebView that loaded
|
||||
*/
|
||||
public void onPageLoaded(WebView webView) {
|
||||
// Override me to add behavior to the page loaded event
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for onReceivedError event.
|
||||
*
|
||||
* @param webView The WebView that loaded
|
||||
*/
|
||||
public void onReceivedError(WebView webView) {
|
||||
// Override me to add behavior to handle the onReceivedError event
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for onReceivedHttpError event.
|
||||
*
|
||||
* @param webView The WebView that loaded
|
||||
*/
|
||||
public void onReceivedHttpError(WebView webView) {
|
||||
// Override me to add behavior to handle the onReceivedHttpError event
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for page start event.
|
||||
*
|
||||
* @param webView The WebView that loaded
|
||||
*/
|
||||
public void onPageStarted(WebView webView) {
|
||||
// Override me to add behavior to the page started event
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for render process gone event. Return true if the state is handled.
|
||||
*
|
||||
* @param webView The WebView that loaded
|
||||
* @return returns false by default if the listener is not overridden and used
|
||||
*/
|
||||
public boolean onRenderProcessGone(WebView webView, RenderProcessGoneDetail detail) {
|
||||
// Override me to add behavior to the web view render process gone event
|
||||
return false;
|
||||
}
|
||||
}
|
||||
775
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
generated
vendored
Executable file
775
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java
generated
vendored
Executable file
@@ -0,0 +1,775 @@
|
||||
/*
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package com.getcapacitor;
|
||||
|
||||
import static com.getcapacitor.plugin.util.HttpRequestHandler.isDomainExcludedFromSSL;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Base64;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import com.getcapacitor.plugin.util.CapacitorHttpUrlConnection;
|
||||
import com.getcapacitor.plugin.util.HttpRequestHandler;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper class meant to be used with the android.webkit.WebView class to enable hosting assets,
|
||||
* resources and other data on 'virtual' https:// URL.
|
||||
* Hosting assets and resources on https:// URLs is desirable as it is compatible with the
|
||||
* Same-Origin policy.
|
||||
* <p>
|
||||
* This class is intended to be used from within the
|
||||
* {@link android.webkit.WebViewClient#shouldInterceptRequest(android.webkit.WebView, String)} and
|
||||
* {@link android.webkit.WebViewClient#shouldInterceptRequest(android.webkit.WebView,
|
||||
* android.webkit.WebResourceRequest)}
|
||||
* methods.
|
||||
*/
|
||||
public class WebViewLocalServer {
|
||||
|
||||
private static final String capacitorFileStart = Bridge.CAPACITOR_FILE_START;
|
||||
private static final String capacitorContentStart = Bridge.CAPACITOR_CONTENT_START;
|
||||
private String basePath;
|
||||
|
||||
private final UriMatcher uriMatcher;
|
||||
private final AndroidProtocolHandler protocolHandler;
|
||||
private final ArrayList<String> authorities;
|
||||
private boolean isAsset;
|
||||
// Whether to route all requests to paths without extensions back to `index.html`
|
||||
private final boolean html5mode;
|
||||
private final JSInjector jsInjector;
|
||||
private final Bridge bridge;
|
||||
|
||||
/**
|
||||
* A handler that produces responses for paths on the virtual asset server.
|
||||
* <p>
|
||||
* Methods of this handler will be invoked on a background thread and care must be taken to
|
||||
* correctly synchronize access to any shared state.
|
||||
* <p>
|
||||
* On Android KitKat and above these methods may be called on more than one thread. This thread
|
||||
* may be different than the thread on which the shouldInterceptRequest method was invoke.
|
||||
* This means that on Android KitKat and above it is possible to block in this method without
|
||||
* blocking other resources from loading. The number of threads used to parallelize loading
|
||||
* is an internal implementation detail of the WebView and may change between updates which
|
||||
* means that the amount of time spend blocking in this method should be kept to an absolute
|
||||
* minimum.
|
||||
*/
|
||||
public abstract static class PathHandler {
|
||||
|
||||
protected String mimeType;
|
||||
private String encoding;
|
||||
private String charset;
|
||||
private int statusCode;
|
||||
private String reasonPhrase;
|
||||
private Map<String, String> responseHeaders;
|
||||
|
||||
public PathHandler() {
|
||||
this(null, null, 200, "OK", null);
|
||||
}
|
||||
|
||||
public PathHandler(String encoding, String charset, int statusCode, String reasonPhrase, Map<String, String> responseHeaders) {
|
||||
this.encoding = encoding;
|
||||
this.charset = charset;
|
||||
this.statusCode = statusCode;
|
||||
this.reasonPhrase = reasonPhrase;
|
||||
Map<String, String> tempResponseHeaders;
|
||||
if (responseHeaders == null) {
|
||||
tempResponseHeaders = new HashMap<>();
|
||||
} else {
|
||||
tempResponseHeaders = responseHeaders;
|
||||
}
|
||||
tempResponseHeaders.put("Cache-Control", "no-cache");
|
||||
this.responseHeaders = tempResponseHeaders;
|
||||
}
|
||||
|
||||
public InputStream handle(WebResourceRequest request) {
|
||||
return handle(request.getUrl());
|
||||
}
|
||||
|
||||
public abstract InputStream handle(Uri url);
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public String getReasonPhrase() {
|
||||
return reasonPhrase;
|
||||
}
|
||||
|
||||
public Map<String, String> getResponseHeaders() {
|
||||
return responseHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
WebViewLocalServer(Context context, Bridge bridge, JSInjector jsInjector, ArrayList<String> authorities, boolean html5mode) {
|
||||
uriMatcher = new UriMatcher(null);
|
||||
this.html5mode = html5mode;
|
||||
this.protocolHandler = new AndroidProtocolHandler(context.getApplicationContext());
|
||||
this.authorities = authorities;
|
||||
this.bridge = bridge;
|
||||
this.jsInjector = jsInjector;
|
||||
}
|
||||
|
||||
private static Uri parseAndVerifyUrl(String url) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
Uri uri = Uri.parse(url);
|
||||
if (uri == null) {
|
||||
Logger.error("Malformed URL: " + url);
|
||||
return null;
|
||||
}
|
||||
String path = uri.getPath();
|
||||
if (path == null || path.isEmpty()) {
|
||||
Logger.error("URL does not have a path: " + url);
|
||||
return null;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to retrieve the WebResourceResponse associated with the given <code>request</code>.
|
||||
* This method should be invoked from within
|
||||
* {@link android.webkit.WebViewClient#shouldInterceptRequest(android.webkit.WebView,
|
||||
* android.webkit.WebResourceRequest)}.
|
||||
*
|
||||
* @param request the request to process.
|
||||
* @return a response if the request URL had a matching handler, null if no handler was found.
|
||||
*/
|
||||
public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
|
||||
Uri loadingUrl = request.getUrl();
|
||||
|
||||
if (null != loadingUrl.getPath() && loadingUrl.getPath().startsWith(Bridge.CAPACITOR_HTTP_INTERCEPTOR_START)) {
|
||||
// Only fetch/XHR should reach the proxy; a document would run remote content at the app origin.
|
||||
boolean httpEnabled = bridge.getConfig().getPluginConfiguration("CapacitorHttp").getBoolean("enabled", false);
|
||||
if (!httpEnabled || isDocumentRequest(request)) {
|
||||
return null;
|
||||
}
|
||||
Logger.debug("Handling CapacitorHttp request: " + loadingUrl);
|
||||
try {
|
||||
return handleCapacitorHttpRequest(request);
|
||||
} catch (Exception e) {
|
||||
Logger.error(e.getLocalizedMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
PathHandler handler;
|
||||
synchronized (uriMatcher) {
|
||||
handler = (PathHandler) uriMatcher.match(request.getUrl());
|
||||
}
|
||||
if (handler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isLocalFile(loadingUrl) || isMainUrl(loadingUrl) || !isAllowedUrl(loadingUrl) || isErrorUrl(loadingUrl)) {
|
||||
Logger.debug("Handling local request: " + request.getUrl().toString());
|
||||
return handleLocalRequest(request, handler);
|
||||
} else {
|
||||
return handleProxyRequest(request, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/** isForMainFrame() is false for an iframe and a fetch alike; only navigations send this header. */
|
||||
private boolean isDocumentRequest(WebResourceRequest request) {
|
||||
if (request.isForMainFrame()) {
|
||||
return true;
|
||||
}
|
||||
Map<String, String> headers = request.getRequestHeaders();
|
||||
if (headers == null) {
|
||||
// The proxy needs the headers too, so the request fails there anyway.
|
||||
return false;
|
||||
}
|
||||
for (String header : headers.keySet()) {
|
||||
if ("Upgrade-Insecure-Requests".equalsIgnoreCase(header)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isLocalFile(Uri uri) {
|
||||
String path = uri.getPath();
|
||||
return path.startsWith(capacitorContentStart) || path.startsWith(capacitorFileStart);
|
||||
}
|
||||
|
||||
private boolean isErrorUrl(Uri uri) {
|
||||
String url = uri.toString();
|
||||
return url.equals(bridge.getErrorUrl());
|
||||
}
|
||||
|
||||
private boolean isMainUrl(Uri loadingUrl) {
|
||||
return (bridge.getServerUrl() == null && loadingUrl.getHost().equalsIgnoreCase(bridge.getHost()));
|
||||
}
|
||||
|
||||
private boolean isAllowedUrl(Uri loadingUrl) {
|
||||
return !(bridge.getServerUrl() == null && !bridge.getAppAllowNavigationMask().matches(loadingUrl.getHost()));
|
||||
}
|
||||
|
||||
private String getReasonPhraseFromResponseCode(int code) {
|
||||
return switch (code) {
|
||||
case 100 -> "Continue";
|
||||
case 101 -> "Switching Protocols";
|
||||
case 200 -> "OK";
|
||||
case 201 -> "Created";
|
||||
case 202 -> "Accepted";
|
||||
case 203 -> "Non-Authoritative Information";
|
||||
case 204 -> "No Content";
|
||||
case 205 -> "Reset Content";
|
||||
case 206 -> "Partial Content";
|
||||
case 300 -> "Multiple Choices";
|
||||
case 301 -> "Moved Permanently";
|
||||
case 302 -> "Found";
|
||||
case 303 -> "See Other";
|
||||
case 304 -> "Not Modified";
|
||||
case 400 -> "Bad Request";
|
||||
case 401 -> "Unauthorized";
|
||||
case 403 -> "Forbidden";
|
||||
case 404 -> "Not Found";
|
||||
case 405 -> "Method Not Allowed";
|
||||
case 406 -> "Not Acceptable";
|
||||
case 407 -> "Proxy Authentication Required";
|
||||
case 408 -> "Request Timeout";
|
||||
case 409 -> "Conflict";
|
||||
case 410 -> "Gone";
|
||||
case 500 -> "Internal Server Error";
|
||||
case 501 -> "Not Implemented";
|
||||
case 502 -> "Bad Gateway";
|
||||
case 503 -> "Service Unavailable";
|
||||
case 504 -> "Gateway Timeout";
|
||||
case 505 -> "HTTP Version Not Supported";
|
||||
default -> "Unknown";
|
||||
};
|
||||
}
|
||||
|
||||
private WebResourceResponse handleCapacitorHttpRequest(WebResourceRequest request) throws IOException {
|
||||
String urlString = request.getUrl().getQueryParameter(Bridge.CAPACITOR_HTTP_INTERCEPTOR_URL_PARAM);
|
||||
URL url = new URL(urlString);
|
||||
JSObject headers = new JSObject();
|
||||
|
||||
for (Map.Entry<String, String> header : request.getRequestHeaders().entrySet()) {
|
||||
headers.put(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
HttpRequestHandler.HttpURLConnectionBuilder connectionBuilder = new HttpRequestHandler.HttpURLConnectionBuilder()
|
||||
.setUrl(url)
|
||||
.setMethod(request.getMethod())
|
||||
.setHeaders(headers)
|
||||
.openConnection();
|
||||
|
||||
CapacitorHttpUrlConnection connection = connectionBuilder.build();
|
||||
|
||||
if (!isDomainExcludedFromSSL(bridge, url)) {
|
||||
connection.setSSLSocketFactory(bridge);
|
||||
}
|
||||
|
||||
connection.connect();
|
||||
|
||||
String mimeType = null;
|
||||
String encoding = null;
|
||||
Map<String, String> responseHeaders = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String value : entry.getValue()) {
|
||||
builder.append(value);
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.setLength(builder.length() - 2);
|
||||
|
||||
if ("Content-Type".equalsIgnoreCase(entry.getKey())) {
|
||||
String[] contentTypeParts = builder.toString().split(";");
|
||||
mimeType = contentTypeParts[0].trim();
|
||||
if (contentTypeParts.length > 1) {
|
||||
String[] encodingParts = contentTypeParts[1].split("=");
|
||||
if (encodingParts.length > 1) {
|
||||
encoding = encodingParts[1].trim();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
responseHeaders.put(entry.getKey(), builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
InputStream inputStream = connection.getErrorStream();
|
||||
if (inputStream == null) {
|
||||
inputStream = connection.getInputStream();
|
||||
}
|
||||
|
||||
if (null == mimeType) {
|
||||
mimeType = getMimeType(request.getUrl().getPath(), inputStream);
|
||||
}
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String reasonPhrase = getReasonPhraseFromResponseCode(responseCode);
|
||||
|
||||
// Nothing should render this. If anything does, sandbox keeps it inert and off the app origin.
|
||||
responseHeaders.put("Content-Security-Policy", "sandbox; frame-ancestors 'none'");
|
||||
|
||||
return new WebResourceResponse(mimeType, encoding, responseCode, reasonPhrase, responseHeaders, inputStream);
|
||||
}
|
||||
|
||||
private WebResourceResponse handleLocalRequest(WebResourceRequest request, PathHandler handler) {
|
||||
String path = request.getUrl().getPath();
|
||||
|
||||
if (request.getRequestHeaders().get("Range") != null) {
|
||||
InputStream responseStream = new LollipopLazyInputStream(handler, request);
|
||||
String mimeType = getMimeType(path, responseStream);
|
||||
Map<String, String> tempResponseHeaders = handler.getResponseHeaders();
|
||||
int statusCode = 206;
|
||||
try {
|
||||
int totalRange = responseStream.available();
|
||||
String rangeString = request.getRequestHeaders().get("Range");
|
||||
String[] parts = rangeString.split("=");
|
||||
String[] streamParts = parts[1].split("-");
|
||||
String fromRange = streamParts[0];
|
||||
int range = totalRange - 1;
|
||||
if (streamParts.length > 1) {
|
||||
range = Integer.parseInt(streamParts[1]);
|
||||
}
|
||||
tempResponseHeaders.put("Accept-Ranges", "bytes");
|
||||
tempResponseHeaders.put("Content-Range", "bytes " + fromRange + "-" + range + "/" + totalRange);
|
||||
} catch (IOException e) {
|
||||
statusCode = 404;
|
||||
}
|
||||
return new WebResourceResponse(
|
||||
mimeType,
|
||||
handler.getEncoding(),
|
||||
statusCode,
|
||||
handler.getReasonPhrase(),
|
||||
tempResponseHeaders,
|
||||
responseStream
|
||||
);
|
||||
}
|
||||
|
||||
if (isLocalFile(request.getUrl()) || isErrorUrl(request.getUrl())) {
|
||||
InputStream responseStream = new LollipopLazyInputStream(handler, request);
|
||||
String mimeType = getMimeType(request.getUrl().getPath(), responseStream);
|
||||
int statusCode = getStatusCode(responseStream, handler.getStatusCode());
|
||||
return new WebResourceResponse(
|
||||
mimeType,
|
||||
handler.getEncoding(),
|
||||
statusCode,
|
||||
handler.getReasonPhrase(),
|
||||
handler.getResponseHeaders(),
|
||||
responseStream
|
||||
);
|
||||
}
|
||||
|
||||
if (path.equals("/cordova.js")) {
|
||||
return new WebResourceResponse(
|
||||
"application/javascript",
|
||||
handler.getEncoding(),
|
||||
handler.getStatusCode(),
|
||||
handler.getReasonPhrase(),
|
||||
handler.getResponseHeaders(),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if (path.equals("/") || (!request.getUrl().getLastPathSegment().contains(".") && html5mode)) {
|
||||
InputStream responseStream;
|
||||
try {
|
||||
String startPath = this.basePath + "/index.html";
|
||||
if (bridge.getRouteProcessor() != null) {
|
||||
ProcessedRoute processedRoute = bridge.getRouteProcessor().process(this.basePath, "/index.html");
|
||||
startPath = processedRoute.getPath();
|
||||
isAsset = processedRoute.isAsset();
|
||||
}
|
||||
|
||||
if (isAsset) {
|
||||
responseStream = protocolHandler.openAsset(startPath);
|
||||
} else {
|
||||
responseStream = protocolHandler.openFile(startPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logger.error("Unable to open index.html", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (jsInjector != null) {
|
||||
responseStream = jsInjector.getInjectedStream(responseStream);
|
||||
}
|
||||
|
||||
int statusCode = getStatusCode(responseStream, handler.getStatusCode());
|
||||
return new WebResourceResponse(
|
||||
"text/html",
|
||||
handler.getEncoding(),
|
||||
statusCode,
|
||||
handler.getReasonPhrase(),
|
||||
handler.getResponseHeaders(),
|
||||
responseStream
|
||||
);
|
||||
}
|
||||
|
||||
if ("/favicon.ico".equalsIgnoreCase(path)) {
|
||||
try {
|
||||
return new WebResourceResponse("image/png", null, null);
|
||||
} catch (Exception e) {
|
||||
Logger.error("favicon handling failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
int periodIndex = path.lastIndexOf(".");
|
||||
if (periodIndex >= 0) {
|
||||
String ext = path.substring(path.lastIndexOf("."));
|
||||
|
||||
InputStream responseStream = new LollipopLazyInputStream(handler, request);
|
||||
|
||||
// TODO: Conjure up a bit more subtlety than this
|
||||
if (ext.equals(".html") && jsInjector != null) {
|
||||
responseStream = jsInjector.getInjectedStream(responseStream);
|
||||
}
|
||||
|
||||
String mimeType = getMimeType(path, responseStream);
|
||||
int statusCode = getStatusCode(responseStream, handler.getStatusCode());
|
||||
return new WebResourceResponse(
|
||||
mimeType,
|
||||
handler.getEncoding(),
|
||||
statusCode,
|
||||
handler.getReasonPhrase(),
|
||||
handler.getResponseHeaders(),
|
||||
responseStream
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends an {@code InputStream} with the JavaScript required by Capacitor.
|
||||
* This method only changes the original {@code InputStream} if {@code WebView} does not
|
||||
* support the {@code DOCUMENT_START_SCRIPT} feature.
|
||||
* @param original the original {@code InputStream}
|
||||
* @return the modified {@code InputStream}
|
||||
*/
|
||||
public InputStream getJavaScriptInjectedStream(InputStream original) {
|
||||
if (jsInjector != null) {
|
||||
return jsInjector.getInjectedStream(original);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of reading files from the filesystem/assets, proxy through to the URL
|
||||
* and let an external server handle it.
|
||||
* @param request
|
||||
* @param handler
|
||||
* @return
|
||||
*/
|
||||
private WebResourceResponse handleProxyRequest(WebResourceRequest request, PathHandler handler) {
|
||||
if (jsInjector != null) {
|
||||
final String method = request.getMethod();
|
||||
if (method.equals("GET")) {
|
||||
try {
|
||||
String url = request.getUrl().toString();
|
||||
Map<String, String> headers = request.getRequestHeaders();
|
||||
boolean isHtmlText = false;
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
if (header.getKey().equalsIgnoreCase("Accept") && header.getValue().toLowerCase().contains("text/html")) {
|
||||
isHtmlText = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isHtmlText) {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
conn.setRequestProperty(header.getKey(), header.getValue());
|
||||
}
|
||||
String getCookie = CookieManager.getInstance().getCookie(url);
|
||||
if (getCookie != null) {
|
||||
conn.setRequestProperty("Cookie", getCookie);
|
||||
}
|
||||
conn.setRequestMethod(method);
|
||||
conn.setReadTimeout(30 * 1000);
|
||||
conn.setConnectTimeout(30 * 1000);
|
||||
if (request.getUrl().getUserInfo() != null) {
|
||||
byte[] userInfoBytes = request.getUrl().getUserInfo().getBytes(StandardCharsets.UTF_8);
|
||||
String base64 = Base64.encodeToString(userInfoBytes, Base64.NO_WRAP);
|
||||
conn.setRequestProperty("Authorization", "Basic " + base64);
|
||||
}
|
||||
|
||||
List<String> cookies = conn.getHeaderFields().get("Set-Cookie");
|
||||
if (cookies != null) {
|
||||
for (String cookie : cookies) {
|
||||
CookieManager.getInstance().setCookie(url, cookie);
|
||||
}
|
||||
}
|
||||
InputStream responseStream = conn.getInputStream();
|
||||
responseStream = jsInjector.getInjectedStream(responseStream);
|
||||
|
||||
return new WebResourceResponse(
|
||||
"text/html",
|
||||
handler.getEncoding(),
|
||||
handler.getStatusCode(),
|
||||
handler.getReasonPhrase(),
|
||||
handler.getResponseHeaders(),
|
||||
responseStream
|
||||
);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
bridge.handleAppUrlLoadError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getMimeType(String path, InputStream stream) {
|
||||
String mimeType = null;
|
||||
try {
|
||||
mimeType = URLConnection.guessContentTypeFromName(path); // Does not recognize *.js
|
||||
if (mimeType != null && path.endsWith(".js") && mimeType.equals("image/x-icon")) {
|
||||
Logger.debug("We shouldn't be here");
|
||||
}
|
||||
if (mimeType == null) {
|
||||
if (path.endsWith(".js") || path.endsWith(".mjs")) {
|
||||
// Make sure JS files get the proper mimetype to support ES modules
|
||||
mimeType = "application/javascript";
|
||||
} else if (path.endsWith(".wasm")) {
|
||||
mimeType = "application/wasm";
|
||||
} else {
|
||||
mimeType = URLConnection.guessContentTypeFromStream(stream);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.error("Unable to get mime type" + path, ex);
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
private int getStatusCode(InputStream stream, int defaultCode) {
|
||||
int finalStatusCode = defaultCode;
|
||||
try {
|
||||
if (stream.available() == -1) {
|
||||
finalStatusCode = 404;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
finalStatusCode = 500;
|
||||
}
|
||||
return finalStatusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for the given <code>uri</code>. The <code>handler</code> will be invoked
|
||||
* every time the <code>shouldInterceptRequest</code> method of the instance is called with
|
||||
* a matching <code>uri</code>.
|
||||
*
|
||||
* @param uri the uri to use the handler for. The scheme and authority (domain) will be matched
|
||||
* exactly. The path may contain a '*' element which will match a single element of
|
||||
* a path (so a handler registered for /a/* will be invoked for /a/b and /a/c.html
|
||||
* but not for /a/b/b) or the '**' element which will match any number of path
|
||||
* elements.
|
||||
* @param handler the handler to use for the uri.
|
||||
*/
|
||||
void register(Uri uri, PathHandler handler) {
|
||||
synchronized (uriMatcher) {
|
||||
uriMatcher.addURI(uri.getScheme(), uri.getAuthority(), uri.getPath(), handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hosts the application's assets on an https:// URL. Assets from the local path
|
||||
* <code>assetPath/...</code> will be available under
|
||||
* <code>https://{uuid}.androidplatform.net/assets/...</code>.
|
||||
*
|
||||
* @param assetPath the local path in the application's asset folder which will be made
|
||||
* available by the server (for example "/www").
|
||||
* @return prefixes under which the assets are hosted.
|
||||
*/
|
||||
public void hostAssets(String assetPath) {
|
||||
this.isAsset = true;
|
||||
this.basePath = assetPath;
|
||||
createHostingDetails();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hosts the application's files on an https:// URL. Files from the basePath
|
||||
* <code>basePath/...</code> will be available under
|
||||
* <code>https://{uuid}.androidplatform.net/...</code>.
|
||||
*
|
||||
* @param basePath the local path in the application's data folder which will be made
|
||||
* available by the server (for example "/www").
|
||||
* @return prefixes under which the assets are hosted.
|
||||
*/
|
||||
public void hostFiles(final String basePath) {
|
||||
this.isAsset = false;
|
||||
this.basePath = basePath;
|
||||
createHostingDetails();
|
||||
}
|
||||
|
||||
private void createHostingDetails() {
|
||||
final String assetPath = this.basePath;
|
||||
|
||||
if (assetPath.indexOf('*') != -1) {
|
||||
throw new IllegalArgumentException("assetPath cannot contain the '*' character.");
|
||||
}
|
||||
|
||||
PathHandler handler = new PathHandler() {
|
||||
@Override
|
||||
public InputStream handle(Uri url) {
|
||||
InputStream stream = null;
|
||||
String path = url.getPath();
|
||||
|
||||
// Pass path to routeProcessor if present
|
||||
RouteProcessor routeProcessor = bridge.getRouteProcessor();
|
||||
boolean ignoreAssetPath = false;
|
||||
if (routeProcessor != null) {
|
||||
ProcessedRoute processedRoute = bridge.getRouteProcessor().process("", path);
|
||||
path = processedRoute.getPath();
|
||||
isAsset = processedRoute.isAsset();
|
||||
ignoreAssetPath = processedRoute.isIgnoreAssetPath();
|
||||
}
|
||||
|
||||
try {
|
||||
if (path.startsWith(capacitorContentStart)) {
|
||||
stream = protocolHandler.openContentUrl(url);
|
||||
} else if (path.startsWith(capacitorFileStart)) {
|
||||
stream = protocolHandler.openFile(path);
|
||||
} else if (!isAsset) {
|
||||
if (routeProcessor == null) {
|
||||
path = basePath + url.getPath();
|
||||
}
|
||||
|
||||
stream = protocolHandler.openFile(path);
|
||||
} else if (ignoreAssetPath) {
|
||||
stream = protocolHandler.openAsset(path);
|
||||
} else {
|
||||
stream = protocolHandler.openAsset(assetPath + path);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logger.error("Unable to open asset URL: " + url);
|
||||
return null;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
for (String authority : authorities) {
|
||||
registerUriForScheme(Bridge.CAPACITOR_HTTP_SCHEME, handler, authority);
|
||||
registerUriForScheme(Bridge.CAPACITOR_HTTPS_SCHEME, handler, authority);
|
||||
|
||||
String customScheme = this.bridge.getScheme();
|
||||
if (!customScheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !customScheme.equals(Bridge.CAPACITOR_HTTPS_SCHEME)) {
|
||||
registerUriForScheme(customScheme, handler, authority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerUriForScheme(String scheme, PathHandler handler, String authority) {
|
||||
Uri.Builder uriBuilder = new Uri.Builder();
|
||||
uriBuilder.scheme(scheme);
|
||||
uriBuilder.authority(authority);
|
||||
uriBuilder.path("");
|
||||
Uri uriPrefix = uriBuilder.build();
|
||||
|
||||
register(Uri.withAppendedPath(uriPrefix, "/"), handler);
|
||||
register(Uri.withAppendedPath(uriPrefix, "**"), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* The KitKat WebView reads the InputStream on a separate threadpool. We can use that to
|
||||
* parallelize loading.
|
||||
*/
|
||||
private abstract static class LazyInputStream extends InputStream {
|
||||
|
||||
protected final PathHandler handler;
|
||||
private InputStream is = null;
|
||||
|
||||
public LazyInputStream(PathHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
private InputStream getInputStream() {
|
||||
if (is == null) {
|
||||
is = handle();
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
protected abstract InputStream handle();
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
InputStream is = getInputStream();
|
||||
return (is != null) ? is.available() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
InputStream is = getInputStream();
|
||||
return (is != null) ? is.read() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
InputStream is = getInputStream();
|
||||
return (is != null) ? is.read(b) : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
InputStream is = getInputStream();
|
||||
return (is != null) ? is.read(b, off, len) : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
InputStream is = getInputStream();
|
||||
return (is != null) ? is.skip(n) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// For L and above.
|
||||
private static class LollipopLazyInputStream extends LazyInputStream {
|
||||
|
||||
private WebResourceRequest request;
|
||||
private InputStream is;
|
||||
|
||||
public LollipopLazyInputStream(PathHandler handler, WebResourceRequest request) {
|
||||
super(handler);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream handle() {
|
||||
return handler.handle(request);
|
||||
}
|
||||
}
|
||||
|
||||
public String getBasePath() {
|
||||
return this.basePath;
|
||||
}
|
||||
}
|
||||
11
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/ActivityCallback.java
generated
vendored
Normal file
11
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/ActivityCallback.java
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.getcapacitor.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface ActivityCallback {
|
||||
}
|
||||
35
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/CapacitorPlugin.java
generated
vendored
Normal file
35
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/CapacitorPlugin.java
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.getcapacitor.annotation;
|
||||
|
||||
import android.content.Intent;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Base annotation for all Plugins
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CapacitorPlugin {
|
||||
/**
|
||||
* A custom name for the plugin, otherwise uses the
|
||||
* simple class name.
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* Request codes this plugin uses and responds to, in order to tie
|
||||
* Android events back the plugin to handle.
|
||||
*
|
||||
* NOTE: This is a legacy option provided to support third party libraries
|
||||
* not currently implementing the new AndroidX Activity Results API. Plugins
|
||||
* without this limitation should use a registered callback with
|
||||
* {@link com.getcapacitor.Plugin#startActivityForResult(PluginCall, Intent, String)}
|
||||
*/
|
||||
int[] requestCodes() default {};
|
||||
|
||||
/**
|
||||
* Permissions this plugin needs, in order to make permission requests
|
||||
* easy if the plugin only needs basic permission prompting
|
||||
*/
|
||||
Permission[] permissions() default {};
|
||||
}
|
||||
22
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/Permission.java
generated
vendored
Normal file
22
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/Permission.java
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.getcapacitor.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Permission annotation for use with @CapacitorPlugin
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Permission {
|
||||
/**
|
||||
* An array of Android permission strings.
|
||||
* Eg: {Manifest.permission.ACCESS_COARSE_LOCATION}
|
||||
* or {"android.permission.ACCESS_COARSE_LOCATION"}
|
||||
*/
|
||||
String[] strings() default {};
|
||||
|
||||
/**
|
||||
* An optional name to use instead of the Android permission string.
|
||||
*/
|
||||
String alias() default "";
|
||||
}
|
||||
11
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/PermissionCallback.java
generated
vendored
Normal file
11
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/annotation/PermissionCallback.java
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.getcapacitor.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface PermissionCallback {
|
||||
}
|
||||
42
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java
generated
vendored
Normal file
42
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.getcapacitor.cordova;
|
||||
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.WebView;
|
||||
import org.apache.cordova.ICordovaCookieManager;
|
||||
|
||||
class CapacitorCordovaCookieManager implements ICordovaCookieManager {
|
||||
|
||||
protected final WebView webView;
|
||||
private final CookieManager cookieManager;
|
||||
|
||||
public CapacitorCordovaCookieManager(WebView webview) {
|
||||
webView = webview;
|
||||
cookieManager = CookieManager.getInstance();
|
||||
cookieManager.setAcceptThirdPartyCookies(webView, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookiesEnabled(boolean accept) {
|
||||
cookieManager.setAcceptCookie(accept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookie(final String url, final String value) {
|
||||
cookieManager.setCookie(url, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCookie(final String url) {
|
||||
return cookieManager.getCookie(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCookies() {
|
||||
cookieManager.removeAllCookies(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
cookieManager.flush();
|
||||
}
|
||||
}
|
||||
39
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java
generated
vendored
Normal file
39
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaInterfaceImpl.java
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.getcapacitor.cordova;
|
||||
|
||||
import android.util.Pair;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.apache.cordova.CordovaInterfaceImpl;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class MockCordovaInterfaceImpl extends CordovaInterfaceImpl {
|
||||
|
||||
public MockCordovaInterfaceImpl(AppCompatActivity activity) {
|
||||
super(activity, Executors.newCachedThreadPool());
|
||||
}
|
||||
|
||||
public CordovaPlugin getActivityResultCallback() {
|
||||
return this.activityResultCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks Cordova permission callbacks to handle permissions defined by a Cordova plugin.
|
||||
* Returns true if Cordova is handling the permission request with a registered code.
|
||||
*
|
||||
* @param requestCode
|
||||
* @param permissions
|
||||
* @param grantResults
|
||||
* @return true if Cordova handled the permission request, false if not
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean handlePermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {
|
||||
Pair<CordovaPlugin, Integer> callback = permissionResultCallbacks.getAndRemoveCallback(requestCode);
|
||||
if (callback != null) {
|
||||
callback.first.onRequestPermissionResult(callback.second, permissions, grantResults);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
284
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java
generated
vendored
Normal file
284
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
package com.getcapacitor.cordova;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPreferences;
|
||||
import org.apache.cordova.CordovaResourceApi;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaWebViewEngine;
|
||||
import org.apache.cordova.ICordovaCookieManager;
|
||||
import org.apache.cordova.NativeToJsMessageQueue;
|
||||
import org.apache.cordova.PluginEntry;
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.apache.cordova.PluginResult;
|
||||
|
||||
public class MockCordovaWebViewImpl implements CordovaWebView {
|
||||
|
||||
private Context context;
|
||||
private PluginManager pluginManager;
|
||||
private CordovaPreferences preferences;
|
||||
private CordovaResourceApi resourceApi;
|
||||
private NativeToJsMessageQueue nativeToJsMessageQueue;
|
||||
private CordovaInterface cordova;
|
||||
private CapacitorCordovaCookieManager cookieManager;
|
||||
private WebView webView;
|
||||
private boolean hasPausedEver;
|
||||
|
||||
public MockCordovaWebViewImpl(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences) {
|
||||
this.cordova = cordova;
|
||||
this.preferences = preferences;
|
||||
this.pluginManager = new PluginManager(this, this.cordova, pluginEntries);
|
||||
this.resourceApi = new CordovaResourceApi(this.context, this.pluginManager);
|
||||
this.pluginManager.init();
|
||||
}
|
||||
|
||||
public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences, WebView webView) {
|
||||
this.cordova = cordova;
|
||||
this.webView = webView;
|
||||
this.preferences = preferences;
|
||||
this.pluginManager = new PluginManager(this, this.cordova, pluginEntries);
|
||||
this.resourceApi = new CordovaResourceApi(this.context, this.pluginManager);
|
||||
nativeToJsMessageQueue = new NativeToJsMessageQueue();
|
||||
nativeToJsMessageQueue.addBridgeMode(new CapacitorEvalBridgeMode(webView, this.cordova));
|
||||
nativeToJsMessageQueue.setBridgeMode(0);
|
||||
this.cookieManager = new CapacitorCordovaCookieManager(webView);
|
||||
this.pluginManager.init();
|
||||
}
|
||||
|
||||
public static class CapacitorEvalBridgeMode extends NativeToJsMessageQueue.BridgeMode {
|
||||
|
||||
private final WebView webView;
|
||||
private final CordovaInterface cordova;
|
||||
|
||||
public CapacitorEvalBridgeMode(WebView webView, CordovaInterface cordova) {
|
||||
this.webView = webView;
|
||||
this.cordova = cordova;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) {
|
||||
cordova
|
||||
.getActivity()
|
||||
.runOnUiThread(
|
||||
() -> {
|
||||
String js = queue.popAndEncodeAsJs();
|
||||
if (js != null) {
|
||||
webView.evaluateJavascript(js, null);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return cordova != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return this.webView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUrlIntoView(String url, boolean recreatePlugins) {
|
||||
if (url.equals("about:blank") || url.startsWith("javascript:")) {
|
||||
webView.loadUrl(url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLoading() {}
|
||||
|
||||
@Override
|
||||
public boolean canGoBack() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void clearCache(boolean b) {}
|
||||
|
||||
@Override
|
||||
public void clearHistory() {}
|
||||
|
||||
@Override
|
||||
public boolean backHistory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePause(boolean keepRunning) {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
hasPausedEver = true;
|
||||
pluginManager.onPause(keepRunning);
|
||||
triggerDocumentEvent("pause");
|
||||
// If app doesn't want to run in background
|
||||
if (!keepRunning) {
|
||||
// Pause JavaScript timers. This affects all webviews within the app!
|
||||
this.setPaused(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
if (this.pluginManager != null) {
|
||||
this.pluginManager.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResume(boolean keepRunning) {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
this.setPaused(false);
|
||||
this.pluginManager.onResume(keepRunning);
|
||||
if (hasPausedEver) {
|
||||
triggerDocumentEvent("resume");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStart() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
pluginManager.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStop() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
pluginManager.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDestroy() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
this.pluginManager.onDestroy();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void sendJavascript(String statememt) {
|
||||
nativeToJsMessageQueue.addJavaScript(statememt);
|
||||
}
|
||||
|
||||
public void eval(final String js, final ValueCallback<String> callback) {
|
||||
Handler mainHandler = new Handler(context.getMainLooper());
|
||||
mainHandler.post(() -> webView.evaluateJavascript(js, callback));
|
||||
}
|
||||
|
||||
public void triggerDocumentEvent(final String eventName) {
|
||||
eval("window.Capacitor.triggerEvent('" + eventName + "', 'document');", s -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showWebPage(String url, boolean openExternal, boolean clearHistory, Map<String, Object> params) {}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean isCustomViewShowing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) {}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void hideCustomView() {}
|
||||
|
||||
@Override
|
||||
public CordovaResourceApi getResourceApi() {
|
||||
return this.resourceApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setButtonPlumbedToJs(int keyCode, boolean override) {}
|
||||
|
||||
@Override
|
||||
public boolean isButtonPlumbedToJs(int keyCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPluginResult(PluginResult cr, String callbackId) {
|
||||
nativeToJsMessageQueue.addPluginResult(cr, callbackId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginManager getPluginManager() {
|
||||
return this.pluginManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CordovaWebViewEngine getEngine() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CordovaPreferences getPreferences() {
|
||||
return this.preferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICordovaCookieManager getCookieManager() {
|
||||
return cookieManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return webView.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return this.webView.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUrl(String url) {
|
||||
loadUrlIntoView(url, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postMessage(String id, Object data) {
|
||||
return pluginManager.postMessage(id, data);
|
||||
}
|
||||
|
||||
public void setPaused(boolean value) {
|
||||
if (value) {
|
||||
webView.onPause();
|
||||
webView.pauseTimers();
|
||||
} else {
|
||||
webView.onResume();
|
||||
webView.resumeTimers();
|
||||
}
|
||||
}
|
||||
}
|
||||
239
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java
generated
vendored
Normal file
239
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
package com.getcapacitor.plugin;
|
||||
|
||||
import com.getcapacitor.Bridge;
|
||||
import com.getcapacitor.Logger;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
import java.net.CookieStore;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CapacitorCookieManager extends CookieManager {
|
||||
|
||||
private final android.webkit.CookieManager webkitCookieManager;
|
||||
|
||||
private final String localUrl;
|
||||
|
||||
private final String serverUrl;
|
||||
|
||||
private final String TAG = "CapacitorCookies";
|
||||
|
||||
/**
|
||||
* Create a new cookie manager with the default cookie store and policy
|
||||
*/
|
||||
public CapacitorCookieManager(Bridge bridge) {
|
||||
this(null, null, bridge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new cookie manager with specified cookie store and cookie policy.
|
||||
* @param store a {@code CookieStore} to be used by CookieManager. if {@code null}, cookie
|
||||
* manager will use a default one, which is an in-memory CookieStore implementation.
|
||||
* @param policy a {@code CookiePolicy} instance to be used by cookie manager as policy
|
||||
* callback. if {@code null}, ACCEPT_ORIGINAL_SERVER will be used.
|
||||
*/
|
||||
public CapacitorCookieManager(CookieStore store, CookiePolicy policy, Bridge bridge) {
|
||||
super(store, policy);
|
||||
webkitCookieManager = android.webkit.CookieManager.getInstance();
|
||||
this.localUrl = bridge.getLocalUrl();
|
||||
this.serverUrl = bridge.getServerUrl();
|
||||
}
|
||||
|
||||
public void removeSessionCookies() {
|
||||
this.webkitCookieManager.removeSessionCookies(null);
|
||||
}
|
||||
|
||||
public String getSanitizedDomain(String url) throws URISyntaxException {
|
||||
if (this.serverUrl != null && !this.serverUrl.isEmpty() && (url == null || url.isEmpty() || this.serverUrl.contains(url))) {
|
||||
url = this.serverUrl;
|
||||
} else if (this.localUrl != null && !this.localUrl.isEmpty() && (url == null || url.isEmpty() || this.localUrl.contains(url))) {
|
||||
url = this.localUrl;
|
||||
} else try {
|
||||
URI uri = new URI(url);
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme == null || scheme.isEmpty()) {
|
||||
url = "https://" + url;
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
Logger.error(TAG, "Failed to get scheme from URL.", e);
|
||||
}
|
||||
|
||||
try {
|
||||
new URI(url);
|
||||
} catch (Exception error) {
|
||||
Logger.error(TAG, "Failed to get sanitized URL.", error);
|
||||
throw error;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private String getDomainFromCookieString(String cookie) throws URISyntaxException {
|
||||
String[] domain = cookie.toLowerCase(Locale.ROOT).split("domain=");
|
||||
return getSanitizedDomain(domain.length <= 1 ? null : domain[1].split(";")[0].trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cookies for the given URL.
|
||||
* @param url the URL for which the cookies are requested
|
||||
* @return value the cookies as a string, using the format of the 'Cookie' HTTP request header
|
||||
*/
|
||||
public String getCookieString(String url) {
|
||||
try {
|
||||
url = getSanitizedDomain(url);
|
||||
Logger.info(TAG, "Getting cookies at: '" + url + "'");
|
||||
return webkitCookieManager.getCookie(url);
|
||||
} catch (Exception error) {
|
||||
Logger.error(TAG, "Failed to get cookies at the given URL.", error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a cookie value for the given URL and key.
|
||||
* @param url the URL for which the cookies are requested
|
||||
* @param key the key of the cookie to search for
|
||||
* @return the {@code HttpCookie} value of the cookie at the key,
|
||||
* otherwise it will return a new empty {@code HttpCookie}
|
||||
*/
|
||||
public HttpCookie getCookie(String url, String key) {
|
||||
HttpCookie[] cookies = getCookies(url);
|
||||
for (HttpCookie cookie : cookies) {
|
||||
if (cookie.getName().equals(key)) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of {@code HttpCookie} given a URL.
|
||||
* @param url the URL for which the cookies are requested
|
||||
* @return an {@code HttpCookie} array of non-expired cookies
|
||||
*/
|
||||
public HttpCookie[] getCookies(String url) {
|
||||
try {
|
||||
ArrayList<HttpCookie> cookieList = new ArrayList<>();
|
||||
String cookieString = getCookieString(url);
|
||||
if (cookieString != null) {
|
||||
String[] singleCookie = cookieString.split(";");
|
||||
for (String c : singleCookie) {
|
||||
HttpCookie parsed = HttpCookie.parse(c).get(0);
|
||||
parsed.setValue(parsed.getValue());
|
||||
cookieList.add(parsed);
|
||||
}
|
||||
}
|
||||
HttpCookie[] cookies = new HttpCookie[cookieList.size()];
|
||||
return cookieList.toArray(cookies);
|
||||
} catch (Exception ex) {
|
||||
return new HttpCookie[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a cookie for the given URL. Any existing cookie with the same host, path and name will
|
||||
* be replaced with the new cookie. The cookie being set will be ignored if it is expired.
|
||||
* @param url the URL for which the cookie is to be set
|
||||
* @param value the cookie as a string, using the format of the 'Set-Cookie' HTTP response header
|
||||
*/
|
||||
public void setCookie(String url, String value) {
|
||||
try {
|
||||
url = getSanitizedDomain(url);
|
||||
Logger.info(TAG, "Setting cookie '" + value + "' at: '" + url + "'");
|
||||
webkitCookieManager.setCookie(url, value);
|
||||
flush();
|
||||
} catch (Exception error) {
|
||||
Logger.error(TAG, "Failed to set cookie.", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a cookie for the given URL. Any existing cookie with the same host, path and name will
|
||||
* be replaced with the new cookie. The cookie being set will be ignored if it is expired.
|
||||
* @param url the URL for which the cookie is to be set
|
||||
* @param key the {@code HttpCookie} name to use for lookup
|
||||
* @param value the value of the {@code HttpCookie} given a key
|
||||
*/
|
||||
public void setCookie(String url, String key, String value) {
|
||||
String cookieValue = key + "=" + value;
|
||||
setCookie(url, cookieValue);
|
||||
}
|
||||
|
||||
public void setCookie(String url, String key, String value, String expires, String path) {
|
||||
String cookieValue = key + "=" + value + "; expires=" + expires + "; path=" + path;
|
||||
setCookie(url, cookieValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all cookies. This method is asynchronous.
|
||||
*/
|
||||
public void removeAllCookies() {
|
||||
webkitCookieManager.removeAllCookies(null);
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures all cookies currently accessible through the getCookie API are written to persistent
|
||||
* storage. This call will block the caller until it is done and may perform I/O.
|
||||
*/
|
||||
public void flush() {
|
||||
webkitCookieManager.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(URI uri, Map<String, List<String>> responseHeaders) {
|
||||
// make sure our args are valid
|
||||
if ((uri == null) || (responseHeaders == null)) return;
|
||||
|
||||
// go over the headers
|
||||
for (String headerKey : responseHeaders.keySet()) {
|
||||
// ignore headers which aren't cookie related
|
||||
if ((headerKey == null) || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie"))) continue;
|
||||
|
||||
// process each of the headers
|
||||
for (String headerValue : Objects.requireNonNull(responseHeaders.get(headerKey))) {
|
||||
try {
|
||||
// Set at the requested server url
|
||||
setCookie(uri.toString(), headerValue);
|
||||
|
||||
// Set at the defined domain in the response or at default capacitor hosted url
|
||||
setCookie(getDomainFromCookieString(headerValue), headerValue);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) {
|
||||
// make sure our args are valid
|
||||
if ((uri == null) || (requestHeaders == null)) throw new IllegalArgumentException("Argument is null");
|
||||
|
||||
// save our url once
|
||||
String url = uri.toString();
|
||||
|
||||
// prepare our response
|
||||
Map<String, List<String>> res = new HashMap<>();
|
||||
|
||||
// get the cookie
|
||||
String cookie = getCookieString(url);
|
||||
|
||||
// return it
|
||||
if (cookie != null) res.put("Cookie", Collections.singletonList(cookie));
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CookieStore getCookieStore() {
|
||||
// we don't want anyone to work with this cookie store directly
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
122
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java
generated
vendored
Normal file
122
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package com.getcapacitor.plugin;
|
||||
|
||||
import android.webkit.JavascriptInterface;
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import com.getcapacitor.PluginConfig;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.CookieHandler;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@CapacitorPlugin
|
||||
public class CapacitorCookies extends Plugin {
|
||||
|
||||
CapacitorCookieManager cookieManager;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.bridge.getWebView().addJavascriptInterface(this, "CapacitorCookiesAndroidInterface");
|
||||
this.cookieManager = new CapacitorCookieManager(null, java.net.CookiePolicy.ACCEPT_ALL, this.bridge);
|
||||
this.cookieManager.removeSessionCookies();
|
||||
CookieHandler.setDefault(this.cookieManager);
|
||||
super.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleOnDestroy() {
|
||||
super.handleOnDestroy();
|
||||
this.cookieManager.removeSessionCookies();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean isEnabled() {
|
||||
PluginConfig pluginConfig = getBridge().getConfig().getPluginConfiguration("CapacitorCookies");
|
||||
return pluginConfig.getBoolean("enabled", false);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setCookie(String domain, String action) {
|
||||
cookieManager.setCookie(domain, action);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void getCookies(PluginCall call) {
|
||||
this.bridge.eval(
|
||||
"document.cookie",
|
||||
value -> {
|
||||
String cookies = value.substring(1, value.length() - 1);
|
||||
String[] cookieArray = cookies.split(";");
|
||||
|
||||
JSObject cookieMap = new JSObject();
|
||||
|
||||
for (String cookie : cookieArray) {
|
||||
if (cookie.length() > 0) {
|
||||
String[] keyValue = cookie.split("=", 2);
|
||||
|
||||
if (keyValue.length == 2) {
|
||||
String key = keyValue[0].trim();
|
||||
String val = keyValue[1].trim();
|
||||
try {
|
||||
key = URLDecoder.decode(keyValue[0].trim(), StandardCharsets.UTF_8.name());
|
||||
val = URLDecoder.decode(keyValue[1].trim(), StandardCharsets.UTF_8.name());
|
||||
} catch (UnsupportedEncodingException ignored) {}
|
||||
|
||||
cookieMap.put(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call.resolve(cookieMap);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void setCookie(PluginCall call) {
|
||||
String key = call.getString("key");
|
||||
if (null == key) {
|
||||
call.reject("Must provide key");
|
||||
}
|
||||
String value = call.getString("value");
|
||||
if (null == value) {
|
||||
call.reject("Must provide value");
|
||||
}
|
||||
String url = call.getString("url");
|
||||
String expires = call.getString("expires", "");
|
||||
String path = call.getString("path", "/");
|
||||
cookieManager.setCookie(url, key, value, expires, path);
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void deleteCookie(PluginCall call) {
|
||||
String key = call.getString("key");
|
||||
if (null == key) {
|
||||
call.reject("Must provide key");
|
||||
}
|
||||
String url = call.getString("url");
|
||||
cookieManager.setCookie(url, key + "=; Expires=Wed, 31 Dec 2000 23:59:59 GMT");
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void clearCookies(PluginCall call) {
|
||||
String url = call.getString("url");
|
||||
HttpCookie[] cookies = cookieManager.getCookies(url);
|
||||
for (HttpCookie cookie : cookies) {
|
||||
cookieManager.setCookie(url, cookie.getName() + "=; Expires=Wed, 31 Dec 2000 23:59:59 GMT");
|
||||
}
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void clearAllCookies(PluginCall call) {
|
||||
cookieManager.removeAllCookies();
|
||||
call.resolve();
|
||||
}
|
||||
}
|
||||
119
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java
generated
vendored
Normal file
119
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package com.getcapacitor.plugin;
|
||||
|
||||
import android.Manifest;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import com.getcapacitor.PluginConfig;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
import com.getcapacitor.annotation.Permission;
|
||||
import com.getcapacitor.plugin.util.CapacitorHttpUrlConnection;
|
||||
import com.getcapacitor.plugin.util.HttpRequestHandler;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@CapacitorPlugin(
|
||||
permissions = {
|
||||
@Permission(strings = { Manifest.permission.WRITE_EXTERNAL_STORAGE }, alias = "HttpWrite"),
|
||||
@Permission(strings = { Manifest.permission.READ_EXTERNAL_STORAGE }, alias = "HttpRead")
|
||||
}
|
||||
)
|
||||
public class CapacitorHttp extends Plugin {
|
||||
|
||||
private final Map<Runnable, PluginCall> activeRequests = new ConcurrentHashMap<>();
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.bridge.getWebView().addJavascriptInterface(this, "CapacitorHttpAndroidInterface");
|
||||
super.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleOnDestroy() {
|
||||
super.handleOnDestroy();
|
||||
|
||||
for (Map.Entry<Runnable, PluginCall> entry : activeRequests.entrySet()) {
|
||||
Runnable job = entry.getKey();
|
||||
PluginCall call = entry.getValue();
|
||||
|
||||
if (call.getData().has("activeCapacitorHttpUrlConnection")) {
|
||||
try {
|
||||
CapacitorHttpUrlConnection connection = (CapacitorHttpUrlConnection) call
|
||||
.getData()
|
||||
.get("activeCapacitorHttpUrlConnection");
|
||||
connection.disconnect();
|
||||
call.getData().remove("activeCapacitorHttpUrlConnection");
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
getBridge().releaseCall(call);
|
||||
}
|
||||
|
||||
activeRequests.clear();
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
private void http(final PluginCall call, final String httpMethod) {
|
||||
Runnable asyncHttpCall = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
JSObject response = HttpRequestHandler.request(call, httpMethod, getBridge());
|
||||
call.resolve(response);
|
||||
} catch (Exception e) {
|
||||
call.reject(e.getLocalizedMessage(), e.getClass().getSimpleName(), e);
|
||||
} finally {
|
||||
activeRequests.remove(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!executor.isShutdown()) {
|
||||
activeRequests.put(asyncHttpCall, call);
|
||||
executor.submit(asyncHttpCall);
|
||||
} else {
|
||||
call.reject("Failed to execute request - Http Plugin was shutdown");
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean isEnabled() {
|
||||
PluginConfig pluginConfig = getBridge().getConfig().getPluginConfiguration("CapacitorHttp");
|
||||
return pluginConfig.getBoolean("enabled", false);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void request(final PluginCall call) {
|
||||
this.http(call, null);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void get(final PluginCall call) {
|
||||
this.http(call, "GET");
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void post(final PluginCall call) {
|
||||
this.http(call, "POST");
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void put(final PluginCall call) {
|
||||
this.http(call, "PUT");
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void patch(final PluginCall call) {
|
||||
this.http(call, "PATCH");
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void delete(final PluginCall call) {
|
||||
this.http(call, "DELETE");
|
||||
}
|
||||
}
|
||||
48
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/WebView.java
generated
vendored
Normal file
48
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/WebView.java
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.getcapacitor.plugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
|
||||
@CapacitorPlugin
|
||||
public class WebView extends Plugin {
|
||||
|
||||
public static final String WEBVIEW_PREFS_NAME = "CapWebViewSettings";
|
||||
public static final String CAP_SERVER_PATH = "serverBasePath";
|
||||
|
||||
@PluginMethod
|
||||
public void setServerAssetPath(PluginCall call) {
|
||||
String path = call.getString("path");
|
||||
bridge.setServerAssetPath(path);
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void setServerBasePath(PluginCall call) {
|
||||
String path = call.getString("path");
|
||||
bridge.setServerBasePath(path);
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void getServerBasePath(PluginCall call) {
|
||||
String path = bridge.getServerBasePath();
|
||||
JSObject ret = new JSObject();
|
||||
ret.put("path", path);
|
||||
call.resolve(ret);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void persistServerBasePath(PluginCall call) {
|
||||
String path = bridge.getServerBasePath();
|
||||
SharedPreferences prefs = getContext().getSharedPreferences(WEBVIEW_PREFS_NAME, Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putString(CAP_SERVER_PATH, path);
|
||||
editor.apply();
|
||||
call.resolve();
|
||||
}
|
||||
}
|
||||
358
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/AssetUtil.java
generated
vendored
Normal file
358
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/AssetUtil.java
generated
vendored
Normal file
@@ -0,0 +1,358 @@
|
||||
package com.getcapacitor.plugin.util;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.StrictMode;
|
||||
import androidx.core.content.FileProvider;
|
||||
import com.getcapacitor.Logger;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Manager for assets.
|
||||
*/
|
||||
public final class AssetUtil {
|
||||
|
||||
public static final int RESOURCE_ID_ZERO_VALUE = 0;
|
||||
// Name of the storage folder
|
||||
private static final String STORAGE_FOLDER = "/capacitorassets";
|
||||
|
||||
// Ref to the context passed through the constructor to access the
|
||||
// resources and app directory.
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context Application context.
|
||||
*/
|
||||
private AssetUtil(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to retrieve class instance.
|
||||
*
|
||||
* @param context Application context.
|
||||
*/
|
||||
public static AssetUtil getInstance(Context context) {
|
||||
return new AssetUtil(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* The URI for a path.
|
||||
*
|
||||
* @param path The given path.
|
||||
*/
|
||||
public Uri parse(String path) {
|
||||
if (path == null || path.isEmpty()) {
|
||||
return Uri.EMPTY;
|
||||
} else if (path.startsWith("res:")) {
|
||||
return getUriForResourcePath(path);
|
||||
} else if (path.startsWith("file:///")) {
|
||||
return getUriFromPath(path);
|
||||
} else if (path.startsWith("file://")) {
|
||||
return getUriFromAsset(path);
|
||||
} else if (path.startsWith("http")) {
|
||||
return getUriFromRemote(path);
|
||||
} else if (path.startsWith("content://")) {
|
||||
return Uri.parse(path);
|
||||
}
|
||||
|
||||
return Uri.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* URI for a file.
|
||||
*
|
||||
* @param path Absolute path like file:///...
|
||||
*
|
||||
* @return URI pointing to the given path.
|
||||
*/
|
||||
private Uri getUriFromPath(String path) {
|
||||
String absPath = path.replaceFirst("file://", "").replaceFirst("\\?.*$", "");
|
||||
File file = new File(absPath);
|
||||
|
||||
if (!file.exists()) {
|
||||
Logger.error("File not found: " + file.getAbsolutePath());
|
||||
return Uri.EMPTY;
|
||||
}
|
||||
|
||||
return getUriFromFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* URI for an asset.
|
||||
*
|
||||
* @param path Asset path like file://...
|
||||
*
|
||||
* @return URI pointing to the given path.
|
||||
*/
|
||||
private Uri getUriFromAsset(String path) {
|
||||
String resPath = path.replaceFirst("file:/", "www").replaceFirst("\\?.*$", "");
|
||||
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
|
||||
File file = getTmpFile(fileName);
|
||||
|
||||
if (file == null) return Uri.EMPTY;
|
||||
|
||||
try {
|
||||
AssetManager assets = context.getAssets();
|
||||
InputStream in = assets.open(resPath);
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
copyFile(in, out);
|
||||
} catch (Exception e) {
|
||||
Logger.error("File not found: assets/" + resPath);
|
||||
return Uri.EMPTY;
|
||||
}
|
||||
|
||||
return getUriFromFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* The URI for a resource.
|
||||
*
|
||||
* @param path The given relative path.
|
||||
*
|
||||
* @return URI pointing to the given path.
|
||||
*/
|
||||
private Uri getUriForResourcePath(String path) {
|
||||
Resources res = context.getResources();
|
||||
String resPath = path.replaceFirst("res://", "");
|
||||
int resId = getResId(resPath);
|
||||
|
||||
if (resId == 0) {
|
||||
Logger.error("File not found: " + resPath);
|
||||
return Uri.EMPTY;
|
||||
}
|
||||
|
||||
return new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(res.getResourcePackageName(resId))
|
||||
.appendPath(res.getResourceTypeName(resId))
|
||||
.appendPath(res.getResourceEntryName(resId))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uri from remote located content.
|
||||
*
|
||||
* @param path Remote address.
|
||||
*
|
||||
* @return Uri of the downloaded file.
|
||||
*/
|
||||
private Uri getUriFromRemote(String path) {
|
||||
File file = getTmpFile();
|
||||
|
||||
if (file == null) return Uri.EMPTY;
|
||||
|
||||
try {
|
||||
URL url = new URL(path);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
|
||||
|
||||
StrictMode.setThreadPolicy(policy);
|
||||
|
||||
connection.setRequestProperty("Connection", "close");
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.connect();
|
||||
|
||||
InputStream in = connection.getInputStream();
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
|
||||
copyFile(in, out);
|
||||
return getUriFromFile(file);
|
||||
} catch (MalformedURLException e) {
|
||||
Logger.error(Logger.tags("Asset"), "Incorrect URL", e);
|
||||
} catch (FileNotFoundException e) {
|
||||
Logger.error(Logger.tags("Asset"), "Failed to create new File from HTTP Content", e);
|
||||
} catch (IOException e) {
|
||||
Logger.error(Logger.tags("Asset"), "No Input can be created from http Stream", e);
|
||||
}
|
||||
|
||||
return Uri.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy content from input stream into output stream.
|
||||
*
|
||||
* @param in The input stream.
|
||||
* @param out The output stream.
|
||||
*/
|
||||
private void copyFile(InputStream in, FileOutputStream out) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
|
||||
try {
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
Logger.error("Error copying", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resource ID for drawable.
|
||||
*
|
||||
* @param resPath Resource path as string.
|
||||
*
|
||||
* @return The resource ID or 0 if not found.
|
||||
*/
|
||||
public int getResId(String resPath) {
|
||||
int resId = getResId(context.getResources(), resPath);
|
||||
|
||||
if (resId == 0) {
|
||||
resId = getResId(Resources.getSystem(), resPath);
|
||||
}
|
||||
|
||||
return resId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resource ID.
|
||||
*
|
||||
* @param res The resources where to look for.
|
||||
* @param resPath The name of the resource.
|
||||
*
|
||||
* @return The resource ID or 0 if not found.
|
||||
*/
|
||||
private int getResId(Resources res, String resPath) {
|
||||
String pkgName = getPkgName(res);
|
||||
String resName = getBaseName(resPath);
|
||||
int resId;
|
||||
|
||||
resId = res.getIdentifier(resName, "mipmap", pkgName);
|
||||
|
||||
if (resId == 0) {
|
||||
resId = res.getIdentifier(resName, "drawable", pkgName);
|
||||
}
|
||||
|
||||
if (resId == 0) {
|
||||
resId = res.getIdentifier(resName, "raw", pkgName);
|
||||
}
|
||||
|
||||
return resId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert URI to Bitmap.
|
||||
*
|
||||
* @param uri Internal image URI
|
||||
*/
|
||||
public Bitmap getIconFromUri(Uri uri) throws IOException {
|
||||
InputStream input = context.getContentResolver().openInputStream(uri);
|
||||
return BitmapFactory.decodeStream(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract name of drawable resource from path.
|
||||
*
|
||||
* @param resPath Resource path as string.
|
||||
*/
|
||||
private String getBaseName(String resPath) {
|
||||
String drawable = resPath;
|
||||
|
||||
if (drawable.contains("/")) {
|
||||
drawable = drawable.substring(drawable.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
if (resPath.contains(".")) {
|
||||
drawable = drawable.substring(0, drawable.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file located under the external cache dir of that app.
|
||||
*
|
||||
* @return File with a random UUID name.
|
||||
*/
|
||||
private File getTmpFile() {
|
||||
return getTmpFile(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file located under the external cache dir of that app.
|
||||
*
|
||||
* @param name The name of the file.
|
||||
*
|
||||
* @return File with the provided name.
|
||||
*/
|
||||
private File getTmpFile(String name) {
|
||||
File dir = context.getExternalCacheDir();
|
||||
|
||||
if (dir == null) {
|
||||
dir = context.getCacheDir();
|
||||
}
|
||||
|
||||
if (dir == null) {
|
||||
Logger.error(Logger.tags("Asset"), "Missing cache dir", null);
|
||||
return null;
|
||||
}
|
||||
|
||||
String storage = dir.toString() + STORAGE_FOLDER;
|
||||
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
new File(storage).mkdir();
|
||||
|
||||
return new File(storage, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content URI for the specified file.
|
||||
*
|
||||
* @param file The file to get the URI.
|
||||
*
|
||||
* @return content://...
|
||||
*/
|
||||
private Uri getUriFromFile(File file) {
|
||||
try {
|
||||
String authority = context.getPackageName() + ".provider";
|
||||
return FileProvider.getUriForFile(context, authority, file);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logger.error("File not supported by provider", e);
|
||||
return Uri.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Package name specified by the resource bundle.
|
||||
*/
|
||||
private String getPkgName(Resources res) {
|
||||
return res == Resources.getSystem() ? "android" : context.getPackageName();
|
||||
}
|
||||
|
||||
public static int getResourceID(Context context, String resourceName, String dir) {
|
||||
return context.getResources().getIdentifier(resourceName, dir, context.getPackageName());
|
||||
}
|
||||
|
||||
public static String getResourceBaseName(String resPath) {
|
||||
if (resPath == null) return null;
|
||||
|
||||
if (resPath.contains("/")) {
|
||||
return resPath.substring(resPath.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
if (resPath.contains(".")) {
|
||||
return resPath.substring(0, resPath.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
return resPath;
|
||||
}
|
||||
}
|
||||
478
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java
generated
vendored
Normal file
478
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java
generated
vendored
Normal file
@@ -0,0 +1,478 @@
|
||||
package com.getcapacitor.plugin.util;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.text.TextUtils;
|
||||
import com.getcapacitor.Bridge;
|
||||
import com.getcapacitor.JSArray;
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.JSValue;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.UnknownServiceException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class CapacitorHttpUrlConnection implements ICapacitorHttpUrlConnection {
|
||||
|
||||
private final HttpURLConnection connection;
|
||||
|
||||
/**
|
||||
* Make a new CapacitorHttpUrlConnection instance, which wraps around HttpUrlConnection
|
||||
* and provides some helper functions for setting request headers and the request body
|
||||
* @param conn the base HttpUrlConnection. You can pass the value from
|
||||
* {@code (HttpUrlConnection) URL.openConnection()}
|
||||
*/
|
||||
public CapacitorHttpUrlConnection(HttpURLConnection conn) {
|
||||
connection = conn;
|
||||
this.setDefaultRequestProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying HttpUrlConnection value
|
||||
* @return the underlying HttpUrlConnection value
|
||||
*/
|
||||
public HttpURLConnection getHttpConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the {@code allowUserInteraction} field of
|
||||
* this {@code URLConnection}.
|
||||
*
|
||||
* @param isAllowedInteraction the new value.
|
||||
* @throws IllegalStateException if already connected
|
||||
*/
|
||||
public void setAllowUserInteraction(boolean isAllowedInteraction) {
|
||||
connection.setAllowUserInteraction(isAllowedInteraction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the method for the URL request, one of:
|
||||
* <UL>
|
||||
* <LI>GET
|
||||
* <LI>POST
|
||||
* <LI>HEAD
|
||||
* <LI>OPTIONS
|
||||
* <LI>PUT
|
||||
* <LI>DELETE
|
||||
* <LI>TRACE
|
||||
* </UL> are legal, subject to protocol restrictions. The default
|
||||
* method is GET.
|
||||
*
|
||||
* @param method the HTTP method
|
||||
* @exception ProtocolException if the method cannot be reset or if
|
||||
* the requested method isn't valid for HTTP.
|
||||
* @exception SecurityException if a security manager is set and the
|
||||
* method is "TRACE", but the "allowHttpTrace"
|
||||
* NetPermission is not granted.
|
||||
*/
|
||||
public void setRequestMethod(String method) throws ProtocolException {
|
||||
connection.setRequestMethod(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a specified timeout value, in milliseconds, to be used
|
||||
* when opening a communications link to the resource referenced
|
||||
* by this URLConnection. If the timeout expires before the
|
||||
* connection can be established, a
|
||||
* java.net.SocketTimeoutException is raised. A timeout of zero is
|
||||
* interpreted as an infinite timeout.
|
||||
*
|
||||
* <p><strong>Warning</strong>: If the hostname resolves to multiple IP
|
||||
* addresses, Android's default implementation of {@link HttpURLConnection}
|
||||
* will try each in
|
||||
* <a href="http://www.ietf.org/rfc/rfc3484.txt">RFC 3484</a> order. If
|
||||
* connecting to each of these addresses fails, multiple timeouts will
|
||||
* elapse before the connect attempt throws an exception. Host names
|
||||
* that support both IPv6 and IPv4 always have at least 2 IP addresses.
|
||||
*
|
||||
* @param timeout an {@code int} that specifies the connect
|
||||
* timeout value in milliseconds
|
||||
* @throws IllegalArgumentException if the timeout parameter is negative
|
||||
*/
|
||||
public void setConnectTimeout(int timeout) {
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("timeout can not be negative");
|
||||
}
|
||||
connection.setConnectTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the read timeout to a specified timeout, in
|
||||
* milliseconds. A non-zero value specifies the timeout when
|
||||
* reading from Input stream when a connection is established to a
|
||||
* resource. If the timeout expires before there is data available
|
||||
* for read, a java.net.SocketTimeoutException is raised. A
|
||||
* timeout of zero is interpreted as an infinite timeout.
|
||||
*
|
||||
* @param timeout an {@code int} that specifies the timeout
|
||||
* value to be used in milliseconds
|
||||
* @throws IllegalArgumentException if the timeout parameter is negative
|
||||
*/
|
||||
public void setReadTimeout(int timeout) {
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("timeout can not be negative");
|
||||
}
|
||||
connection.setReadTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether automatic HTTP redirects should be disabled
|
||||
* @param disableRedirects the flag to determine if redirects should be followed
|
||||
*/
|
||||
public void setDisableRedirects(boolean disableRedirects) {
|
||||
connection.setInstanceFollowRedirects(!disableRedirects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request headers given a JSObject of key-value pairs
|
||||
* @param headers the JSObject values to map to the HttpUrlConnection request headers
|
||||
*/
|
||||
public void setRequestHeaders(JSObject headers) {
|
||||
Iterator<String> keys = headers.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
String value = headers.getString(key);
|
||||
connection.setRequestProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the {@code doOutput} field for this
|
||||
* {@code URLConnection} to the specified value.
|
||||
* <p>
|
||||
* A URL connection can be used for input and/or output. Set the DoOutput
|
||||
* flag to true if you intend to use the URL connection for output,
|
||||
* false if not. The default is false.
|
||||
*
|
||||
* @param shouldDoOutput the new value.
|
||||
* @throws IllegalStateException if already connected
|
||||
*/
|
||||
public void setDoOutput(boolean shouldDoOutput) {
|
||||
connection.setDoOutput(shouldDoOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param call
|
||||
* @throws JSONException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void setRequestBody(PluginCall call, JSValue body) throws JSONException, IOException {
|
||||
setRequestBody(call, body, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param call
|
||||
* @throws JSONException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void setRequestBody(PluginCall call, JSValue body, String bodyType) throws JSONException, IOException {
|
||||
String contentType = connection.getRequestProperty("Content-Type");
|
||||
String dataString = "";
|
||||
|
||||
if (contentType == null || contentType.isEmpty()) return;
|
||||
|
||||
if (contentType.contains("application/json")) {
|
||||
JSArray jsArray = null;
|
||||
if (body != null) {
|
||||
dataString = body.toString();
|
||||
} else {
|
||||
jsArray = call.getArray("data", null);
|
||||
}
|
||||
if (jsArray != null) {
|
||||
dataString = jsArray.toString();
|
||||
} else if (body == null) {
|
||||
dataString = call.getString("data");
|
||||
}
|
||||
this.writeRequestBody(dataString != null ? dataString : "");
|
||||
} else if (bodyType != null && bodyType.equals("file")) {
|
||||
try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
os.write(Base64.getDecoder().decode(body.toString()));
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
} else if (contentType.contains("application/x-www-form-urlencoded")) {
|
||||
try {
|
||||
JSObject obj = body.toJSObject();
|
||||
this.writeObjectRequestBody(obj);
|
||||
} catch (Exception e) {
|
||||
// Body is not a valid JSON, treat it as an already formatted string
|
||||
this.writeRequestBody(body.toString());
|
||||
}
|
||||
} else if (bodyType != null && bodyType.equals("formData")) {
|
||||
this.writeFormDataRequestBody(contentType, body.toJSArray());
|
||||
} else {
|
||||
this.writeRequestBody(body.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the provided string to the HTTP connection managed by this instance.
|
||||
*
|
||||
* @param body The string value to write to the connection stream.
|
||||
*/
|
||||
private void writeRequestBody(String body) throws IOException {
|
||||
try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
|
||||
os.write(body.getBytes(StandardCharsets.UTF_8));
|
||||
os.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeObjectRequestBody(JSObject object) throws IOException, JSONException {
|
||||
try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
|
||||
Iterator<String> keys = object.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Object d = object.get(key);
|
||||
os.writeBytes(key);
|
||||
os.writeBytes("=");
|
||||
os.writeBytes(URLEncoder.encode(d.toString(), "UTF-8"));
|
||||
|
||||
if (keys.hasNext()) {
|
||||
os.writeBytes("&");
|
||||
}
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFormDataRequestBody(String contentType, JSArray entries) throws IOException, JSONException {
|
||||
try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
|
||||
String boundary = contentType.split(";")[1].split("=")[1];
|
||||
String lineEnd = "\r\n";
|
||||
String twoHyphens = "--";
|
||||
|
||||
for (Object e : entries.toList()) {
|
||||
if (e instanceof JSONObject) {
|
||||
JSONObject entry = (JSONObject) e;
|
||||
String type = entry.getString("type");
|
||||
String key = entry.getString("key");
|
||||
String value = entry.getString("value");
|
||||
if (type.equals("string")) {
|
||||
os.writeBytes(twoHyphens + boundary + lineEnd);
|
||||
os.writeBytes("Content-Disposition: form-data; name=\"" + key + "\"" + lineEnd + lineEnd);
|
||||
os.write(value.getBytes(StandardCharsets.UTF_8));
|
||||
os.writeBytes(lineEnd);
|
||||
} else if (type.equals("base64File")) {
|
||||
String fileName = entry.getString("fileName");
|
||||
String fileContentType = entry.getString("contentType");
|
||||
|
||||
os.writeBytes(twoHyphens + boundary + lineEnd);
|
||||
os.writeBytes("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + fileName + "\"" + lineEnd);
|
||||
os.writeBytes("Content-Type: " + fileContentType + lineEnd);
|
||||
os.writeBytes("Content-Transfer-Encoding: binary" + lineEnd);
|
||||
os.writeBytes(lineEnd);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
os.write(Base64.getDecoder().decode(value));
|
||||
} else {
|
||||
os.write(android.util.Base64.decode(value, android.util.Base64.DEFAULT));
|
||||
}
|
||||
|
||||
os.writeBytes(lineEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
|
||||
os.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a communications link to the resource referenced by this
|
||||
* URL, if such a connection has not already been established.
|
||||
* <p>
|
||||
* If the {@code connect} method is called when the connection
|
||||
* has already been opened (indicated by the {@code connected}
|
||||
* field having the value {@code true}), the call is ignored.
|
||||
* <p>
|
||||
* URLConnection objects go through two phases: first they are
|
||||
* created, then they are connected. After being created, and
|
||||
* before being connected, various options can be specified
|
||||
* (e.g., doInput and UseCaches). After connecting, it is an
|
||||
* error to try to set them. Operations that depend on being
|
||||
* connected, like getContentLength, will implicitly perform the
|
||||
* connection, if necessary.
|
||||
*
|
||||
* @throws SocketTimeoutException if the timeout expires before
|
||||
* the connection can be established
|
||||
* @exception IOException if an I/O error occurs while opening the
|
||||
* connection.
|
||||
*/
|
||||
public void connect() throws IOException {
|
||||
connection.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status code from an HTTP response message.
|
||||
* For example, in the case of the following status lines:
|
||||
* <PRE>
|
||||
* HTTP/1.0 200 OK
|
||||
* HTTP/1.0 401 Unauthorized
|
||||
* </PRE>
|
||||
* It will return 200 and 401 respectively.
|
||||
* Returns -1 if no code can be discerned
|
||||
* from the response (i.e., the response is not valid HTTP).
|
||||
* @throws IOException if an error occurred connecting to the server.
|
||||
* @return the HTTP Status-Code, or -1
|
||||
*/
|
||||
public int getResponseCode() throws IOException {
|
||||
return connection.getResponseCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this {@code URLConnection}'s {@code URL}
|
||||
* field.
|
||||
*
|
||||
* @return the value of this {@code URLConnection}'s {@code URL}
|
||||
* field.
|
||||
*/
|
||||
public URL getURL() {
|
||||
return connection.getURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error stream if the connection failed
|
||||
* but the server sent useful data nonetheless. The
|
||||
* typical example is when an HTTP server responds
|
||||
* with a 404, which will cause a FileNotFoundException
|
||||
* to be thrown in connect, but the server sent an HTML
|
||||
* help page with suggestions as to what to do.
|
||||
*
|
||||
* <p>This method will not cause a connection to be initiated. If
|
||||
* the connection was not connected, or if the server did not have
|
||||
* an error while connecting or if the server had an error but
|
||||
* no error data was sent, this method will return null. This is
|
||||
* the default.
|
||||
*
|
||||
* @return an error stream if any, null if there have been no
|
||||
* errors, the connection is not connected or the server sent no
|
||||
* useful data.
|
||||
*/
|
||||
@Override
|
||||
public InputStream getErrorStream() {
|
||||
return connection.getErrorStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the named header field.
|
||||
* <p>
|
||||
* If called on a connection that sets the same header multiple times
|
||||
* with possibly different values, only the last value is returned.
|
||||
*
|
||||
*
|
||||
* @param name the name of a header field.
|
||||
* @return the value of the named header field, or {@code null}
|
||||
* if there is no such field in the header.
|
||||
*/
|
||||
@Override
|
||||
public String getHeaderField(String name) {
|
||||
return connection.getHeaderField(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream that reads from this open connection.
|
||||
*
|
||||
* A SocketTimeoutException can be thrown when reading from the
|
||||
* returned input stream if the read timeout expires before data
|
||||
* is available for read.
|
||||
*
|
||||
* @return an input stream that reads from this open connection.
|
||||
* @exception IOException if an I/O error occurs while
|
||||
* creating the input stream.
|
||||
* @exception UnknownServiceException if the protocol does not support
|
||||
* input.
|
||||
* @see #setReadTimeout(int)
|
||||
*/
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return connection.getInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable Map of the header fields.
|
||||
* The Map keys are Strings that represent the
|
||||
* response-header field names. Each Map value is an
|
||||
* unmodifiable List of Strings that represents
|
||||
* the corresponding field values.
|
||||
*
|
||||
* @return a Map of header fields
|
||||
*/
|
||||
public Map<String, List<String>> getHeaderFields() {
|
||||
return connection.getHeaderFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default request properties on the newly created connection.
|
||||
* This is called as early as possible to allow overrides by user-provided values.
|
||||
*/
|
||||
private void setDefaultRequestProperties() {
|
||||
String acceptLanguage = buildDefaultAcceptLanguageProperty();
|
||||
if (!TextUtils.isEmpty(acceptLanguage)) {
|
||||
connection.setRequestProperty("Accept-Language", acceptLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a locale string describing the device's current locale preferences.
|
||||
*/
|
||||
private String buildDefaultAcceptLanguageProperty() {
|
||||
Locale locale;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
locale = LocaleList.getDefault().get(0);
|
||||
} else {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
String result = "";
|
||||
String lang = locale.getLanguage();
|
||||
String country = locale.getCountry();
|
||||
if (!TextUtils.isEmpty(lang)) {
|
||||
if (!TextUtils.isEmpty(country)) {
|
||||
result = String.format("%s-%s,%s;q=0.5", lang, country, lang);
|
||||
} else {
|
||||
result = String.format("%s;q=0.5", lang);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSSLSocketFactory(Bridge bridge) {
|
||||
// Attach SSL Certificates if Enterprise Plugin is available
|
||||
try {
|
||||
Class<?> sslPinningImpl = Class.forName("io.ionic.sslpinning.SSLPinning");
|
||||
Method method = sslPinningImpl.getDeclaredMethod("getSSLSocketFactory", Bridge.class);
|
||||
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) method.invoke(
|
||||
sslPinningImpl.getDeclaredConstructor().newInstance(),
|
||||
bridge
|
||||
);
|
||||
if (sslSocketFactory != null) {
|
||||
((HttpsURLConnection) this.connection).setSSLSocketFactory(sslSocketFactory);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
452
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java
generated
vendored
Normal file
452
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java
generated
vendored
Normal file
@@ -0,0 +1,452 @@
|
||||
package com.getcapacitor.plugin.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import com.getcapacitor.Bridge;
|
||||
import com.getcapacitor.JSArray;
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.JSValue;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class HttpRequestHandler {
|
||||
|
||||
/**
|
||||
* An enum specifying conventional HTTP Response Types
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
|
||||
*/
|
||||
public enum ResponseType {
|
||||
ARRAY_BUFFER("arraybuffer"),
|
||||
BLOB("blob"),
|
||||
DOCUMENT("document"),
|
||||
JSON("json"),
|
||||
TEXT("text");
|
||||
|
||||
private final String name;
|
||||
|
||||
ResponseType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
static final ResponseType DEFAULT = TEXT;
|
||||
|
||||
public static ResponseType parse(String value) {
|
||||
for (ResponseType responseType : values()) {
|
||||
if (responseType.name.equalsIgnoreCase(value)) {
|
||||
return responseType;
|
||||
}
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal builder class for building a CapacitorHttpUrlConnection
|
||||
*/
|
||||
public static class HttpURLConnectionBuilder {
|
||||
|
||||
public Integer connectTimeout;
|
||||
public Integer readTimeout;
|
||||
public Boolean disableRedirects;
|
||||
public JSObject headers;
|
||||
public String method;
|
||||
public URL url;
|
||||
|
||||
public CapacitorHttpUrlConnection connection;
|
||||
|
||||
public HttpURLConnectionBuilder setConnectTimeout(Integer connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder setReadTimeout(Integer readTimeout) {
|
||||
this.readTimeout = readTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder setDisableRedirects(Boolean disableRedirects) {
|
||||
this.disableRedirects = disableRedirects;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder setHeaders(JSObject headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder setMethod(String method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder setUrl(URL url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder openConnection() throws IOException {
|
||||
connection = new CapacitorHttpUrlConnection((HttpURLConnection) url.openConnection());
|
||||
|
||||
connection.setAllowUserInteraction(false);
|
||||
connection.setRequestMethod(method);
|
||||
|
||||
if (connectTimeout != null) connection.setConnectTimeout(connectTimeout);
|
||||
if (readTimeout != null) connection.setReadTimeout(readTimeout);
|
||||
if (disableRedirects != null) connection.setDisableRedirects(disableRedirects);
|
||||
|
||||
connection.setRequestHeaders(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder setUrlParams(JSObject params) throws MalformedURLException, URISyntaxException, JSONException {
|
||||
return this.setUrlParams(params, true);
|
||||
}
|
||||
|
||||
public HttpURLConnectionBuilder setUrlParams(JSObject params, boolean shouldEncode)
|
||||
throws URISyntaxException, MalformedURLException {
|
||||
String initialQuery = url.getQuery();
|
||||
String initialQueryBuilderStr = initialQuery == null ? "" : initialQuery;
|
||||
|
||||
Iterator<String> keys = params.keys();
|
||||
|
||||
if (!keys.hasNext()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
StringBuilder urlQueryBuilder = new StringBuilder(initialQueryBuilderStr);
|
||||
|
||||
// Build the new query string
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
|
||||
// Attempt as JSONArray and fallback to string if it fails
|
||||
try {
|
||||
StringBuilder value = new StringBuilder();
|
||||
JSONArray arr = params.getJSONArray(key);
|
||||
for (int x = 0; x < arr.length(); x++) {
|
||||
this.addUrlParam(value, key, arr.getString(x), shouldEncode);
|
||||
if (x != arr.length() - 1) {
|
||||
value.append("&");
|
||||
}
|
||||
}
|
||||
if (urlQueryBuilder.length() > 0) {
|
||||
urlQueryBuilder.append("&");
|
||||
}
|
||||
urlQueryBuilder.append(value);
|
||||
} catch (JSONException e) {
|
||||
if (urlQueryBuilder.length() > 0) {
|
||||
urlQueryBuilder.append("&");
|
||||
}
|
||||
this.addUrlParam(urlQueryBuilder, key, params.getString(key), shouldEncode);
|
||||
}
|
||||
}
|
||||
|
||||
String urlQuery = urlQueryBuilder.toString();
|
||||
|
||||
URI uri = url.toURI();
|
||||
String unEncodedUrlString =
|
||||
uri.getScheme() +
|
||||
"://" +
|
||||
uri.getAuthority() +
|
||||
uri.getPath() +
|
||||
((!urlQuery.equals("")) ? "?" + urlQuery : "") +
|
||||
((uri.getFragment() != null) ? uri.getFragment() : "");
|
||||
this.url = new URL(unEncodedUrlString);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void addUrlParam(StringBuilder sb, String key, String value, boolean shouldEncode) {
|
||||
if (shouldEncode) {
|
||||
try {
|
||||
key = URLEncoder.encode(key, "UTF-8");
|
||||
value = URLEncoder.encode(value, "UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new RuntimeException(ex.getCause());
|
||||
}
|
||||
}
|
||||
sb.append(key).append("=").append(value);
|
||||
}
|
||||
|
||||
public CapacitorHttpUrlConnection build() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects.
|
||||
* Defaults to ResponseType.DEFAULT
|
||||
* @param connection The CapacitorHttpUrlConnection to respond with
|
||||
* @throws IOException Thrown if the InputStream is unable to be parsed correctly
|
||||
* @throws JSONException Thrown if the JSON is unable to be parsed
|
||||
*/
|
||||
public static JSObject buildResponse(CapacitorHttpUrlConnection connection) throws IOException, JSONException {
|
||||
return buildResponse(connection, ResponseType.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects
|
||||
* @param connection The CapacitorHttpUrlConnection to respond with
|
||||
* @param responseType The requested ResponseType
|
||||
* @return A JSObject that contains the HTTPResponse to return to the browser
|
||||
* @throws IOException Thrown if the InputStream is unable to be parsed correctly
|
||||
* @throws JSONException Thrown if the JSON is unable to be parsed
|
||||
*/
|
||||
public static JSObject buildResponse(CapacitorHttpUrlConnection connection, ResponseType responseType)
|
||||
throws IOException, JSONException {
|
||||
int statusCode = connection.getResponseCode();
|
||||
|
||||
JSObject output = new JSObject();
|
||||
output.put("status", statusCode);
|
||||
output.put("headers", buildResponseHeaders(connection));
|
||||
output.put("url", connection.getURL());
|
||||
output.put("data", readData(connection, responseType));
|
||||
|
||||
InputStream errorStream = connection.getErrorStream();
|
||||
if (errorStream != null) {
|
||||
output.put("error", true);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the existing ICapacitorHttpUrlConnection data
|
||||
* @param connection The ICapacitorHttpUrlConnection object to read in
|
||||
* @param responseType The type of HTTP response to return to the API
|
||||
* @return The parsed data from the connection
|
||||
* @throws IOException Thrown if the InputStreams cannot be properly parsed
|
||||
* @throws JSONException Thrown if the JSON is malformed when parsing as JSON
|
||||
*/
|
||||
public static Object readData(ICapacitorHttpUrlConnection connection, ResponseType responseType) throws IOException, JSONException {
|
||||
InputStream errorStream = connection.getErrorStream();
|
||||
String contentType = connection.getHeaderField("Content-Type");
|
||||
|
||||
if (errorStream != null) {
|
||||
if (isOneOf(contentType, MimeType.APPLICATION_JSON, MimeType.APPLICATION_VND_API_JSON)) {
|
||||
return parseJSON(readStreamAsString(errorStream));
|
||||
} else {
|
||||
return readStreamAsString(errorStream);
|
||||
}
|
||||
} else if (contentType != null && contentType.contains(MimeType.APPLICATION_JSON.getValue())) {
|
||||
// backward compatibility
|
||||
return parseJSON(readStreamAsString(connection.getInputStream()));
|
||||
} else {
|
||||
InputStream stream = connection.getInputStream();
|
||||
switch (responseType) {
|
||||
case ARRAY_BUFFER:
|
||||
case BLOB:
|
||||
return readStreamAsBase64(stream);
|
||||
case JSON:
|
||||
return parseJSON(readStreamAsString(stream));
|
||||
case DOCUMENT:
|
||||
case TEXT:
|
||||
default:
|
||||
return readStreamAsString(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for determining if the Content-Type is a typeof an existing Mime-Type
|
||||
* @param contentType The Content-Type string to check for
|
||||
* @param mimeTypes The Mime-Type values to check against
|
||||
* @return
|
||||
*/
|
||||
public static boolean isOneOf(String contentType, MimeType... mimeTypes) {
|
||||
if (contentType != null) {
|
||||
for (MimeType mimeType : mimeTypes) {
|
||||
if (contentType.contains(mimeType.getValue())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the JSObject response headers based on the connection header map
|
||||
* @param connection The CapacitorHttpUrlConnection connection
|
||||
* @return A JSObject of the header values from the CapacitorHttpUrlConnection
|
||||
*/
|
||||
public static JSObject buildResponseHeaders(CapacitorHttpUrlConnection connection) {
|
||||
JSObject output = new JSObject();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
|
||||
String valuesString = TextUtils.join(", ", entry.getValue());
|
||||
output.put(entry.getKey(), valuesString);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSObject or a JSArray based on a string-ified input
|
||||
* @param input String-ified JSON that needs parsing
|
||||
* @return A JSObject or JSArray
|
||||
* @throws JSONException thrown if the JSON is malformed
|
||||
*/
|
||||
public static Object parseJSON(String input) throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
if ("null".equals(input.trim())) {
|
||||
return JSONObject.NULL;
|
||||
} else if ("true".equals(input.trim())) {
|
||||
return true;
|
||||
} else if ("false".equals(input.trim())) {
|
||||
return false;
|
||||
} else if (input.trim().length() <= 0) {
|
||||
return "";
|
||||
} else if (input.trim().matches("^\".*\"$")) {
|
||||
// a string enclosed in " " is a json value, return the string without the quotes
|
||||
return input.trim().substring(1, input.trim().length() - 1);
|
||||
} else if (input.trim().matches("^-?\\d+$")) {
|
||||
return Integer.parseInt(input.trim());
|
||||
} else if (input.trim().matches("^-?\\d+(\\.\\d+)?$")) {
|
||||
return Double.parseDouble(input.trim());
|
||||
} else {
|
||||
try {
|
||||
return new JSObject(input);
|
||||
} catch (JSONException e) {
|
||||
return new JSArray(input);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string based on a base64 InputStream
|
||||
* @param in The base64 InputStream to convert to a String
|
||||
* @return String value of InputStream
|
||||
* @throws IOException thrown if the InputStream is unable to be read as base64
|
||||
*/
|
||||
public static String readStreamAsBase64(InputStream in) throws IOException {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int readBytes;
|
||||
while ((readBytes = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, readBytes);
|
||||
}
|
||||
byte[] result = out.toByteArray();
|
||||
return Base64.encodeToString(result, 0, result.length, Base64.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string based on an InputStream
|
||||
* @param in The InputStream to convert to a String
|
||||
* @return String value of InputStream
|
||||
* @throws IOException thrown if the InputStream is unable to be read
|
||||
*/
|
||||
public static String readStreamAsString(InputStream in) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
builder.append(line);
|
||||
line = reader.readLine();
|
||||
if (line != null) {
|
||||
builder.append(System.getProperty("line.separator"));
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes an Http Request based on the PluginCall parameters
|
||||
* @param call The Capacitor PluginCall that contains the options need for an Http request
|
||||
* @param httpMethod The HTTP method that overrides the PluginCall HTTP method
|
||||
* @throws IOException throws an IO request when a connection can't be made
|
||||
* @throws URISyntaxException thrown when the URI is malformed
|
||||
* @throws JSONException thrown when the incoming JSON is malformed
|
||||
*/
|
||||
public static JSObject request(PluginCall call, String httpMethod, Bridge bridge)
|
||||
throws IOException, URISyntaxException, JSONException {
|
||||
String urlString = call.getString("url", "");
|
||||
JSObject headers = call.getObject("headers", new JSObject());
|
||||
JSObject params = call.getObject("params", new JSObject());
|
||||
Integer connectTimeout = call.getInt("connectTimeout");
|
||||
Integer readTimeout = call.getInt("readTimeout");
|
||||
Boolean disableRedirects = call.getBoolean("disableRedirects");
|
||||
Boolean shouldEncode = call.getBoolean("shouldEncodeUrlParams", true);
|
||||
ResponseType responseType = ResponseType.parse(call.getString("responseType"));
|
||||
String dataType = call.getString("dataType");
|
||||
|
||||
String method = httpMethod != null ? httpMethod.toUpperCase(Locale.ROOT) : call.getString("method", "GET").toUpperCase(Locale.ROOT);
|
||||
|
||||
boolean isHttpMutate = method.equals("DELETE") || method.equals("PATCH") || method.equals("POST") || method.equals("PUT");
|
||||
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnectionBuilder connectionBuilder = new HttpURLConnectionBuilder()
|
||||
.setUrl(url)
|
||||
.setMethod(method)
|
||||
.setHeaders(headers)
|
||||
.setUrlParams(params, shouldEncode)
|
||||
.setConnectTimeout(connectTimeout)
|
||||
.setReadTimeout(readTimeout)
|
||||
.setDisableRedirects(disableRedirects)
|
||||
.openConnection();
|
||||
|
||||
CapacitorHttpUrlConnection connection = connectionBuilder.build();
|
||||
|
||||
if (null != bridge && !isDomainExcludedFromSSL(bridge, url)) {
|
||||
connection.setSSLSocketFactory(bridge);
|
||||
}
|
||||
|
||||
// Set HTTP body on a non GET or HEAD request
|
||||
if (isHttpMutate) {
|
||||
JSValue data = new JSValue(call, "data");
|
||||
if (data.getValue() != null) {
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestBody(call, data, dataType);
|
||||
}
|
||||
}
|
||||
|
||||
call.getData().put("activeCapacitorHttpUrlConnection", connection);
|
||||
connection.connect();
|
||||
|
||||
JSObject response = buildResponse(connection, responseType);
|
||||
|
||||
connection.disconnect();
|
||||
call.getData().remove("activeCapacitorHttpUrlConnection");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static Boolean isDomainExcludedFromSSL(Bridge bridge, URL url) {
|
||||
try {
|
||||
Class<?> sslPinningImpl = Class.forName("io.ionic.sslpinning.SSLPinning");
|
||||
Method method = sslPinningImpl.getDeclaredMethod("isDomainExcluded", Bridge.class, URL.class);
|
||||
return (Boolean) method.invoke(sslPinningImpl.getDeclaredConstructor().newInstance(), bridge, url);
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ProgressEmitter {
|
||||
void emit(Integer bytes, Integer contentLength);
|
||||
}
|
||||
}
|
||||
15
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java
generated
vendored
Normal file
15
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.getcapacitor.plugin.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* This interface was extracted from {@link CapacitorHttpUrlConnection} to enable mocking that class.
|
||||
*/
|
||||
public interface ICapacitorHttpUrlConnection {
|
||||
InputStream getErrorStream();
|
||||
|
||||
String getHeaderField(String name);
|
||||
|
||||
InputStream getInputStream() throws IOException;
|
||||
}
|
||||
17
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java
generated
vendored
Normal file
17
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.getcapacitor.plugin.util;
|
||||
|
||||
enum MimeType {
|
||||
APPLICATION_JSON("application/json"),
|
||||
APPLICATION_VND_API_JSON("application/vnd.api+json"), // https://jsonapi.org
|
||||
TEXT_HTML("text/html");
|
||||
|
||||
private final String value;
|
||||
|
||||
MimeType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
123
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/HostMask.java
generated
vendored
Normal file
123
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/HostMask.java
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package com.getcapacitor.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public interface HostMask {
|
||||
boolean matches(String host);
|
||||
|
||||
class Parser {
|
||||
|
||||
private static HostMask NOTHING = new Nothing();
|
||||
|
||||
public static HostMask parse(String[] masks) {
|
||||
return masks == null ? NOTHING : HostMask.Any.parse(masks);
|
||||
}
|
||||
|
||||
public static HostMask parse(String mask) {
|
||||
return mask == null ? NOTHING : HostMask.Simple.parse(mask);
|
||||
}
|
||||
}
|
||||
|
||||
class Simple implements HostMask {
|
||||
|
||||
private final List<String> maskParts;
|
||||
|
||||
private Simple(List<String> maskParts) {
|
||||
if (maskParts == null) {
|
||||
throw new IllegalArgumentException("Mask parts can not be null");
|
||||
}
|
||||
this.maskParts = maskParts;
|
||||
}
|
||||
|
||||
static Simple parse(String mask) {
|
||||
List<String> parts = Util.splitAndReverse(mask);
|
||||
return new Simple(parts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String host) {
|
||||
if (host == null) {
|
||||
return false;
|
||||
}
|
||||
List<String> hostParts = Util.splitAndReverse(host);
|
||||
int hostSize = hostParts.size();
|
||||
int maskSize = maskParts.size();
|
||||
if (maskSize > 1 && hostSize != maskSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int minSize = Math.min(hostSize, maskSize);
|
||||
|
||||
for (int i = 0; i < minSize; i++) {
|
||||
String maskPart = maskParts.get(i);
|
||||
String hostPart = hostParts.get(i);
|
||||
if (!Util.matches(maskPart, hostPart)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Any implements HostMask {
|
||||
|
||||
private final List<? extends HostMask> masks;
|
||||
|
||||
Any(List<? extends HostMask> masks) {
|
||||
this.masks = masks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String host) {
|
||||
for (HostMask mask : masks) {
|
||||
if (mask.matches(host)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Any parse(String... rawMasks) {
|
||||
List<HostMask.Simple> masks = new ArrayList<>();
|
||||
for (String raw : rawMasks) {
|
||||
masks.add(HostMask.Simple.parse(raw));
|
||||
}
|
||||
return new Any(masks);
|
||||
}
|
||||
}
|
||||
|
||||
class Nothing implements HostMask {
|
||||
|
||||
@Override
|
||||
public boolean matches(String host) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class Util {
|
||||
|
||||
static boolean matches(String mask, String string) {
|
||||
if (mask == null) {
|
||||
return false;
|
||||
} else if ("*".equals(mask)) {
|
||||
return true;
|
||||
} else if (string == null) {
|
||||
return false;
|
||||
} else {
|
||||
return mask.toUpperCase().equals(string.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> splitAndReverse(String string) {
|
||||
if (string == null) {
|
||||
throw new IllegalArgumentException("Can not split null argument");
|
||||
}
|
||||
List<String> parts = Arrays.asList(string.split("\\."));
|
||||
Collections.reverse(parts);
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/InternalUtils.java
generated
vendored
Normal file
27
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/InternalUtils.java
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.getcapacitor.util;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
public class InternalUtils {
|
||||
|
||||
public static PackageInfo getPackageInfo(PackageManager pm, String packageName) throws PackageManager.NameNotFoundException {
|
||||
return InternalUtils.getPackageInfo(pm, packageName, 0);
|
||||
}
|
||||
|
||||
public static PackageInfo getPackageInfo(PackageManager pm, String packageName, long flags)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
return pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags));
|
||||
} else {
|
||||
return getPackageInfoLegacy(pm, packageName, (int) flags);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static PackageInfo getPackageInfoLegacy(PackageManager pm, String packageName, long flags)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
return pm.getPackageInfo(packageName, (int) flags);
|
||||
}
|
||||
}
|
||||
166
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.java
generated
vendored
Normal file
166
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/JSONUtils.java
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
package com.getcapacitor.util;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Helper methods for parsing JSON objects.
|
||||
*/
|
||||
public class JSONUtils {
|
||||
|
||||
/**
|
||||
* Get a string value from the given JSON object.
|
||||
*
|
||||
* @param jsonObject A JSON object to search
|
||||
* @param key A key to fetch from the JSON object
|
||||
* @param defaultValue A default value to return if the key cannot be found
|
||||
* @return The value at the given key in the JSON object, or the default value
|
||||
*/
|
||||
public static String getString(JSONObject jsonObject, String key, String defaultValue) {
|
||||
String k = getDeepestKey(key);
|
||||
try {
|
||||
JSONObject o = getDeepestObject(jsonObject, key);
|
||||
|
||||
String value = o.getString(k);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
} catch (JSONException ignore) {
|
||||
// value was not found
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value from the given JSON object.
|
||||
*
|
||||
* @param jsonObject A JSON object to search
|
||||
* @param key A key to fetch from the JSON object
|
||||
* @param defaultValue A default value to return if the key cannot be found
|
||||
* @return The value at the given key in the JSON object, or the default value
|
||||
*/
|
||||
public static boolean getBoolean(JSONObject jsonObject, String key, boolean defaultValue) {
|
||||
String k = getDeepestKey(key);
|
||||
try {
|
||||
JSONObject o = getDeepestObject(jsonObject, key);
|
||||
|
||||
return o.getBoolean(k);
|
||||
} catch (JSONException ignore) {
|
||||
// value was not found
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an int value from the given JSON object.
|
||||
*
|
||||
* @param jsonObject A JSON object to search
|
||||
* @param key A key to fetch from the JSON object
|
||||
* @param defaultValue A default value to return if the key cannot be found
|
||||
* @return The value at the given key in the JSON object, or the default value
|
||||
*/
|
||||
public static int getInt(JSONObject jsonObject, String key, int defaultValue) {
|
||||
String k = getDeepestKey(key);
|
||||
try {
|
||||
JSONObject o = getDeepestObject(jsonObject, key);
|
||||
return o.getInt(k);
|
||||
} catch (JSONException ignore) {
|
||||
// value was not found
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON object value from the given JSON object.
|
||||
*
|
||||
* @param jsonObject A JSON object to search
|
||||
* @param key A key to fetch from the JSON object
|
||||
* @return The value from the config, if exists. Null if not
|
||||
*/
|
||||
public static JSONObject getObject(JSONObject jsonObject, String key) {
|
||||
String k = getDeepestKey(key);
|
||||
try {
|
||||
JSONObject o = getDeepestObject(jsonObject, key);
|
||||
|
||||
return o.getJSONObject(k);
|
||||
} catch (JSONException ignore) {
|
||||
// value was not found
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string array value from the given JSON object.
|
||||
*
|
||||
* @param jsonObject A JSON object to search
|
||||
* @param key A key to fetch from the JSON object
|
||||
* @param defaultValue A default value to return if the key cannot be found
|
||||
* @return The value at the given key in the JSON object, or the default value
|
||||
*/
|
||||
public static String[] getArray(JSONObject jsonObject, String key, String[] defaultValue) {
|
||||
String k = getDeepestKey(key);
|
||||
try {
|
||||
JSONObject o = getDeepestObject(jsonObject, key);
|
||||
|
||||
JSONArray a = o.getJSONArray(k);
|
||||
if (a == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int l = a.length();
|
||||
String[] value = new String[l];
|
||||
|
||||
for (int i = 0; i < l; i++) {
|
||||
value[i] = (String) a.get(i);
|
||||
}
|
||||
|
||||
return value;
|
||||
} catch (JSONException ignore) {
|
||||
// value was not found
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a JSON key path, gets the deepest key.
|
||||
*
|
||||
* @param key The key path
|
||||
* @return The deepest key
|
||||
*/
|
||||
private static String getDeepestKey(String key) {
|
||||
String[] parts = key.split("\\.");
|
||||
if (parts.length > 0) {
|
||||
return parts[parts.length - 1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a JSON object and key path, gets the deepest object in the path.
|
||||
*
|
||||
* @param jsonObject A JSON object
|
||||
* @param key The key path to follow
|
||||
* @return The deepest object along the key path
|
||||
* @throws JSONException Thrown if any JSON errors
|
||||
*/
|
||||
private static JSONObject getDeepestObject(JSONObject jsonObject, String key) throws JSONException {
|
||||
String[] parts = key.split("\\.");
|
||||
JSONObject o = jsonObject;
|
||||
|
||||
// Search until the second to last part of the key
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
String k = parts[i];
|
||||
o = o.getJSONObject(k);
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
}
|
||||
114
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/PermissionHelper.java
generated
vendored
Normal file
114
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/PermissionHelper.java
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.getcapacitor.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A helper class for checking permissions.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class PermissionHelper {
|
||||
|
||||
/**
|
||||
* Checks if a list of given permissions are all granted by the user
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param permissions Permissions to check.
|
||||
* @return True if all permissions are granted, false if at least one is not.
|
||||
*/
|
||||
public static boolean hasPermissions(Context context, String[] permissions) {
|
||||
for (String perm : permissions) {
|
||||
if (ActivityCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given permission has been defined in the AndroidManifest.xml
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param permission A permission to check.
|
||||
* @return True if the permission has been defined in the Manifest, false if not.
|
||||
*/
|
||||
public static boolean hasDefinedPermission(Context context, String permission) {
|
||||
boolean hasPermission = false;
|
||||
String[] requestedPermissions = PermissionHelper.getManifestPermissions(context);
|
||||
if (requestedPermissions != null && requestedPermissions.length > 0) {
|
||||
List<String> requestedPermissionsList = Arrays.asList(requestedPermissions);
|
||||
ArrayList<String> requestedPermissionsArrayList = new ArrayList<>(requestedPermissionsList);
|
||||
if (requestedPermissionsArrayList.contains(permission)) {
|
||||
hasPermission = true;
|
||||
}
|
||||
}
|
||||
return hasPermission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether all of the given permissions have been defined in the AndroidManifest.xml
|
||||
* @param context the app context
|
||||
* @param permissions a list of permissions
|
||||
* @return true only if all permissions are defined in the AndroidManifest.xml
|
||||
*/
|
||||
public static boolean hasDefinedPermissions(Context context, String[] permissions) {
|
||||
for (String permission : permissions) {
|
||||
if (!PermissionHelper.hasDefinedPermission(context, permission)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permissions defined in AndroidManifest.xml
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @return The permissions defined in AndroidManifest.xml
|
||||
*/
|
||||
public static String[] getManifestPermissions(Context context) {
|
||||
String[] requestedPermissions = null;
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo packageInfo = InternalUtils.getPackageInfo(pm, context.getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||
|
||||
if (packageInfo != null) {
|
||||
requestedPermissions = packageInfo.requestedPermissions;
|
||||
}
|
||||
} catch (Exception ex) {}
|
||||
return requestedPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of permissions, return a new list with the ones not present in AndroidManifest.xml
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param neededPermissions The permissions needed.
|
||||
* @return The permissions not present in AndroidManifest.xml
|
||||
*/
|
||||
public static String[] getUndefinedPermissions(Context context, String[] neededPermissions) {
|
||||
ArrayList<String> undefinedPermissions = new ArrayList<>();
|
||||
String[] requestedPermissions = getManifestPermissions(context);
|
||||
if (requestedPermissions != null && requestedPermissions.length > 0) {
|
||||
List<String> requestedPermissionsList = Arrays.asList(requestedPermissions);
|
||||
ArrayList<String> requestedPermissionsArrayList = new ArrayList<>(requestedPermissionsList);
|
||||
for (String permission : neededPermissions) {
|
||||
if (!requestedPermissionsArrayList.contains(permission)) {
|
||||
undefinedPermissions.add(permission);
|
||||
}
|
||||
}
|
||||
String[] undefinedPermissionArray = new String[undefinedPermissions.size()];
|
||||
undefinedPermissionArray = undefinedPermissions.toArray(undefinedPermissionArray);
|
||||
|
||||
return undefinedPermissionArray;
|
||||
}
|
||||
return neededPermissions;
|
||||
}
|
||||
}
|
||||
28
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/WebColor.java
generated
vendored
Normal file
28
node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/util/WebColor.java
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.getcapacitor.util;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
public class WebColor {
|
||||
|
||||
/**
|
||||
* Parse the color string, and return the corresponding color-int. If the string cannot be parsed, throws an IllegalArgumentException exception.
|
||||
* @param colorString The hexadecimal color string. The format is an RGB or RGBA hex string.
|
||||
* @return The corresponding color as an int.
|
||||
*/
|
||||
public static int parseColor(String colorString) {
|
||||
String formattedColor = colorString;
|
||||
if (colorString.charAt(0) != '#') {
|
||||
formattedColor = "#" + formattedColor;
|
||||
}
|
||||
|
||||
if (formattedColor.length() != 7 && formattedColor.length() != 9) {
|
||||
throw new IllegalArgumentException("The encoded color space is invalid or unknown");
|
||||
} else if (formattedColor.length() == 7) {
|
||||
return Color.parseColor(formattedColor);
|
||||
} else {
|
||||
// Convert to Android format #AARRGGBB from #RRGGBBAA
|
||||
formattedColor = "#" + formattedColor.substring(7) + formattedColor.substring(1, 7);
|
||||
return Color.parseColor(formattedColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
node_modules/@capacitor/android/capacitor/src/main/res/layout/bridge_layout_main.xml
generated
vendored
Normal file
15
node_modules/@capacitor/android/capacitor/src/main/res/layout/bridge_layout_main.xml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.getcapacitor.BridgeActivity"
|
||||
>
|
||||
|
||||
<com.getcapacitor.CapacitorWebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
13
node_modules/@capacitor/android/capacitor/src/main/res/layout/fragment_bridge.xml
generated
vendored
Normal file
13
node_modules/@capacitor/android/capacitor/src/main/res/layout/fragment_bridge.xml
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#F0FF1414"
|
||||
tools:context="com.getcapacitor.BridgeFragment">
|
||||
|
||||
<com.getcapacitor.CapacitorWebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
14
node_modules/@capacitor/android/capacitor/src/main/res/layout/no_webview.xml
generated
vendored
Normal file
14
node_modules/@capacitor/android/capacitor/src/main/res/layout/no_webview.xml
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/no_webview_text"
|
||||
android:gravity="center"
|
||||
android:textSize="48sp" />
|
||||
</LinearLayout>
|
||||
6
node_modules/@capacitor/android/capacitor/src/main/res/values/attrs.xml
generated
vendored
Normal file
6
node_modules/@capacitor/android/capacitor/src/main/res/values/attrs.xml
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="bridge_fragment">
|
||||
<attr name="start_dir" format="string"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
6
node_modules/@capacitor/android/capacitor/src/main/res/values/colors.xml
generated
vendored
Normal file
6
node_modules/@capacitor/android/capacitor/src/main/res/values/colors.xml
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<color tools:ignore="UnusedResources" name="colorPrimary">#3F51B5</color>
|
||||
<color tools:ignore="UnusedResources" name="colorPrimaryDark">#303F9F</color>
|
||||
<color tools:ignore="UnusedResources" name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
3
node_modules/@capacitor/android/capacitor/src/main/res/values/strings.xml
generated
vendored
Normal file
3
node_modules/@capacitor/android/capacitor/src/main/res/values/strings.xml
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="no_webview_text">This app requires a WebView to work</string>
|
||||
</resources>
|
||||
6
node_modules/@capacitor/android/capacitor/src/main/res/values/styles.xml
generated
vendored
Normal file
6
node_modules/@capacitor/android/capacitor/src/main/res/values/styles.xml
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
31
node_modules/@capacitor/android/package.json
generated
vendored
Normal file
31
node_modules/@capacitor/android/package.json
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@capacitor/android",
|
||||
"version": "6.2.2",
|
||||
"description": "Capacitor: Cross-platform apps with JavaScript and the web",
|
||||
"homepage": "https://capacitorjs.com",
|
||||
"author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ionic-team/capacitor.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ionic-team/capacitor/issues"
|
||||
},
|
||||
"files": [
|
||||
"capacitor/build.gradle",
|
||||
"capacitor/lint-baseline.xml",
|
||||
"capacitor/lint.xml",
|
||||
"capacitor/proguard-rules.pro",
|
||||
"capacitor/src/main/"
|
||||
],
|
||||
"scripts": {
|
||||
"verify": "./gradlew clean lint build test -b capacitor/build.gradle"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": "^6.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
21
node_modules/@capacitor/cli/LICENSE
generated
vendored
Normal file
21
node_modules/@capacitor/cli/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-present Drifty Co.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
15
node_modules/@capacitor/cli/README.md
generated
vendored
Normal file
15
node_modules/@capacitor/cli/README.md
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Capacitor CLI
|
||||
|
||||
The Capacitor command-line interface should be installed locally and executed through `npm` scripts.
|
||||
|
||||
```
|
||||
npm install @capacitor/cli --save-dev
|
||||
```
|
||||
|
||||
## Using Capacitor CLI
|
||||
|
||||
Consult the Getting Started guide for information on using the CLI and Capacitor scripts.
|
||||
|
||||
### License
|
||||
|
||||
* [MIT](https://github.com/ionic-team/capacitor/blob/HEAD/LICENSE)
|
||||
BIN
node_modules/@capacitor/cli/assets/android-template.tar.gz
generated
vendored
Normal file
BIN
node_modules/@capacitor/cli/assets/android-template.tar.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/@capacitor/cli/assets/capacitor-cordova-android-plugins.tar.gz
generated
vendored
Normal file
BIN
node_modules/@capacitor/cli/assets/capacitor-cordova-android-plugins.tar.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/@capacitor/cli/assets/capacitor-cordova-ios-plugins.tar.gz
generated
vendored
Normal file
BIN
node_modules/@capacitor/cli/assets/capacitor-cordova-ios-plugins.tar.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/@capacitor/cli/assets/ios-pods-template.tar.gz
generated
vendored
Normal file
BIN
node_modules/@capacitor/cli/assets/ios-pods-template.tar.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/@capacitor/cli/assets/ios-spm-template.tar.gz
generated
vendored
Normal file
BIN
node_modules/@capacitor/cli/assets/ios-spm-template.tar.gz
generated
vendored
Normal file
Binary file not shown.
18
node_modules/@capacitor/cli/bin/capacitor
generated
vendored
Executable file
18
node_modules/@capacitor/cli/bin/capacitor
generated
vendored
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
var satisfies = require('semver/functions/satisfies');
|
||||
var packageJson = require('../package.json');
|
||||
var requiresNodeVersion = packageJson.engines.node;
|
||||
|
||||
if (!satisfies(process.version, requiresNodeVersion, { includePrerelease: true})) {
|
||||
process.stdout.write(
|
||||
'\x1b[31m[fatal]\x1b[39m The Capacitor CLI requires NodeJS ' + requiresNodeVersion + '\n' +
|
||||
' Please install the latest LTS version.\n'
|
||||
);
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var cli = require('../dist/index');
|
||||
cli.run();
|
||||
52
node_modules/@capacitor/cli/dist/android/add.js
generated
vendored
Normal file
52
node_modules/@capacitor/cli/dist/android/add.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createLocalProperties = exports.addAndroid = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const os_1 = require("os");
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
||||
const common_1 = require("../common");
|
||||
const subprocess_1 = require("../util/subprocess");
|
||||
const template_1 = require("../util/template");
|
||||
async function addAndroid(config) {
|
||||
await (0, common_1.runTask)(`Adding native android project in ${colors_1.default.strong(config.android.platformDir)}`, async () => {
|
||||
return (0, template_1.extractTemplate)(config.cli.assets.android.platformTemplateArchiveAbs, config.android.platformDirAbs);
|
||||
});
|
||||
}
|
||||
exports.addAndroid = addAndroid;
|
||||
async function createLocalProperties(platformDir) {
|
||||
const defaultAndroidPath = (0, path_1.join)((0, os_1.homedir)(), 'Library/Android/sdk');
|
||||
if (await (0, utils_fs_1.pathExists)(defaultAndroidPath)) {
|
||||
const localSettings = `
|
||||
## This file is automatically generated by Android Studio.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file should *NOT* be checked into Version Control Systems,
|
||||
# as it contains information specific to your local configuration.
|
||||
#
|
||||
# Location of the SDK. This is only used by Gradle.
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
sdk.dir=${defaultAndroidPath}
|
||||
`;
|
||||
await (0, utils_fs_1.writeFile)((0, path_1.join)(platformDir, 'local.properties'), localSettings, {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
// Only sync if we were able to create the local properties above, otherwise
|
||||
// this will fail
|
||||
try {
|
||||
await gradleSync(platformDir);
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Error running gradle sync', e);
|
||||
console.error('Unable to infer default Android SDK settings. This is fine, just run npx cap open android and import and sync gradle manually');
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.createLocalProperties = createLocalProperties;
|
||||
async function gradleSync(platformDir) {
|
||||
await (0, subprocess_1.runCommand)(`./gradlew`, [], {
|
||||
cwd: platformDir,
|
||||
});
|
||||
}
|
||||
106
node_modules/@capacitor/cli/dist/android/build.js
generated
vendored
Normal file
106
node_modules/@capacitor/cli/dist/android/build.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.buildAndroid = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
||||
const common_1 = require("../common");
|
||||
const log_1 = require("../log");
|
||||
const subprocess_1 = require("../util/subprocess");
|
||||
async function buildAndroid(config, buildOptions) {
|
||||
var _a, _b;
|
||||
const releaseType = (_a = buildOptions.androidreleasetype) !== null && _a !== void 0 ? _a : 'AAB';
|
||||
const releaseTypeIsAAB = releaseType === 'AAB';
|
||||
const flavor = (_b = buildOptions.flavor) !== null && _b !== void 0 ? _b : '';
|
||||
const arg = releaseTypeIsAAB
|
||||
? `:app:bundle${flavor}Release`
|
||||
: `assemble${flavor}Release`;
|
||||
const gradleArgs = [arg];
|
||||
try {
|
||||
await (0, common_1.runTask)('Running Gradle build', async () => (0, subprocess_1.runCommand)('./gradlew', gradleArgs, {
|
||||
cwd: config.android.platformDirAbs,
|
||||
}));
|
||||
}
|
||||
catch (e) {
|
||||
if (e.includes('EACCES')) {
|
||||
throw `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${colors_1.default.strong(`chmod +x ./${config.android.platformDir}/gradlew`)} and try again.`;
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
const releaseDir = releaseTypeIsAAB
|
||||
? flavor !== ''
|
||||
? `${flavor}Release`
|
||||
: 'release'
|
||||
: flavor !== ''
|
||||
? (0, path_1.join)(flavor, 'release')
|
||||
: 'release';
|
||||
const releasePath = (0, path_1.join)(config.android.appDirAbs, 'build', 'outputs', releaseTypeIsAAB ? 'bundle' : 'apk', releaseDir);
|
||||
const unsignedReleaseName = `app${flavor !== '' ? `-${flavor}` : ''}-release${releaseTypeIsAAB ? '' : '-unsigned'}.${releaseType.toLowerCase()}`;
|
||||
const signedReleaseName = unsignedReleaseName.replace(`-release${releaseTypeIsAAB ? '' : '-unsigned'}.${releaseType.toLowerCase()}`, `-release-signed.${releaseType.toLowerCase()}`);
|
||||
if (buildOptions.signingtype == 'jarsigner') {
|
||||
await signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
|
||||
}
|
||||
else {
|
||||
await signWithApkSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName);
|
||||
}
|
||||
(0, log_1.logSuccess)(`Successfully generated ${signedReleaseName} at: ${releasePath}`);
|
||||
}
|
||||
exports.buildAndroid = buildAndroid;
|
||||
async function signWithApkSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName) {
|
||||
if (!buildOptions.keystorepath || !buildOptions.keystorepass) {
|
||||
throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password)';
|
||||
}
|
||||
const signingArgs = [
|
||||
'sign',
|
||||
'--ks',
|
||||
buildOptions.keystorepath,
|
||||
'--ks-pass',
|
||||
`pass:${buildOptions.keystorepass}`,
|
||||
'--in',
|
||||
`${(0, path_1.join)(releasePath, unsignedReleaseName)}`,
|
||||
'--out',
|
||||
`${(0, path_1.join)(releasePath, signedReleaseName)}`,
|
||||
];
|
||||
if (buildOptions.keystorealias) {
|
||||
signingArgs.push('--ks-key-alias', buildOptions.keystorealias);
|
||||
}
|
||||
if (buildOptions.keystorealiaspass) {
|
||||
signingArgs.push('--key-pass', `pass:${buildOptions.keystorealiaspass}`);
|
||||
}
|
||||
await (0, common_1.runTask)('Signing Release', async () => {
|
||||
await (0, subprocess_1.runCommand)('apksigner', signingArgs, {
|
||||
cwd: config.android.platformDirAbs,
|
||||
});
|
||||
});
|
||||
}
|
||||
async function signWithJarSigner(config, buildOptions, releasePath, signedReleaseName, unsignedReleaseName) {
|
||||
if (!buildOptions.keystorepath ||
|
||||
!buildOptions.keystorealias ||
|
||||
!buildOptions.keystorealiaspass ||
|
||||
!buildOptions.keystorepass) {
|
||||
throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password, Keystore Key Alias, Keystore Key Password)';
|
||||
}
|
||||
const signingArgs = [
|
||||
'-sigalg',
|
||||
'SHA1withRSA',
|
||||
'-digestalg',
|
||||
'SHA1',
|
||||
'-keystore',
|
||||
buildOptions.keystorepath,
|
||||
'-keypass',
|
||||
buildOptions.keystorealiaspass,
|
||||
'-storepass',
|
||||
buildOptions.keystorepass,
|
||||
`-signedjar`,
|
||||
`${(0, path_1.join)(releasePath, signedReleaseName)}`,
|
||||
`${(0, path_1.join)(releasePath, unsignedReleaseName)}`,
|
||||
buildOptions.keystorealias,
|
||||
];
|
||||
await (0, common_1.runTask)('Signing Release', async () => {
|
||||
await (0, subprocess_1.runCommand)('jarsigner', signingArgs, {
|
||||
cwd: config.android.platformDirAbs,
|
||||
});
|
||||
});
|
||||
}
|
||||
97
node_modules/@capacitor/cli/dist/android/common.js
generated
vendored
Normal file
97
node_modules/@capacitor/cli/dist/android/common.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.editProjectSettingsAndroid = exports.resolvePlugin = exports.getAndroidPlugins = exports.checkAndroidPackage = void 0;
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const path_1 = require("path");
|
||||
const common_1 = require("../common");
|
||||
const cordova_1 = require("../cordova");
|
||||
const plugin_1 = require("../plugin");
|
||||
const fs_1 = require("../util/fs");
|
||||
async function checkAndroidPackage(config) {
|
||||
return (0, common_1.checkCapacitorPlatform)(config, 'android');
|
||||
}
|
||||
exports.checkAndroidPackage = checkAndroidPackage;
|
||||
async function getAndroidPlugins(allPlugins) {
|
||||
const resolved = await Promise.all(allPlugins.map(async (plugin) => await resolvePlugin(plugin)));
|
||||
return resolved.filter((plugin) => !!plugin);
|
||||
}
|
||||
exports.getAndroidPlugins = getAndroidPlugins;
|
||||
async function resolvePlugin(plugin) {
|
||||
var _a;
|
||||
const platform = 'android';
|
||||
if ((_a = plugin.manifest) === null || _a === void 0 ? void 0 : _a.android) {
|
||||
let pluginFilesPath = plugin.manifest.android.src
|
||||
? plugin.manifest.android.src
|
||||
: platform;
|
||||
const absolutePath = (0, path_1.join)(plugin.rootPath, pluginFilesPath, plugin.id);
|
||||
// Android folder shouldn't have subfolders, but they used to, so search for them for compatibility reasons
|
||||
if (await (0, utils_fs_1.pathExists)(absolutePath)) {
|
||||
pluginFilesPath = (0, path_1.join)(platform, plugin.id);
|
||||
}
|
||||
plugin.android = {
|
||||
type: 0 /* PluginType.Core */,
|
||||
path: (0, fs_1.convertToUnixPath)(pluginFilesPath),
|
||||
};
|
||||
}
|
||||
else if (plugin.xml) {
|
||||
plugin.android = {
|
||||
type: 1 /* PluginType.Cordova */,
|
||||
path: 'src/' + platform,
|
||||
};
|
||||
if ((0, cordova_1.getIncompatibleCordovaPlugins)(platform).includes(plugin.id) ||
|
||||
!(0, plugin_1.getPluginPlatform)(plugin, platform)) {
|
||||
plugin.android.type = 2 /* PluginType.Incompatible */;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
exports.resolvePlugin = resolvePlugin;
|
||||
/**
|
||||
* Update an Android project with the desired app name and appId.
|
||||
* This is a little trickier for Android because the appId becomes
|
||||
* the package name.
|
||||
*/
|
||||
async function editProjectSettingsAndroid(config) {
|
||||
const appId = config.app.appId;
|
||||
const appName = config.app.appName
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/'/g, "\\'");
|
||||
const buildGradlePath = (0, path_1.resolve)(config.android.appDirAbs, 'build.gradle');
|
||||
const domainPath = appId.split('.').join('/');
|
||||
// Make the package source path to the new plugin Java file
|
||||
const newJavaPath = (0, path_1.resolve)(config.android.srcMainDirAbs, `java/${domainPath}`);
|
||||
if (!(await (0, utils_fs_1.pathExists)(newJavaPath))) {
|
||||
await (0, utils_fs_1.mkdirp)(newJavaPath);
|
||||
}
|
||||
await (0, utils_fs_1.copy)((0, path_1.resolve)(config.android.srcMainDirAbs, 'java/com/getcapacitor/myapp/MainActivity.java'), (0, path_1.resolve)(newJavaPath, 'MainActivity.java'));
|
||||
if (appId.split('.')[1] !== 'getcapacitor') {
|
||||
await (0, utils_fs_1.remove)((0, path_1.resolve)(config.android.srcMainDirAbs, 'java/com/getcapacitor'));
|
||||
}
|
||||
// Remove our template 'com' folder if their ID doesn't have it
|
||||
if (appId.split('.')[0] !== 'com') {
|
||||
await (0, utils_fs_1.remove)((0, path_1.resolve)(config.android.srcMainDirAbs, 'java/com/'));
|
||||
}
|
||||
// Update the package in the MainActivity java file
|
||||
const activityPath = (0, path_1.resolve)(newJavaPath, 'MainActivity.java');
|
||||
let activityContent = await (0, utils_fs_1.readFile)(activityPath, { encoding: 'utf-8' });
|
||||
activityContent = activityContent.replace(/package ([^;]*)/, `package ${appId}`);
|
||||
await (0, utils_fs_1.writeFile)(activityPath, activityContent, { encoding: 'utf-8' });
|
||||
// Update the applicationId in build.gradle
|
||||
let gradleContent = await (0, utils_fs_1.readFile)(buildGradlePath, { encoding: 'utf-8' });
|
||||
gradleContent = gradleContent.replace(/applicationId "[^"]+"/, `applicationId "${appId}"`);
|
||||
// Update the namespace in build.gradle
|
||||
gradleContent = gradleContent.replace(/namespace "[^"]+"/, `namespace "${appId}"`);
|
||||
await (0, utils_fs_1.writeFile)(buildGradlePath, gradleContent, { encoding: 'utf-8' });
|
||||
// Update the settings in res/values/strings.xml
|
||||
const stringsPath = (0, path_1.resolve)(config.android.resDirAbs, 'values/strings.xml');
|
||||
let stringsContent = await (0, utils_fs_1.readFile)(stringsPath, { encoding: 'utf-8' });
|
||||
stringsContent = stringsContent.replace(/com.getcapacitor.myapp/g, appId);
|
||||
stringsContent = stringsContent.replace(/My App/g, appName);
|
||||
await (0, utils_fs_1.writeFile)(stringsPath, stringsContent);
|
||||
}
|
||||
exports.editProjectSettingsAndroid = editProjectSettingsAndroid;
|
||||
163
node_modules/@capacitor/cli/dist/android/doctor.js
generated
vendored
Normal file
163
node_modules/@capacitor/cli/dist/android/doctor.js
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.doctorAndroid = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
||||
const common_1 = require("../common");
|
||||
const errors_1 = require("../errors");
|
||||
const log_1 = require("../log");
|
||||
const xml_1 = require("../util/xml");
|
||||
async function doctorAndroid(config) {
|
||||
var _a;
|
||||
try {
|
||||
await (0, common_1.check)([
|
||||
checkAndroidInstalled,
|
||||
() => checkGradlew(config),
|
||||
() => checkAppSrcDirs(config),
|
||||
]);
|
||||
(0, log_1.logSuccess)('Android looking great! 👌');
|
||||
}
|
||||
catch (e) {
|
||||
if (!(0, errors_1.isFatal)(e)) {
|
||||
(0, errors_1.fatal)((_a = e.stack) !== null && _a !== void 0 ? _a : e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
exports.doctorAndroid = doctorAndroid;
|
||||
async function checkAppSrcDirs(config) {
|
||||
if (!(await (0, utils_fs_1.pathExists)(config.android.appDirAbs))) {
|
||||
return `${colors_1.default.strong(config.android.appDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
|
||||
}
|
||||
if (!(await (0, utils_fs_1.pathExists)(config.android.srcMainDirAbs))) {
|
||||
return `${colors_1.default.strong(config.android.srcMainDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
|
||||
}
|
||||
if (!(await (0, utils_fs_1.pathExists)(config.android.assetsDirAbs))) {
|
||||
return `${colors_1.default.strong(config.android.assetsDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
|
||||
}
|
||||
if (!(await (0, utils_fs_1.pathExists)(config.android.webDirAbs))) {
|
||||
return `${colors_1.default.strong(config.android.webDir)} directory is missing in ${colors_1.default.strong(config.android.platformDir)}`;
|
||||
}
|
||||
const appSrcMainAssetsWwwIndexHtmlDir = (0, path_1.join)(config.android.webDirAbs, 'index.html');
|
||||
if (!(await (0, utils_fs_1.pathExists)(appSrcMainAssetsWwwIndexHtmlDir))) {
|
||||
return `${colors_1.default.strong('index.html')} file is missing in ${colors_1.default.strong(config.android.webDirAbs)}`;
|
||||
}
|
||||
return checkAndroidManifestFile(config);
|
||||
}
|
||||
async function checkAndroidManifestFile(config) {
|
||||
const manifestFileName = 'AndroidManifest.xml';
|
||||
const manifestFilePath = (0, path_1.join)(config.android.srcMainDirAbs, manifestFileName);
|
||||
if (!(await (0, utils_fs_1.pathExists)(manifestFilePath))) {
|
||||
return `${colors_1.default.strong(manifestFileName)} is missing in ${colors_1.default.strong(config.android.srcMainDir)}`;
|
||||
}
|
||||
try {
|
||||
const xmlData = await (0, xml_1.readXML)(manifestFilePath);
|
||||
return checkAndroidManifestData(config, xmlData);
|
||||
}
|
||||
catch (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
async function checkAndroidManifestData(config, xmlData) {
|
||||
const manifestNode = xmlData.manifest;
|
||||
if (!manifestNode) {
|
||||
return `Missing ${colors_1.default.input('<manifest>')} XML node in ${colors_1.default.strong(config.android.srcMainDir)}`;
|
||||
}
|
||||
const applicationChildNodes = manifestNode.application;
|
||||
if (!Array.isArray(manifestNode.application)) {
|
||||
return `Missing ${colors_1.default.input('<application>')} XML node as a child node of ${colors_1.default.input('<manifest>')} in ${colors_1.default.strong(config.android.srcMainDir)}`;
|
||||
}
|
||||
let mainActivityClassPath = '';
|
||||
const mainApplicationNode = applicationChildNodes.find(applicationChildNode => {
|
||||
const activityChildNodes = applicationChildNode.activity;
|
||||
if (!Array.isArray(activityChildNodes)) {
|
||||
return false;
|
||||
}
|
||||
const mainActivityNode = activityChildNodes.find(activityChildNode => {
|
||||
const intentFilterChildNodes = activityChildNode['intent-filter'];
|
||||
if (!Array.isArray(intentFilterChildNodes)) {
|
||||
return false;
|
||||
}
|
||||
return intentFilterChildNodes.find(intentFilterChildNode => {
|
||||
const actionChildNodes = intentFilterChildNode.action;
|
||||
if (!Array.isArray(actionChildNodes)) {
|
||||
return false;
|
||||
}
|
||||
const mainActionChildNode = actionChildNodes.find(actionChildNode => {
|
||||
const androidName = actionChildNode.$['android:name'];
|
||||
return androidName === 'android.intent.action.MAIN';
|
||||
});
|
||||
if (!mainActionChildNode) {
|
||||
return false;
|
||||
}
|
||||
const categoryChildNodes = intentFilterChildNode.category;
|
||||
if (!Array.isArray(categoryChildNodes)) {
|
||||
return false;
|
||||
}
|
||||
return categoryChildNodes.find(categoryChildNode => {
|
||||
const androidName = categoryChildNode.$['android:name'];
|
||||
return androidName === 'android.intent.category.LAUNCHER';
|
||||
});
|
||||
});
|
||||
});
|
||||
if (mainActivityNode) {
|
||||
mainActivityClassPath = mainActivityNode.$['android:name'];
|
||||
}
|
||||
return mainActivityNode;
|
||||
});
|
||||
if (!mainApplicationNode) {
|
||||
return `Missing main ${colors_1.default.input('<activity>')} XML node in ${colors_1.default.strong(config.android.srcMainDir)}`;
|
||||
}
|
||||
if (!mainActivityClassPath) {
|
||||
return `Missing ${colors_1.default.input('<activity android:name="">')} attribute for MainActivity class in ${colors_1.default.strong(config.android.srcMainDir)}`;
|
||||
}
|
||||
return checkPackage(config, mainActivityClassPath);
|
||||
}
|
||||
async function checkPackage(config, mainActivityClassPath) {
|
||||
const appSrcMainJavaDir = (0, path_1.join)(config.android.srcMainDirAbs, 'java');
|
||||
if (!(await (0, utils_fs_1.pathExists)(appSrcMainJavaDir))) {
|
||||
return `${colors_1.default.strong('java')} directory is missing in ${colors_1.default.strong(appSrcMainJavaDir)}`;
|
||||
}
|
||||
const mainActivityClassName = mainActivityClassPath.split('.').pop();
|
||||
const srcFiles = await (0, utils_fs_1.readdirp)(appSrcMainJavaDir, {
|
||||
filter: entry => !entry.stats.isDirectory() &&
|
||||
['.java', '.kt'].includes((0, path_1.extname)(entry.path)) &&
|
||||
mainActivityClassName === (0, path_1.parse)(entry.path).name,
|
||||
});
|
||||
if (srcFiles.length == 0) {
|
||||
return `Main activity file (${mainActivityClassName}) is missing`;
|
||||
}
|
||||
return checkBuildGradle(config);
|
||||
}
|
||||
async function checkBuildGradle(config) {
|
||||
const fileName = 'build.gradle';
|
||||
const filePath = (0, path_1.join)(config.android.appDirAbs, fileName);
|
||||
if (!(await (0, utils_fs_1.pathExists)(filePath))) {
|
||||
return `${colors_1.default.strong(fileName)} file is missing in ${colors_1.default.strong(config.android.appDir)}`;
|
||||
}
|
||||
let fileContent = await (0, utils_fs_1.readFile)(filePath, { encoding: 'utf-8' });
|
||||
fileContent = fileContent.replace(/'|"/g, '').replace(/\s+/g, ' ');
|
||||
const searchFor = `applicationId`;
|
||||
if (fileContent.indexOf(searchFor) === -1) {
|
||||
return `${colors_1.default.strong('build.gradle')} file missing ${colors_1.default.input(`applicationId`)} config in ${filePath}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function checkGradlew(config) {
|
||||
const fileName = 'gradlew';
|
||||
const filePath = (0, path_1.join)(config.android.platformDirAbs, fileName);
|
||||
if (!(await (0, utils_fs_1.pathExists)(filePath))) {
|
||||
return `${colors_1.default.strong(fileName)} file is missing in ${colors_1.default.strong(config.android.platformDir)}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function checkAndroidInstalled() {
|
||||
/*
|
||||
if (!await isInstalled('android')) {
|
||||
return 'Android is not installed. For information: https://developer.android.com/studio/index.html';
|
||||
}
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
28
node_modules/@capacitor/cli/dist/android/open.js
generated
vendored
Normal file
28
node_modules/@capacitor/cli/dist/android/open.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.openAndroid = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const debug_1 = tslib_1.__importDefault(require("debug"));
|
||||
const open_1 = tslib_1.__importDefault(require("open"));
|
||||
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
||||
const log_1 = require("../log");
|
||||
const debug = (0, debug_1.default)('capacitor:android:open');
|
||||
async function openAndroid(config) {
|
||||
const androidStudioPath = await config.android.studioPath;
|
||||
const dir = config.android.platformDirAbs;
|
||||
try {
|
||||
if (!(await (0, utils_fs_1.pathExists)(androidStudioPath))) {
|
||||
throw new Error(`Android Studio does not exist at: ${androidStudioPath}`);
|
||||
}
|
||||
await (0, open_1.default)(dir, { app: { name: androidStudioPath }, wait: false });
|
||||
log_1.logger.info(`Opening Android project at: ${colors_1.default.strong(config.android.platformDir)}.`);
|
||||
}
|
||||
catch (e) {
|
||||
debug('Error opening Android Studio: %O', e);
|
||||
log_1.logger.error('Unable to launch Android Studio. Is it installed?\n' +
|
||||
`Attempted to open Android Studio at: ${colors_1.default.strong(androidStudioPath)}\n` +
|
||||
`You can configure this with the ${colors_1.default.input('CAPACITOR_ANDROID_STUDIO_PATH')} environment variable.`);
|
||||
}
|
||||
}
|
||||
exports.openAndroid = openAndroid;
|
||||
42
node_modules/@capacitor/cli/dist/android/run.js
generated
vendored
Normal file
42
node_modules/@capacitor/cli/dist/android/run.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.runAndroid = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const debug_1 = tslib_1.__importDefault(require("debug"));
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
||||
const common_1 = require("../common");
|
||||
const native_run_1 = require("../util/native-run");
|
||||
const subprocess_1 = require("../util/subprocess");
|
||||
const debug = (0, debug_1.default)('capacitor:android:run');
|
||||
async function runAndroid(config, { target: selectedTarget, flavor: selectedFlavor, forwardPorts: selectedPorts, }) {
|
||||
var _a;
|
||||
const target = await (0, common_1.promptForPlatformTarget)(await (0, native_run_1.getPlatformTargets)('android'), selectedTarget);
|
||||
const runFlavor = selectedFlavor || ((_a = config.android) === null || _a === void 0 ? void 0 : _a.flavor) || '';
|
||||
const arg = `assemble${runFlavor}Debug`;
|
||||
const gradleArgs = [arg];
|
||||
debug('Invoking ./gradlew with args: %O', gradleArgs);
|
||||
try {
|
||||
await (0, common_1.runTask)('Running Gradle build', async () => (0, subprocess_1.runCommand)('./gradlew', gradleArgs, {
|
||||
cwd: config.android.platformDirAbs,
|
||||
}));
|
||||
}
|
||||
catch (e) {
|
||||
if (e.includes('EACCES')) {
|
||||
throw `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${colors_1.default.strong(`chmod +x ./${config.android.platformDir}/gradlew`)} and try again.`;
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
const pathToApk = `${config.android.platformDirAbs}/${config.android.appDir}/build/outputs/apk${runFlavor !== '' ? '/' + runFlavor : ''}/debug`;
|
||||
const apkName = (0, common_1.parseApkNameFromFlavor)(runFlavor);
|
||||
const apkPath = (0, path_1.resolve)(pathToApk, apkName);
|
||||
const nativeRunArgs = ['android', '--app', apkPath, '--target', target.id];
|
||||
if (selectedPorts) {
|
||||
nativeRunArgs.push('--forward', `${selectedPorts}`);
|
||||
}
|
||||
debug('Invoking native-run with args: %O', nativeRunArgs);
|
||||
await (0, common_1.runTask)(`Deploying ${colors_1.default.strong(apkName)} to ${colors_1.default.input(target.id)}`, async () => (0, native_run_1.runNativeRun)(nativeRunArgs));
|
||||
}
|
||||
exports.runAndroid = runAndroid;
|
||||
323
node_modules/@capacitor/cli/dist/android/update.js
generated
vendored
Normal file
323
node_modules/@capacitor/cli/dist/android/update.js
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.handleCordovaPluginsGradle = exports.installGradlePlugins = exports.updateAndroid = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const debug_1 = tslib_1.__importDefault(require("debug"));
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
||||
const common_1 = require("../common");
|
||||
const cordova_1 = require("../cordova");
|
||||
const errors_1 = require("../errors");
|
||||
const plugin_1 = require("../plugin");
|
||||
const copy_1 = require("../tasks/copy");
|
||||
const migrate_1 = require("../tasks/migrate");
|
||||
const fs_1 = require("../util/fs");
|
||||
const node_1 = require("../util/node");
|
||||
const template_1 = require("../util/template");
|
||||
const common_2 = require("./common");
|
||||
const platform = 'android';
|
||||
const debug = (0, debug_1.default)('capacitor:android:update');
|
||||
async function updateAndroid(config) {
|
||||
const plugins = await getPluginsTask(config);
|
||||
const capacitorPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 0 /* PluginType.Core */);
|
||||
(0, plugin_1.printPlugins)(capacitorPlugins, 'android');
|
||||
await writePluginsJson(config, capacitorPlugins);
|
||||
await removePluginsNativeFiles(config);
|
||||
const cordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
|
||||
await (0, migrate_1.patchOldCapacitorPlugins)(config);
|
||||
if (cordovaPlugins.length > 0) {
|
||||
await copyPluginsNativeFiles(config, cordovaPlugins);
|
||||
}
|
||||
if (!(await (0, utils_fs_1.pathExists)(config.android.webDirAbs))) {
|
||||
await (0, copy_1.copy)(config, platform);
|
||||
}
|
||||
await (0, cordova_1.handleCordovaPluginsJS)(cordovaPlugins, config, platform);
|
||||
await (0, cordova_1.checkPluginDependencies)(plugins, platform);
|
||||
await installGradlePlugins(config, capacitorPlugins, cordovaPlugins);
|
||||
await handleCordovaPluginsGradle(config, cordovaPlugins);
|
||||
await (0, cordova_1.writeCordovaAndroidManifest)(cordovaPlugins, config, platform);
|
||||
const incompatibleCordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 2 /* PluginType.Incompatible */);
|
||||
(0, plugin_1.printPlugins)(incompatibleCordovaPlugins, platform, 'incompatible');
|
||||
await (0, common_1.checkPlatformVersions)(config, platform);
|
||||
}
|
||||
exports.updateAndroid = updateAndroid;
|
||||
function getGradlePackageName(id) {
|
||||
return id.replace('@', '').replace('/', '-');
|
||||
}
|
||||
async function writePluginsJson(config, plugins) {
|
||||
const classes = await findAndroidPluginClasses(plugins);
|
||||
const pluginsJsonPath = (0, path_1.resolve)(config.android.assetsDirAbs, 'capacitor.plugins.json');
|
||||
await (0, utils_fs_1.writeJSON)(pluginsJsonPath, classes, { spaces: '\t' });
|
||||
}
|
||||
async function findAndroidPluginClasses(plugins) {
|
||||
const entries = [];
|
||||
for (const plugin of plugins) {
|
||||
entries.push(...(await findAndroidPluginClassesInPlugin(plugin)));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
async function findAndroidPluginClassesInPlugin(plugin) {
|
||||
if (!plugin.android || (0, plugin_1.getPluginType)(plugin, platform) !== 0 /* PluginType.Core */) {
|
||||
return [];
|
||||
}
|
||||
const srcPath = (0, path_1.resolve)(plugin.rootPath, plugin.android.path, 'src/main');
|
||||
const srcFiles = await (0, utils_fs_1.readdirp)(srcPath, {
|
||||
filter: entry => !entry.stats.isDirectory() &&
|
||||
['.java', '.kt'].includes((0, path_1.extname)(entry.path)),
|
||||
});
|
||||
const classRegex = /^@(?:CapacitorPlugin|NativePlugin)[\s\S]+?class ([\w]+)/gm;
|
||||
const packageRegex = /^package ([\w.]+);?$/gm;
|
||||
debug('Searching %O source files in %O by %O regex', srcFiles.length, srcPath, classRegex);
|
||||
const entries = await Promise.all(srcFiles.map(async (srcFile) => {
|
||||
const srcFileContents = await (0, utils_fs_1.readFile)(srcFile, { encoding: 'utf-8' });
|
||||
classRegex.lastIndex = 0;
|
||||
const classMatch = classRegex.exec(srcFileContents);
|
||||
if (classMatch) {
|
||||
const className = classMatch[1];
|
||||
debug('Searching %O for package by %O regex', srcFile, packageRegex);
|
||||
packageRegex.lastIndex = 0;
|
||||
const packageMatch = packageRegex.exec(srcFileContents.substring(0, classMatch.index));
|
||||
if (!packageMatch) {
|
||||
(0, errors_1.fatal)(`Package could not be parsed from Android plugin.\n` +
|
||||
`Location: ${colors_1.default.strong(srcFile)}`);
|
||||
}
|
||||
const packageName = packageMatch[1];
|
||||
const classpath = `${packageName}.${className}`;
|
||||
debug('%O is a suitable plugin class', classpath);
|
||||
return {
|
||||
pkg: plugin.id,
|
||||
classpath,
|
||||
};
|
||||
}
|
||||
}));
|
||||
return entries.filter((entry) => !!entry);
|
||||
}
|
||||
async function installGradlePlugins(config, capacitorPlugins, cordovaPlugins) {
|
||||
const capacitorAndroidPackagePath = (0, node_1.resolveNode)(config.app.rootDir, '@capacitor/android', 'package.json');
|
||||
if (!capacitorAndroidPackagePath) {
|
||||
(0, errors_1.fatal)(`Unable to find ${colors_1.default.strong('node_modules/@capacitor/android')}.\n` +
|
||||
`Are you sure ${colors_1.default.strong('@capacitor/android')} is installed?`);
|
||||
}
|
||||
const capacitorAndroidPath = (0, path_1.resolve)((0, path_1.dirname)(capacitorAndroidPackagePath), 'capacitor');
|
||||
const settingsPath = config.android.platformDirAbs;
|
||||
const dependencyPath = config.android.appDirAbs;
|
||||
const relativeCapcitorAndroidPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(settingsPath, capacitorAndroidPath));
|
||||
const settingsLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
include ':capacitor-android'
|
||||
project(':capacitor-android').projectDir = new File('${relativeCapcitorAndroidPath}')
|
||||
${capacitorPlugins
|
||||
.map(p => {
|
||||
if (!p.android) {
|
||||
return '';
|
||||
}
|
||||
const relativePluginPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(settingsPath, p.rootPath));
|
||||
return `
|
||||
include ':${getGradlePackageName(p.id)}'
|
||||
project(':${getGradlePackageName(p.id)}').projectDir = new File('${relativePluginPath}/${p.android.path}')
|
||||
`;
|
||||
})
|
||||
.join('')}`;
|
||||
const applyArray = [];
|
||||
const frameworksArray = [];
|
||||
let prefsArray = [];
|
||||
cordovaPlugins.map(p => {
|
||||
const relativePluginPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(dependencyPath, p.rootPath));
|
||||
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
|
||||
frameworks.map((framework) => {
|
||||
if (framework.$.custom &&
|
||||
framework.$.custom === 'true' &&
|
||||
framework.$.type &&
|
||||
framework.$.type === 'gradleReference') {
|
||||
applyArray.push(`apply from: "${relativePluginPath}/${framework.$.src}"`);
|
||||
}
|
||||
else if (!framework.$.type && !framework.$.custom) {
|
||||
if (framework.$.src.startsWith('platform(')) {
|
||||
frameworksArray.push(` implementation ${framework.$.src}`);
|
||||
}
|
||||
else {
|
||||
frameworksArray.push(` implementation "${framework.$.src}"`);
|
||||
}
|
||||
}
|
||||
});
|
||||
prefsArray = prefsArray.concat((0, plugin_1.getAllElements)(p, platform, 'preference'));
|
||||
});
|
||||
let frameworkString = frameworksArray.join('\n');
|
||||
frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString);
|
||||
const dependencyLines = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||
dependencies {
|
||||
${capacitorPlugins
|
||||
.map(p => {
|
||||
return ` implementation project(':${getGradlePackageName(p.id)}')`;
|
||||
})
|
||||
.join('\n')}
|
||||
${frameworkString}
|
||||
}
|
||||
${applyArray.join('\n')}
|
||||
|
||||
if (hasProperty('postBuildExtras')) {
|
||||
postBuildExtras()
|
||||
}
|
||||
`;
|
||||
await (0, utils_fs_1.writeFile)((0, path_1.join)(settingsPath, 'capacitor.settings.gradle'), settingsLines);
|
||||
await (0, utils_fs_1.writeFile)((0, path_1.join)(dependencyPath, 'capacitor.build.gradle'), dependencyLines);
|
||||
}
|
||||
exports.installGradlePlugins = installGradlePlugins;
|
||||
async function handleCordovaPluginsGradle(config, cordovaPlugins) {
|
||||
var _a, _b, _c;
|
||||
const pluginsGradlePath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'build.gradle');
|
||||
const kotlinNeeded = await kotlinNeededCheck(config, cordovaPlugins);
|
||||
const kotlinVersionString = (_c = (_b = (_a = config.app.extConfig.cordova) === null || _a === void 0 ? void 0 : _a.preferences) === null || _b === void 0 ? void 0 : _b.GradlePluginKotlinVersion) !== null && _c !== void 0 ? _c : '1.9.10';
|
||||
const frameworksArray = [];
|
||||
let prefsArray = [];
|
||||
const applyArray = [];
|
||||
applyArray.push(`apply from: "cordova.variables.gradle"`);
|
||||
cordovaPlugins.map(p => {
|
||||
const relativePluginPath = (0, fs_1.convertToUnixPath)((0, path_1.relative)(config.android.cordovaPluginsDirAbs, p.rootPath));
|
||||
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
|
||||
frameworks.map((framework) => {
|
||||
if (!framework.$.type && !framework.$.custom) {
|
||||
frameworksArray.push(framework.$.src);
|
||||
}
|
||||
else if (framework.$.custom &&
|
||||
framework.$.custom === 'true' &&
|
||||
framework.$.type &&
|
||||
framework.$.type === 'gradleReference') {
|
||||
applyArray.push(`apply from: "${relativePluginPath}/${framework.$.src}"`);
|
||||
}
|
||||
});
|
||||
prefsArray = prefsArray.concat((0, plugin_1.getAllElements)(p, platform, 'preference'));
|
||||
});
|
||||
let frameworkString = frameworksArray
|
||||
.map(f => {
|
||||
if (f.startsWith('platform(')) {
|
||||
return ` implementation ${f}`;
|
||||
}
|
||||
else {
|
||||
return ` implementation "${f}"`;
|
||||
}
|
||||
})
|
||||
.join('\n');
|
||||
frameworkString = await replaceFrameworkVariables(config, prefsArray, frameworkString);
|
||||
if (kotlinNeeded) {
|
||||
frameworkString += `\n implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"`;
|
||||
frameworkString += `\n implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"`;
|
||||
}
|
||||
const applyString = applyArray.join('\n');
|
||||
let buildGradle = await (0, utils_fs_1.readFile)(pluginsGradlePath, { encoding: 'utf-8' });
|
||||
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + frameworkString.concat('\n') + ' $2');
|
||||
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + applyString.concat('\n') + '$2');
|
||||
if (kotlinNeeded) {
|
||||
buildGradle = buildGradle.replace(/(buildscript\s{\n(\t|\s{4})repositories\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})}\n(\t|\s{4})dependencies\s{\n(\t{2}|\s{8}).+)\n((\t|\s{4})}\n}\n)/, `$1\n classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"\n$8`);
|
||||
buildGradle = buildGradle.replace(/(ext\s{)/, `$1\n androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.8.0'`);
|
||||
buildGradle = buildGradle.replace(/(buildscript\s{)/, `$1\n ext.kotlin_version = project.hasProperty('kotlin_version') ? rootProject.ext.kotlin_version : '${kotlinVersionString}'`);
|
||||
buildGradle = buildGradle.replace(/(apply\splugin:\s'com\.android\.library')/, `$1\napply plugin: 'kotlin-android'`);
|
||||
buildGradle = buildGradle.replace(/(compileOptions\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})})\n(})/, `$1\n sourceSets {\n main.java.srcDirs += 'src/main/kotlin'\n }\n$5`);
|
||||
}
|
||||
await (0, utils_fs_1.writeFile)(pluginsGradlePath, buildGradle);
|
||||
const cordovaVariables = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
ext {
|
||||
cdvMinSdkVersion = project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : ${config.android.minVersion}
|
||||
// Plugin gradle extensions can append to this to have code run at the end.
|
||||
cdvPluginPostBuildExtras = []
|
||||
cordovaConfig = [:]
|
||||
}`;
|
||||
await (0, utils_fs_1.writeFile)((0, path_1.join)(config.android.cordovaPluginsDirAbs, 'cordova.variables.gradle'), cordovaVariables);
|
||||
}
|
||||
exports.handleCordovaPluginsGradle = handleCordovaPluginsGradle;
|
||||
async function kotlinNeededCheck(config, cordovaPlugins) {
|
||||
var _a, _b;
|
||||
if (((_b = (_a = config.app.extConfig.cordova) === null || _a === void 0 ? void 0 : _a.preferences) === null || _b === void 0 ? void 0 : _b.GradlePluginKotlinEnabled) !==
|
||||
'true') {
|
||||
for (const plugin of cordovaPlugins) {
|
||||
const androidPlatform = (0, plugin_1.getPluginPlatform)(plugin, platform);
|
||||
const sourceFiles = androidPlatform['source-file'];
|
||||
if (sourceFiles) {
|
||||
for (const srcFile of sourceFiles) {
|
||||
if (/^.*\.kt$/.test(srcFile['$'].src)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
||||
const pluginsPath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'src', 'main');
|
||||
for (const p of cordovaPlugins) {
|
||||
const androidPlatform = (0, plugin_1.getPluginPlatform)(p, platform);
|
||||
if (androidPlatform) {
|
||||
const sourceFiles = androidPlatform['source-file'];
|
||||
if (sourceFiles) {
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const fileName = sourceFile.$.src.split('/').pop();
|
||||
let baseFolder = 'java/';
|
||||
if (fileName.split('.').pop() === 'aidl') {
|
||||
baseFolder = 'aidl/';
|
||||
}
|
||||
const target = sourceFile.$['target-dir']
|
||||
.replace('app/src/main/', '')
|
||||
.replace('src/', baseFolder);
|
||||
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, sourceFile.$.src), (0, path_1.join)(pluginsPath, target, fileName));
|
||||
}
|
||||
}
|
||||
const resourceFiles = androidPlatform['resource-file'];
|
||||
if (resourceFiles) {
|
||||
for (const resourceFile of resourceFiles) {
|
||||
const target = resourceFile.$['target'];
|
||||
if (resourceFile.$.src.split('.').pop() === 'aar') {
|
||||
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, resourceFile.$.src), (0, path_1.join)(pluginsPath, 'libs', target.split('/').pop()));
|
||||
}
|
||||
else if (target !== '.') {
|
||||
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, resourceFile.$.src), (0, path_1.join)(pluginsPath, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
const libFiles = (0, plugin_1.getPlatformElement)(p, platform, 'lib-file');
|
||||
for (const libFile of libFiles) {
|
||||
await (0, utils_fs_1.copy)((0, plugin_1.getFilePath)(config, p, libFile.$.src), (0, path_1.join)(pluginsPath, 'libs', libFile.$.src.split('/').pop()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async function removePluginsNativeFiles(config) {
|
||||
await (0, utils_fs_1.remove)(config.android.cordovaPluginsDirAbs);
|
||||
await (0, template_1.extractTemplate)(config.cli.assets.android.cordovaPluginsTemplateArchiveAbs, config.android.cordovaPluginsDirAbs);
|
||||
}
|
||||
async function getPluginsTask(config) {
|
||||
return await (0, common_1.runTask)('Updating Android plugins', async () => {
|
||||
const allPlugins = await (0, plugin_1.getPlugins)(config, 'android');
|
||||
const androidPlugins = await (0, common_2.getAndroidPlugins)(allPlugins);
|
||||
return androidPlugins;
|
||||
});
|
||||
}
|
||||
async function getVariablesGradleFile(config) {
|
||||
const variablesFile = (0, path_1.resolve)(config.android.platformDirAbs, 'variables.gradle');
|
||||
let variablesGradle = '';
|
||||
if (await (0, utils_fs_1.pathExists)(variablesFile)) {
|
||||
variablesGradle = await (0, utils_fs_1.readFile)(variablesFile, { encoding: 'utf-8' });
|
||||
}
|
||||
return variablesGradle;
|
||||
}
|
||||
async function replaceFrameworkVariables(config, prefsArray, frameworkString) {
|
||||
const variablesGradle = await getVariablesGradleFile(config);
|
||||
prefsArray.map((preference) => {
|
||||
if (!variablesGradle.includes(preference.$.name)) {
|
||||
frameworkString = frameworkString.replace(new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), preference.$.default);
|
||||
}
|
||||
});
|
||||
return frameworkString;
|
||||
}
|
||||
26
node_modules/@capacitor/cli/dist/colors.js
generated
vendored
Normal file
26
node_modules/@capacitor/cli/dist/colors.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ancillary = exports.failure = exports.success = exports.input = exports.weak = exports.strong = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const kleur_1 = tslib_1.__importDefault(require("kleur"));
|
||||
exports.strong = kleur_1.default.bold;
|
||||
exports.weak = kleur_1.default.dim;
|
||||
exports.input = kleur_1.default.cyan;
|
||||
exports.success = kleur_1.default.green;
|
||||
exports.failure = kleur_1.default.red;
|
||||
exports.ancillary = kleur_1.default.cyan;
|
||||
const COLORS = {
|
||||
strong: exports.strong,
|
||||
weak: exports.weak,
|
||||
input: exports.input,
|
||||
success: exports.success,
|
||||
failure: exports.failure,
|
||||
ancillary: exports.ancillary,
|
||||
log: {
|
||||
DEBUG: kleur_1.default.magenta,
|
||||
INFO: kleur_1.default.cyan,
|
||||
WARN: kleur_1.default.yellow,
|
||||
ERROR: kleur_1.default.red,
|
||||
},
|
||||
};
|
||||
exports.default = COLORS;
|
||||
407
node_modules/@capacitor/cli/dist/common.js
generated
vendored
Normal file
407
node_modules/@capacitor/cli/dist/common.js
generated
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseApkNameFromFlavor = exports.checkJDKMajorVersion = exports.resolvePlatform = exports.checkPlatformVersions = exports.getAddedPlatforms = exports.getPlatformTargetName = exports.promptForPlatformTarget = exports.promptForPlatform = exports.isValidEnterprisePlatform = exports.getKnownEnterprisePlatforms = exports.isValidCommunityPlatform = exports.getKnownCommunityPlatforms = exports.isValidPlatform = exports.getKnownPlatforms = exports.selectPlatforms = exports.getProjectPlatformDirectory = exports.getCLIVersion = exports.getCoreVersion = exports.getCapacitorPackageVersion = exports.requireCapacitorPackage = exports.getCapacitorPackage = exports.runTask = exports.runPlatformHook = exports.runHooks = exports.wait = exports.checkAppName = exports.checkAppId = exports.checkAppDir = exports.checkAppConfig = exports.checkCapacitorPlatform = exports.checkPackage = exports.checkWebDir = exports.check = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const utils_terminal_1 = require("@ionic/utils-terminal");
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("./colors"));
|
||||
const errors_1 = require("./errors");
|
||||
const log_1 = require("./log");
|
||||
const plugin_1 = require("./plugin");
|
||||
const monorepotools_1 = require("./util/monorepotools");
|
||||
const node_1 = require("./util/node");
|
||||
const subprocess_1 = require("./util/subprocess");
|
||||
async function check(checks) {
|
||||
const results = await Promise.all(checks.map(f => f()));
|
||||
const errors = results.filter(r => r != null);
|
||||
if (errors.length > 0) {
|
||||
throw errors.join('\n');
|
||||
}
|
||||
}
|
||||
exports.check = check;
|
||||
async function checkWebDir(config) {
|
||||
var _a;
|
||||
// We can skip checking the web dir if a server URL is set.
|
||||
if ((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.url) {
|
||||
return null;
|
||||
}
|
||||
const invalidFolders = ['', '.', '..', '../', './'];
|
||||
if (invalidFolders.includes(config.app.webDir)) {
|
||||
return `"${config.app.webDir}" is not a valid value for webDir`;
|
||||
}
|
||||
if (!(await (0, utils_fs_1.pathExists)(config.app.webDirAbs))) {
|
||||
return (`Could not find the web assets directory: ${colors_1.default.strong((0, utils_terminal_1.prettyPath)(config.app.webDirAbs))}.\n` +
|
||||
`Please create it and make sure it has an ${colors_1.default.strong('index.html')} file. You can change the path of this directory in ${colors_1.default.strong(config.app.extConfigName)} (${colors_1.default.input('webDir')} option). You may need to compile the web assets for your app (typically ${colors_1.default.input('npm run build')}). More info: ${colors_1.default.strong('https://capacitorjs.com/docs/basics/workflow#sync-your-project')}`);
|
||||
}
|
||||
if (!(await (0, utils_fs_1.pathExists)((0, path_1.join)(config.app.webDirAbs, 'index.html')))) {
|
||||
return (`The web assets directory (${colors_1.default.strong((0, utils_terminal_1.prettyPath)(config.app.webDirAbs))}) must contain an ${colors_1.default.strong('index.html')} file.\n` +
|
||||
`It will be the entry point for the web portion of the Capacitor app.`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.checkWebDir = checkWebDir;
|
||||
async function checkPackage() {
|
||||
if (!(await (0, utils_fs_1.pathExists)('package.json'))) {
|
||||
if (await (0, utils_fs_1.pathExists)('project.json')) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return (`The Capacitor CLI needs to run at the root of an npm package or in a valid NX monorepo.\n` +
|
||||
`Make sure you have a package.json or project.json file in the directory where you run the Capacitor CLI.\n` +
|
||||
`More info: ${colors_1.default.strong('https://docs.npmjs.com/cli/init')}`);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.checkPackage = checkPackage;
|
||||
async function checkCapacitorPlatform(config, platform) {
|
||||
const pkg = await getCapacitorPackage(config, platform);
|
||||
if (!pkg) {
|
||||
return (`Could not find the ${colors_1.default.input(platform)} platform.\n` +
|
||||
`You must install it in your project first, e.g. w/ ${colors_1.default.input(`npm install @capacitor/${platform}`)}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.checkCapacitorPlatform = checkCapacitorPlatform;
|
||||
async function checkAppConfig(config) {
|
||||
if (!config.app.appId) {
|
||||
return (`Missing ${colors_1.default.input('appId')} for new platform.\n` +
|
||||
`Please add it in ${config.app.extConfigName} or run ${colors_1.default.input('npx cap init')}.`);
|
||||
}
|
||||
if (!config.app.appName) {
|
||||
return (`Missing ${colors_1.default.input('appName')} for new platform.\n` +
|
||||
`Please add it in ${config.app.extConfigName} or run ${colors_1.default.input('npx cap init')}.`);
|
||||
}
|
||||
const appIdError = await checkAppId(config, config.app.appId);
|
||||
if (appIdError) {
|
||||
return appIdError;
|
||||
}
|
||||
const appNameError = await checkAppName(config, config.app.appName);
|
||||
if (appNameError) {
|
||||
return appNameError;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.checkAppConfig = checkAppConfig;
|
||||
async function checkAppDir(config, dir) {
|
||||
if (!/^\S*$/.test(dir)) {
|
||||
return `Your app directory should not contain spaces`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.checkAppDir = checkAppDir;
|
||||
async function checkAppId(config, id) {
|
||||
if (!id) {
|
||||
return `Invalid App ID. Must be in Java package form with no dashes (ex: com.example.app)`;
|
||||
}
|
||||
if (/^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$/.test(id.toLowerCase())) {
|
||||
return null;
|
||||
}
|
||||
return `Invalid App ID "${id}". Must be in Java package form with no dashes (ex: com.example.app)`;
|
||||
}
|
||||
exports.checkAppId = checkAppId;
|
||||
async function checkAppName(config, name) {
|
||||
// We allow pretty much anything right now, have fun
|
||||
if (!(name === null || name === void 0 ? void 0 : name.length)) {
|
||||
return `Must provide an app name. For example: 'Spacebook'`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.checkAppName = checkAppName;
|
||||
async function wait(time) {
|
||||
return new Promise(resolve => setTimeout(resolve, time));
|
||||
}
|
||||
exports.wait = wait;
|
||||
async function runHooks(config, platformName, dir, hook) {
|
||||
await runPlatformHook(config, platformName, dir, hook);
|
||||
const allPlugins = await (0, plugin_1.getPlugins)(config, platformName);
|
||||
allPlugins.forEach(async (p) => {
|
||||
await runPlatformHook(config, platformName, p.rootPath, hook);
|
||||
});
|
||||
}
|
||||
exports.runHooks = runHooks;
|
||||
async function runPlatformHook(config, platformName, platformDir, hook) {
|
||||
var _a;
|
||||
const { spawn } = await Promise.resolve().then(() => tslib_1.__importStar(require('child_process')));
|
||||
let pkg;
|
||||
if ((0, monorepotools_1.isNXMonorepo)(platformDir)) {
|
||||
pkg = await (0, utils_fs_1.readJSON)((0, path_1.join)((0, monorepotools_1.findNXMonorepoRoot)(platformDir), 'package.json'));
|
||||
}
|
||||
else {
|
||||
pkg = await (0, utils_fs_1.readJSON)((0, path_1.join)(platformDir, 'package.json'));
|
||||
}
|
||||
const cmd = (_a = pkg.scripts) === null || _a === void 0 ? void 0 : _a[hook];
|
||||
if (!cmd) {
|
||||
return;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const p = spawn(cmd, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
cwd: platformDir,
|
||||
env: {
|
||||
INIT_CWD: platformDir,
|
||||
CAPACITOR_ROOT_DIR: config.app.rootDir,
|
||||
CAPACITOR_WEB_DIR: config.app.webDirAbs,
|
||||
CAPACITOR_CONFIG: JSON.stringify(config.app.extConfig),
|
||||
CAPACITOR_PLATFORM_NAME: platformName,
|
||||
...process.env,
|
||||
},
|
||||
});
|
||||
p.on('close', () => {
|
||||
resolve();
|
||||
});
|
||||
p.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.runPlatformHook = runPlatformHook;
|
||||
async function runTask(title, fn) {
|
||||
const chain = log_1.output.createTaskChain();
|
||||
chain.next(title);
|
||||
try {
|
||||
const value = await fn();
|
||||
chain.end();
|
||||
return value;
|
||||
}
|
||||
catch (e) {
|
||||
chain.fail();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
exports.runTask = runTask;
|
||||
async function getCapacitorPackage(config, name) {
|
||||
const packagePath = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor/${name}`, 'package.json');
|
||||
if (!packagePath) {
|
||||
return null;
|
||||
}
|
||||
return (0, utils_fs_1.readJSON)(packagePath);
|
||||
}
|
||||
exports.getCapacitorPackage = getCapacitorPackage;
|
||||
async function requireCapacitorPackage(config, name) {
|
||||
const pkg = await getCapacitorPackage(config, name);
|
||||
if (!pkg) {
|
||||
(0, errors_1.fatal)(`Unable to find node_modules/@capacitor/${name}.\n` +
|
||||
`Are you sure ${colors_1.default.strong(`@capacitor/${name}`)} is installed?`);
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
exports.requireCapacitorPackage = requireCapacitorPackage;
|
||||
async function getCapacitorPackageVersion(config, platform) {
|
||||
return (await requireCapacitorPackage(config, platform)).version;
|
||||
}
|
||||
exports.getCapacitorPackageVersion = getCapacitorPackageVersion;
|
||||
async function getCoreVersion(config) {
|
||||
return getCapacitorPackageVersion(config, 'core');
|
||||
}
|
||||
exports.getCoreVersion = getCoreVersion;
|
||||
async function getCLIVersion(config) {
|
||||
return getCapacitorPackageVersion(config, 'cli');
|
||||
}
|
||||
exports.getCLIVersion = getCLIVersion;
|
||||
function getPlatformDirectory(config, platform) {
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
return config.android.platformDirAbs;
|
||||
case 'ios':
|
||||
return config.ios.platformDirAbs;
|
||||
case 'web':
|
||||
return config.web.platformDirAbs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function getProjectPlatformDirectory(config, platform) {
|
||||
const platformPath = getPlatformDirectory(config, platform);
|
||||
if (platformPath && (await (0, utils_fs_1.pathExists)(platformPath))) {
|
||||
return platformPath;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.getProjectPlatformDirectory = getProjectPlatformDirectory;
|
||||
async function selectPlatforms(config, selectedPlatformName) {
|
||||
if (selectedPlatformName) {
|
||||
// already passed in a platform name
|
||||
const platformName = selectedPlatformName.toLowerCase().trim();
|
||||
if (!(await isValidPlatform(platformName))) {
|
||||
(0, errors_1.fatal)(`Invalid platform: ${colors_1.default.input(platformName)}`);
|
||||
}
|
||||
else if (!(await getProjectPlatformDirectory(config, platformName))) {
|
||||
if (platformName === 'web') {
|
||||
(0, errors_1.fatal)(`Could not find the web platform directory.\n` +
|
||||
`Make sure ${colors_1.default.strong(config.app.webDir)} exists.`);
|
||||
}
|
||||
(0, errors_1.fatal)(`${colors_1.default.strong(platformName)} platform has not been added yet.\n` +
|
||||
`See the docs for adding the ${colors_1.default.strong(platformName)} platform: ${colors_1.default.strong(`https://capacitorjs.com/docs/${platformName}#adding-the-${platformName}-platform`)}`);
|
||||
}
|
||||
// return the platform in an string array
|
||||
return [platformName];
|
||||
}
|
||||
// wasn't given a platform name, so let's
|
||||
// get the platforms that have already been created
|
||||
return getAddedPlatforms(config);
|
||||
}
|
||||
exports.selectPlatforms = selectPlatforms;
|
||||
async function getKnownPlatforms() {
|
||||
return ['web', 'android', 'ios'];
|
||||
}
|
||||
exports.getKnownPlatforms = getKnownPlatforms;
|
||||
async function isValidPlatform(platform) {
|
||||
return (await getKnownPlatforms()).includes(platform);
|
||||
}
|
||||
exports.isValidPlatform = isValidPlatform;
|
||||
async function getKnownCommunityPlatforms() {
|
||||
return ['electron'];
|
||||
}
|
||||
exports.getKnownCommunityPlatforms = getKnownCommunityPlatforms;
|
||||
async function isValidCommunityPlatform(platform) {
|
||||
return (await getKnownCommunityPlatforms()).includes(platform);
|
||||
}
|
||||
exports.isValidCommunityPlatform = isValidCommunityPlatform;
|
||||
async function getKnownEnterprisePlatforms() {
|
||||
return ['windows'];
|
||||
}
|
||||
exports.getKnownEnterprisePlatforms = getKnownEnterprisePlatforms;
|
||||
async function isValidEnterprisePlatform(platform) {
|
||||
return (await getKnownEnterprisePlatforms()).includes(platform);
|
||||
}
|
||||
exports.isValidEnterprisePlatform = isValidEnterprisePlatform;
|
||||
async function promptForPlatform(platforms, promptMessage, selectedPlatformName) {
|
||||
const { prompt } = await Promise.resolve().then(() => tslib_1.__importStar(require('prompts')));
|
||||
if (!selectedPlatformName) {
|
||||
const answers = await prompt([
|
||||
{
|
||||
type: 'select',
|
||||
name: 'mode',
|
||||
message: promptMessage,
|
||||
choices: platforms.map(p => ({ title: p, value: p })),
|
||||
},
|
||||
], { onCancel: () => process.exit(1) });
|
||||
return answers.mode.toLowerCase().trim();
|
||||
}
|
||||
const platformName = selectedPlatformName.toLowerCase().trim();
|
||||
if (!(await isValidPlatform(platformName))) {
|
||||
const knownPlatforms = await getKnownPlatforms();
|
||||
(0, errors_1.fatal)(`Invalid platform: ${colors_1.default.input(platformName)}.\n` +
|
||||
`Valid platforms include: ${knownPlatforms.join(', ')}`);
|
||||
}
|
||||
return platformName;
|
||||
}
|
||||
exports.promptForPlatform = promptForPlatform;
|
||||
async function promptForPlatformTarget(targets, selectedTarget) {
|
||||
const { prompt } = await Promise.resolve().then(() => tslib_1.__importStar(require('prompts')));
|
||||
const validTargets = targets.filter(t => t.id !== undefined);
|
||||
if (!selectedTarget) {
|
||||
if (validTargets.length === 1) {
|
||||
return validTargets[0];
|
||||
}
|
||||
else {
|
||||
const answers = await prompt([
|
||||
{
|
||||
type: 'select',
|
||||
name: 'target',
|
||||
message: 'Please choose a target device:',
|
||||
choices: validTargets.map(t => ({
|
||||
title: `${getPlatformTargetName(t)} (${t.id})`,
|
||||
value: t,
|
||||
})),
|
||||
},
|
||||
], { onCancel: () => process.exit(1) });
|
||||
return answers.target;
|
||||
}
|
||||
}
|
||||
const targetID = selectedTarget.trim();
|
||||
const target = targets.find(t => t.id === targetID);
|
||||
if (!target) {
|
||||
(0, errors_1.fatal)(`Invalid target ID: ${colors_1.default.input(targetID)}.\n` +
|
||||
`Valid targets are: ${targets.map(t => t.id).join(', ')}`);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
exports.promptForPlatformTarget = promptForPlatformTarget;
|
||||
function getPlatformTargetName(target) {
|
||||
var _a, _b, _c;
|
||||
return `${(_c = (_b = (_a = target.name) !== null && _a !== void 0 ? _a : target.model) !== null && _b !== void 0 ? _b : target.id) !== null && _c !== void 0 ? _c : '?'}${target.virtual
|
||||
? ` (${target.platform === 'ios' ? 'simulator' : 'emulator'})`
|
||||
: ''}`;
|
||||
}
|
||||
exports.getPlatformTargetName = getPlatformTargetName;
|
||||
async function getAddedPlatforms(config) {
|
||||
const platforms = [];
|
||||
if (await getProjectPlatformDirectory(config, config.android.name)) {
|
||||
platforms.push(config.android.name);
|
||||
}
|
||||
if (await getProjectPlatformDirectory(config, config.ios.name)) {
|
||||
platforms.push(config.ios.name);
|
||||
}
|
||||
platforms.push(config.web.name);
|
||||
return platforms;
|
||||
}
|
||||
exports.getAddedPlatforms = getAddedPlatforms;
|
||||
async function checkPlatformVersions(config, platform) {
|
||||
const semver = await Promise.resolve().then(() => tslib_1.__importStar(require('semver')));
|
||||
const coreVersion = await getCoreVersion(config);
|
||||
const platformVersion = await getCapacitorPackageVersion(config, platform);
|
||||
if (semver.diff(coreVersion, platformVersion) === 'minor' ||
|
||||
semver.diff(coreVersion, platformVersion) === 'major') {
|
||||
log_1.logger.warn(`${colors_1.default.strong('@capacitor/core')}${colors_1.default.weak(`@${coreVersion}`)} version doesn't match ${colors_1.default.strong(`@capacitor/${platform}`)}${colors_1.default.weak(`@${platformVersion}`)} version.\n` +
|
||||
`Consider updating to a matching version, e.g. w/ ${colors_1.default.input(`npm install @capacitor/core@${platformVersion}`)}`);
|
||||
}
|
||||
}
|
||||
exports.checkPlatformVersions = checkPlatformVersions;
|
||||
function resolvePlatform(config, platform) {
|
||||
if (platform[0] !== '@') {
|
||||
const core = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor/${platform}`, 'package.json');
|
||||
if (core) {
|
||||
return (0, path_1.dirname)(core);
|
||||
}
|
||||
const community = (0, node_1.resolveNode)(config.app.rootDir, `@capacitor-community/${platform}`, 'package.json');
|
||||
if (community) {
|
||||
return (0, path_1.dirname)(community);
|
||||
}
|
||||
const enterprise = (0, node_1.resolveNode)(config.app.rootDir, `@ionic-enterprise/capacitor-${platform}`, 'package.json');
|
||||
if (enterprise) {
|
||||
return (0, path_1.dirname)(enterprise);
|
||||
}
|
||||
}
|
||||
// third-party
|
||||
const thirdParty = (0, node_1.resolveNode)(config.app.rootDir, platform, 'package.json');
|
||||
if (thirdParty) {
|
||||
return (0, path_1.dirname)(thirdParty);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.resolvePlatform = resolvePlatform;
|
||||
async function checkJDKMajorVersion() {
|
||||
try {
|
||||
const string = await (0, subprocess_1.runCommand)('java', ['--version']);
|
||||
const versionRegex = RegExp(/([0-9]+)\.?([0-9]*)\.?([0-9]*)/);
|
||||
const versionMatch = versionRegex.exec(string);
|
||||
if (versionMatch === null) {
|
||||
return -1;
|
||||
}
|
||||
const firstVersionNumber = parseInt(versionMatch[1]);
|
||||
const secondVersionNumber = parseInt(versionMatch[2]);
|
||||
if (typeof firstVersionNumber === 'number' && firstVersionNumber != 1) {
|
||||
return firstVersionNumber;
|
||||
}
|
||||
else if (typeof secondVersionNumber === 'number' &&
|
||||
firstVersionNumber == 1 &&
|
||||
secondVersionNumber < 9) {
|
||||
return secondVersionNumber;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
exports.checkJDKMajorVersion = checkJDKMajorVersion;
|
||||
function parseApkNameFromFlavor(flavor) {
|
||||
const convertedName = flavor.replace(/([A-Z])/g, '$1').toLowerCase();
|
||||
return `app-${convertedName ? `${convertedName}-` : ''}debug.apk`;
|
||||
}
|
||||
exports.parseApkNameFromFlavor = parseApkNameFromFlavor;
|
||||
398
node_modules/@capacitor/cli/dist/config.js
generated
vendored
Normal file
398
node_modules/@capacitor/cli/dist/config.js
generated
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.checkExternalConfig = exports.writeConfig = exports.loadConfig = exports.CONFIG_FILE_NAME_JSON = exports.CONFIG_FILE_NAME_JS = exports.CONFIG_FILE_NAME_TS = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const debug_1 = tslib_1.__importDefault(require("debug"));
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("./colors"));
|
||||
const common_1 = require("./common");
|
||||
const errors_1 = require("./errors");
|
||||
const log_1 = require("./log");
|
||||
const fn_1 = require("./util/fn");
|
||||
const js_1 = require("./util/js");
|
||||
const monorepotools_1 = require("./util/monorepotools");
|
||||
const node_1 = require("./util/node");
|
||||
const promise_1 = require("./util/promise");
|
||||
const subprocess_1 = require("./util/subprocess");
|
||||
const debug = (0, debug_1.default)('capacitor:config');
|
||||
exports.CONFIG_FILE_NAME_TS = 'capacitor.config.ts';
|
||||
exports.CONFIG_FILE_NAME_JS = 'capacitor.config.js';
|
||||
exports.CONFIG_FILE_NAME_JSON = 'capacitor.config.json';
|
||||
async function loadConfig() {
|
||||
var _a, _b, _c, _d;
|
||||
const appRootDir = process.cwd();
|
||||
const cliRootDir = (0, path_1.dirname)(__dirname);
|
||||
const conf = await loadExtConfig(appRootDir);
|
||||
const depsForNx = await (async () => {
|
||||
var _a, _b;
|
||||
if ((0, monorepotools_1.isNXMonorepo)(appRootDir)) {
|
||||
const rootOfNXMonorepo = (0, monorepotools_1.findNXMonorepoRoot)(appRootDir);
|
||||
const pkgJSONOfMonorepoRoot = await (0, fn_1.tryFn)(utils_fs_1.readJSON, (0, path_1.resolve)(rootOfNXMonorepo, 'package.json'));
|
||||
const devDependencies = (_a = pkgJSONOfMonorepoRoot === null || pkgJSONOfMonorepoRoot === void 0 ? void 0 : pkgJSONOfMonorepoRoot.devDependencies) !== null && _a !== void 0 ? _a : {};
|
||||
const dependencies = (_b = pkgJSONOfMonorepoRoot === null || pkgJSONOfMonorepoRoot === void 0 ? void 0 : pkgJSONOfMonorepoRoot.dependencies) !== null && _b !== void 0 ? _b : {};
|
||||
return {
|
||||
devDependencies,
|
||||
dependencies,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
})();
|
||||
const appId = (_a = conf.extConfig.appId) !== null && _a !== void 0 ? _a : '';
|
||||
const appName = (_b = conf.extConfig.appName) !== null && _b !== void 0 ? _b : '';
|
||||
const webDir = (_c = conf.extConfig.webDir) !== null && _c !== void 0 ? _c : 'www';
|
||||
const cli = await loadCLIConfig(cliRootDir);
|
||||
const config = {
|
||||
android: await loadAndroidConfig(appRootDir, conf.extConfig, cli),
|
||||
ios: await loadIOSConfig(appRootDir, conf.extConfig),
|
||||
web: await loadWebConfig(appRootDir, webDir),
|
||||
cli,
|
||||
app: {
|
||||
rootDir: appRootDir,
|
||||
appId,
|
||||
appName,
|
||||
webDir,
|
||||
webDirAbs: (0, path_1.resolve)(appRootDir, webDir),
|
||||
package: (_d = (await (0, fn_1.tryFn)(utils_fs_1.readJSON, (0, path_1.resolve)(appRootDir, 'package.json')))) !== null && _d !== void 0 ? _d : {
|
||||
name: appName,
|
||||
version: '1.0.0',
|
||||
...depsForNx,
|
||||
},
|
||||
...conf,
|
||||
},
|
||||
};
|
||||
debug('config: %O', config);
|
||||
return config;
|
||||
}
|
||||
exports.loadConfig = loadConfig;
|
||||
async function writeConfig(extConfig, extConfigFilePath) {
|
||||
switch ((0, path_1.extname)(extConfigFilePath)) {
|
||||
case '.json': {
|
||||
await (0, utils_fs_1.writeJSON)(extConfigFilePath, extConfig, { spaces: 2 });
|
||||
break;
|
||||
}
|
||||
case '.ts': {
|
||||
await (0, utils_fs_1.writeFile)(extConfigFilePath, formatConfigTS(extConfig));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.writeConfig = writeConfig;
|
||||
async function loadExtConfigTS(rootDir, extConfigName, extConfigFilePath) {
|
||||
var _a;
|
||||
try {
|
||||
const tsPath = (0, node_1.resolveNode)(rootDir, 'typescript');
|
||||
if (!tsPath) {
|
||||
(0, errors_1.fatal)('Could not find installation of TypeScript.\n' +
|
||||
`To use ${colors_1.default.strong(extConfigName)} files, you must install TypeScript in your project, e.g. w/ ${colors_1.default.input('npm install -D typescript')}`);
|
||||
}
|
||||
const ts = require(tsPath); // eslint-disable-line @typescript-eslint/no-var-requires
|
||||
const extConfigObject = (0, node_1.requireTS)(ts, extConfigFilePath);
|
||||
const extConfig = extConfigObject.default
|
||||
? await extConfigObject.default
|
||||
: extConfigObject;
|
||||
return {
|
||||
extConfigType: 'ts',
|
||||
extConfigName,
|
||||
extConfigFilePath: extConfigFilePath,
|
||||
extConfig,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
if (!(0, errors_1.isFatal)(e)) {
|
||||
(0, errors_1.fatal)(`Parsing ${colors_1.default.strong(extConfigName)} failed.\n\n${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
async function loadExtConfigJS(rootDir, extConfigName, extConfigFilePath) {
|
||||
var _a;
|
||||
try {
|
||||
return {
|
||||
extConfigType: 'js',
|
||||
extConfigName,
|
||||
extConfigFilePath: extConfigFilePath,
|
||||
extConfig: await require(extConfigFilePath),
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
(0, errors_1.fatal)(`Parsing ${colors_1.default.strong(extConfigName)} failed.\n\n${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
|
||||
}
|
||||
}
|
||||
async function loadExtConfig(rootDir) {
|
||||
var _a;
|
||||
const extConfigFilePathTS = (0, path_1.resolve)(rootDir, exports.CONFIG_FILE_NAME_TS);
|
||||
if (await (0, utils_fs_1.pathExists)(extConfigFilePathTS)) {
|
||||
return loadExtConfigTS(rootDir, exports.CONFIG_FILE_NAME_TS, extConfigFilePathTS);
|
||||
}
|
||||
const extConfigFilePathJS = (0, path_1.resolve)(rootDir, exports.CONFIG_FILE_NAME_JS);
|
||||
if (await (0, utils_fs_1.pathExists)(extConfigFilePathJS)) {
|
||||
return loadExtConfigJS(rootDir, exports.CONFIG_FILE_NAME_JS, extConfigFilePathJS);
|
||||
}
|
||||
const extConfigFilePath = (0, path_1.resolve)(rootDir, exports.CONFIG_FILE_NAME_JSON);
|
||||
return {
|
||||
extConfigType: 'json',
|
||||
extConfigName: exports.CONFIG_FILE_NAME_JSON,
|
||||
extConfigFilePath: extConfigFilePath,
|
||||
extConfig: (_a = (await (0, fn_1.tryFn)(utils_fs_1.readJSON, extConfigFilePath))) !== null && _a !== void 0 ? _a : {},
|
||||
};
|
||||
}
|
||||
async function loadCLIConfig(rootDir) {
|
||||
const assetsDir = 'assets';
|
||||
const assetsDirAbs = (0, path_1.join)(rootDir, assetsDir);
|
||||
const iosPlatformTemplateArchive = 'ios-pods-template.tar.gz';
|
||||
const iosCordovaPluginsTemplateArchive = 'capacitor-cordova-ios-plugins.tar.gz';
|
||||
const androidPlatformTemplateArchive = 'android-template.tar.gz';
|
||||
const androidCordovaPluginsTemplateArchive = 'capacitor-cordova-android-plugins.tar.gz';
|
||||
return {
|
||||
rootDir,
|
||||
assetsDir,
|
||||
assetsDirAbs,
|
||||
assets: {
|
||||
ios: {
|
||||
platformTemplateArchive: iosPlatformTemplateArchive,
|
||||
platformTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, iosPlatformTemplateArchive),
|
||||
cordovaPluginsTemplateArchive: iosCordovaPluginsTemplateArchive,
|
||||
cordovaPluginsTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, iosCordovaPluginsTemplateArchive),
|
||||
},
|
||||
android: {
|
||||
platformTemplateArchive: androidPlatformTemplateArchive,
|
||||
platformTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, androidPlatformTemplateArchive),
|
||||
cordovaPluginsTemplateArchive: androidCordovaPluginsTemplateArchive,
|
||||
cordovaPluginsTemplateArchiveAbs: (0, path_1.resolve)(assetsDirAbs, androidCordovaPluginsTemplateArchive),
|
||||
},
|
||||
},
|
||||
package: await (0, utils_fs_1.readJSON)((0, path_1.resolve)(rootDir, 'package.json')),
|
||||
os: determineOS(process.platform),
|
||||
};
|
||||
}
|
||||
async function loadAndroidConfig(rootDir, extConfig, cliConfig) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
||||
const name = 'android';
|
||||
const platformDir = (_b = (_a = extConfig.android) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : 'android';
|
||||
const platformDirAbs = (0, path_1.resolve)(rootDir, platformDir);
|
||||
const appDir = 'app';
|
||||
const srcDir = `${appDir}/src`;
|
||||
const srcMainDir = `${srcDir}/main`;
|
||||
const assetsDir = `${srcMainDir}/assets`;
|
||||
const webDir = `${assetsDir}/public`;
|
||||
const resDir = `${srcMainDir}/res`;
|
||||
let apkPath = `${appDir}/build/outputs/apk/`;
|
||||
const flavor = ((_c = extConfig.android) === null || _c === void 0 ? void 0 : _c.flavor) || '';
|
||||
if ((_d = extConfig.android) === null || _d === void 0 ? void 0 : _d.flavor) {
|
||||
apkPath = `${apkPath}/${(_e = extConfig.android) === null || _e === void 0 ? void 0 : _e.flavor}`;
|
||||
}
|
||||
const apkName = (0, common_1.parseApkNameFromFlavor)(flavor);
|
||||
const buildOutputDir = `${apkPath}/debug`;
|
||||
const cordovaPluginsDir = 'capacitor-cordova-android-plugins';
|
||||
const studioPath = (0, promise_1.lazy)(() => determineAndroidStudioPath(cliConfig.os));
|
||||
const buildOptions = {
|
||||
keystorePath: (_g = (_f = extConfig.android) === null || _f === void 0 ? void 0 : _f.buildOptions) === null || _g === void 0 ? void 0 : _g.keystorePath,
|
||||
keystorePassword: (_j = (_h = extConfig.android) === null || _h === void 0 ? void 0 : _h.buildOptions) === null || _j === void 0 ? void 0 : _j.keystorePassword,
|
||||
keystoreAlias: (_l = (_k = extConfig.android) === null || _k === void 0 ? void 0 : _k.buildOptions) === null || _l === void 0 ? void 0 : _l.keystoreAlias,
|
||||
keystoreAliasPassword: (_o = (_m = extConfig.android) === null || _m === void 0 ? void 0 : _m.buildOptions) === null || _o === void 0 ? void 0 : _o.keystoreAliasPassword,
|
||||
signingType: (_q = (_p = extConfig.android) === null || _p === void 0 ? void 0 : _p.buildOptions) === null || _q === void 0 ? void 0 : _q.signingType,
|
||||
releaseType: (_s = (_r = extConfig.android) === null || _r === void 0 ? void 0 : _r.buildOptions) === null || _s === void 0 ? void 0 : _s.releaseType,
|
||||
};
|
||||
return {
|
||||
name,
|
||||
minVersion: '22',
|
||||
studioPath,
|
||||
platformDir,
|
||||
platformDirAbs,
|
||||
cordovaPluginsDir,
|
||||
cordovaPluginsDirAbs: (0, path_1.resolve)(platformDirAbs, cordovaPluginsDir),
|
||||
appDir,
|
||||
appDirAbs: (0, path_1.resolve)(platformDirAbs, appDir),
|
||||
srcDir,
|
||||
srcDirAbs: (0, path_1.resolve)(platformDirAbs, srcDir),
|
||||
srcMainDir,
|
||||
srcMainDirAbs: (0, path_1.resolve)(platformDirAbs, srcMainDir),
|
||||
assetsDir,
|
||||
assetsDirAbs: (0, path_1.resolve)(platformDirAbs, assetsDir),
|
||||
webDir,
|
||||
webDirAbs: (0, path_1.resolve)(platformDirAbs, webDir),
|
||||
resDir,
|
||||
resDirAbs: (0, path_1.resolve)(platformDirAbs, resDir),
|
||||
apkName,
|
||||
buildOutputDir,
|
||||
buildOutputDirAbs: (0, path_1.resolve)(platformDirAbs, buildOutputDir),
|
||||
flavor,
|
||||
buildOptions,
|
||||
};
|
||||
}
|
||||
async function loadIOSConfig(rootDir, extConfig) {
|
||||
var _a, _b, _c, _d;
|
||||
const name = 'ios';
|
||||
const platformDir = (_b = (_a = extConfig.ios) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : 'ios';
|
||||
const platformDirAbs = (0, path_1.resolve)(rootDir, platformDir);
|
||||
const scheme = (_d = (_c = extConfig.ios) === null || _c === void 0 ? void 0 : _c.scheme) !== null && _d !== void 0 ? _d : 'App';
|
||||
const nativeProjectDir = 'App';
|
||||
const nativeProjectDirAbs = (0, path_1.resolve)(platformDirAbs, nativeProjectDir);
|
||||
const nativeTargetDir = `${nativeProjectDir}/App`;
|
||||
const nativeTargetDirAbs = (0, path_1.resolve)(platformDirAbs, nativeTargetDir);
|
||||
const nativeXcodeProjDir = `${nativeProjectDir}/App.xcodeproj`;
|
||||
const nativeXcodeProjDirAbs = (0, path_1.resolve)(platformDirAbs, nativeXcodeProjDir);
|
||||
const nativeXcodeWorkspaceDirAbs = (0, promise_1.lazy)(() => determineXcodeWorkspaceDirAbs(nativeProjectDirAbs));
|
||||
const podPath = (0, promise_1.lazy)(() => determineGemfileOrCocoapodPath(rootDir, platformDirAbs, nativeProjectDirAbs));
|
||||
const webDirAbs = (0, promise_1.lazy)(() => determineIOSWebDirAbs(nativeProjectDirAbs, nativeTargetDirAbs, nativeXcodeProjDirAbs));
|
||||
const cordovaPluginsDir = 'capacitor-cordova-ios-plugins';
|
||||
return {
|
||||
name,
|
||||
minVersion: '13.0',
|
||||
platformDir,
|
||||
platformDirAbs,
|
||||
scheme,
|
||||
cordovaPluginsDir,
|
||||
cordovaPluginsDirAbs: (0, path_1.resolve)(platformDirAbs, cordovaPluginsDir),
|
||||
nativeProjectDir,
|
||||
nativeProjectDirAbs,
|
||||
nativeTargetDir,
|
||||
nativeTargetDirAbs,
|
||||
nativeXcodeProjDir,
|
||||
nativeXcodeProjDirAbs,
|
||||
nativeXcodeWorkspaceDir: (0, promise_1.lazy)(async () => (0, path_1.relative)(platformDirAbs, await nativeXcodeWorkspaceDirAbs)),
|
||||
nativeXcodeWorkspaceDirAbs,
|
||||
webDir: (0, promise_1.lazy)(async () => (0, path_1.relative)(platformDirAbs, await webDirAbs)),
|
||||
webDirAbs,
|
||||
podPath,
|
||||
};
|
||||
}
|
||||
async function loadWebConfig(rootDir, webDir) {
|
||||
const platformDir = webDir;
|
||||
const platformDirAbs = (0, path_1.resolve)(rootDir, platformDir);
|
||||
return {
|
||||
name: 'web',
|
||||
platformDir,
|
||||
platformDirAbs,
|
||||
};
|
||||
}
|
||||
function determineOS(os) {
|
||||
switch (os) {
|
||||
case 'darwin':
|
||||
return "mac" /* OS.Mac */;
|
||||
case 'win32':
|
||||
return "windows" /* OS.Windows */;
|
||||
case 'linux':
|
||||
return "linux" /* OS.Linux */;
|
||||
}
|
||||
return "unknown" /* OS.Unknown */;
|
||||
}
|
||||
async function determineXcodeWorkspaceDirAbs(nativeProjectDirAbs) {
|
||||
return (0, path_1.resolve)(nativeProjectDirAbs, 'App.xcworkspace');
|
||||
}
|
||||
async function determineIOSWebDirAbs(nativeProjectDirAbs, nativeTargetDirAbs, nativeXcodeProjDirAbs) {
|
||||
const re = /path\s=\spublic[\s\S]+?sourceTree\s=\s([^;]+)/;
|
||||
const pbxprojPath = (0, path_1.resolve)(nativeXcodeProjDirAbs, 'project.pbxproj');
|
||||
try {
|
||||
const pbxproj = await (0, utils_fs_1.readFile)(pbxprojPath, { encoding: 'utf8' });
|
||||
const m = pbxproj.match(re);
|
||||
if (m && m[1] === 'SOURCE_ROOT') {
|
||||
log_1.logger.warn(`Using the iOS project root for the ${colors_1.default.strong('public')} directory is deprecated.\n` +
|
||||
`Please follow the Upgrade Guide to move ${colors_1.default.strong('public')} inside the iOS target directory: ${colors_1.default.strong('https://capacitorjs.com/docs/updating/3-0#move-public-into-the-ios-target-directory')}`);
|
||||
return (0, path_1.resolve)(nativeProjectDirAbs, 'public');
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// ignore
|
||||
}
|
||||
return (0, path_1.resolve)(nativeTargetDirAbs, 'public');
|
||||
}
|
||||
async function determineAndroidStudioPath(os) {
|
||||
if (process.env.CAPACITOR_ANDROID_STUDIO_PATH) {
|
||||
return process.env.CAPACITOR_ANDROID_STUDIO_PATH;
|
||||
}
|
||||
switch (os) {
|
||||
case "mac" /* OS.Mac */:
|
||||
return '/Applications/Android Studio.app';
|
||||
case "windows" /* OS.Windows */: {
|
||||
const { runCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./util/subprocess')));
|
||||
let p = 'C:\\Program Files\\Android\\Android Studio\\bin\\studio64.exe';
|
||||
try {
|
||||
if (!(await (0, utils_fs_1.pathExists)(p))) {
|
||||
let commandResult = await runCommand('REG', [
|
||||
'QUERY',
|
||||
'HKEY_LOCAL_MACHINE\\SOFTWARE\\Android Studio',
|
||||
'/v',
|
||||
'Path',
|
||||
]);
|
||||
commandResult = commandResult.replace(/(\r\n|\n|\r)/gm, '');
|
||||
const i = commandResult.indexOf('REG_SZ');
|
||||
if (i > 0) {
|
||||
p = commandResult.substring(i + 6).trim() + '\\bin\\studio64.exe';
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
debug(`Error checking registry for Android Studio path: %O`, e);
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
case "linux" /* OS.Linux */:
|
||||
return '/usr/local/android-studio/bin/studio.sh';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
async function determineGemfileOrCocoapodPath(rootDir, platformDir, nativeProjectDirAbs) {
|
||||
if (process.env.CAPACITOR_COCOAPODS_PATH) {
|
||||
return process.env.CAPACITOR_COCOAPODS_PATH;
|
||||
}
|
||||
let gemfilePath = '';
|
||||
if (await (0, utils_fs_1.pathExists)((0, path_1.resolve)(rootDir, 'Gemfile'))) {
|
||||
gemfilePath = (0, path_1.resolve)(rootDir, 'Gemfile');
|
||||
}
|
||||
else if (await (0, utils_fs_1.pathExists)((0, path_1.resolve)(platformDir, 'Gemfile'))) {
|
||||
gemfilePath = (0, path_1.resolve)(platformDir, 'Gemfile');
|
||||
}
|
||||
else if (await (0, utils_fs_1.pathExists)((0, path_1.resolve)(nativeProjectDirAbs, 'Gemfile'))) {
|
||||
gemfilePath = (0, path_1.resolve)(nativeProjectDirAbs, 'Gemfile');
|
||||
}
|
||||
const appSpecificGemfileExists = gemfilePath != '';
|
||||
// Multi-app projects might share a single global 'Gemfile' at the Git repository root directory.
|
||||
if (!appSpecificGemfileExists) {
|
||||
try {
|
||||
const output = await (0, subprocess_1.getCommandOutput)('git', ['rev-parse', '--show-toplevel'], { cwd: rootDir });
|
||||
if (output != null) {
|
||||
gemfilePath = (0, path_1.resolve)(output, 'Gemfile');
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
try {
|
||||
const gemfileText = (await (0, utils_fs_1.readFile)(gemfilePath)).toString();
|
||||
if (!gemfileText) {
|
||||
return 'pod';
|
||||
}
|
||||
const cocoapodsInGemfile = new RegExp(/gem\s+['"]cocoapods/).test(gemfileText);
|
||||
if (cocoapodsInGemfile) {
|
||||
return 'bundle exec pod';
|
||||
}
|
||||
else {
|
||||
return 'pod';
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return 'pod';
|
||||
}
|
||||
}
|
||||
function formatConfigTS(extConfig) {
|
||||
// TODO: <reference> tags
|
||||
return `import type { CapacitorConfig } from '@capacitor/cli';
|
||||
|
||||
const config: CapacitorConfig = ${(0, js_1.formatJSObject)(extConfig)};
|
||||
|
||||
export default config;\n`;
|
||||
}
|
||||
function checkExternalConfig(config) {
|
||||
if (typeof config.extConfig.bundledWebRuntime !== 'undefined') {
|
||||
let actionMessage = `Can be safely deleted.`;
|
||||
if (config.extConfig.bundledWebRuntime === true) {
|
||||
actionMessage = `Please, use a bundler to bundle Capacitor and its plugins.`;
|
||||
}
|
||||
log_1.logger.warn(`The ${colors_1.default.strong('bundledWebRuntime')} configuration option has been deprecated. ${actionMessage}`);
|
||||
}
|
||||
}
|
||||
exports.checkExternalConfig = checkExternalConfig;
|
||||
830
node_modules/@capacitor/cli/dist/cordova.js
generated
vendored
Normal file
830
node_modules/@capacitor/cli/dist/cordova.js
generated
vendored
Normal file
@@ -0,0 +1,830 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.writeCordovaAndroidManifest = exports.getCordovaPreferences = exports.needsStaticPod = exports.getIncompatibleCordovaPlugins = exports.checkPluginDependencies = exports.logCordovaManualSteps = exports.getCordovaPlugins = exports.handleCordovaPluginsJS = exports.autoGenerateConfig = exports.removePluginFiles = exports.createEmptyCordovaJS = exports.copyCordovaJS = exports.copyPluginsJS = exports.generateCordovaPluginsJSFile = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const utils_fs_1 = require("@ionic/utils-fs");
|
||||
const path_1 = require("path");
|
||||
const plist_1 = tslib_1.__importDefault(require("plist"));
|
||||
const prompts_1 = tslib_1.__importDefault(require("prompts"));
|
||||
const common_1 = require("./android/common");
|
||||
const colors_1 = tslib_1.__importDefault(require("./colors"));
|
||||
const errors_1 = require("./errors");
|
||||
const common_2 = require("./ios/common");
|
||||
const log_1 = require("./log");
|
||||
const plugin_1 = require("./plugin");
|
||||
const node_1 = require("./util/node");
|
||||
const term_1 = require("./util/term");
|
||||
const xml_1 = require("./util/xml");
|
||||
/**
|
||||
* Build the root cordova_plugins.js file referencing each Plugin JS file.
|
||||
*/
|
||||
function generateCordovaPluginsJSFile(config, plugins, platform) {
|
||||
const pluginModules = [];
|
||||
const pluginExports = [];
|
||||
plugins.map(p => {
|
||||
const pluginId = p.xml.$.id;
|
||||
const jsModules = (0, plugin_1.getJSModules)(p, platform);
|
||||
jsModules.map((jsModule) => {
|
||||
const clobbers = [];
|
||||
const merges = [];
|
||||
let clobbersModule = '';
|
||||
let mergesModule = '';
|
||||
let runsModule = '';
|
||||
let clobberKey = '';
|
||||
let mergeKey = '';
|
||||
if (jsModule.clobbers) {
|
||||
jsModule.clobbers.map((clobber) => {
|
||||
clobbers.push(clobber.$.target);
|
||||
clobberKey = clobber.$.target;
|
||||
});
|
||||
clobbersModule = `,
|
||||
"clobbers": [
|
||||
"${clobbers.join('",\n "')}"
|
||||
]`;
|
||||
}
|
||||
if (jsModule.merges) {
|
||||
jsModule.merges.map((merge) => {
|
||||
merges.push(merge.$.target);
|
||||
mergeKey = merge.$.target;
|
||||
});
|
||||
mergesModule = `,
|
||||
"merges": [
|
||||
"${merges.join('",\n "')}"
|
||||
]`;
|
||||
}
|
||||
if (jsModule.runs) {
|
||||
runsModule = ',\n "runs": true';
|
||||
}
|
||||
const pluginModule = {
|
||||
clobber: clobberKey,
|
||||
merge: mergeKey,
|
||||
// mimics Cordova's module name logic if the name attr is missing
|
||||
pluginContent: `{
|
||||
"id": "${pluginId +
|
||||
'.' +
|
||||
(jsModule.$.name || jsModule.$.src.match(/([^/]+)\.js/)[1])}",
|
||||
"file": "plugins/${pluginId}/${jsModule.$.src}",
|
||||
"pluginId": "${pluginId}"${clobbersModule}${mergesModule}${runsModule}
|
||||
}`,
|
||||
};
|
||||
pluginModules.push(pluginModule);
|
||||
});
|
||||
pluginExports.push(`"${pluginId}": "${p.xml.$.version}"`);
|
||||
});
|
||||
return `
|
||||
cordova.define('cordova/plugin_list', function(require, exports, module) {
|
||||
module.exports = [
|
||||
${pluginModules
|
||||
.sort((a, b) => a.clobber && b.clobber // Clobbers in alpha order
|
||||
? a.clobber.localeCompare(b.clobber)
|
||||
: a.clobber || b.clobber // Clobbers before anything else
|
||||
? b.clobber.localeCompare(a.clobber)
|
||||
: a.merge.localeCompare(b.merge))
|
||||
.map(e => e.pluginContent)
|
||||
.join(',\n ')}
|
||||
];
|
||||
module.exports.metadata =
|
||||
// TOP OF METADATA
|
||||
{
|
||||
${pluginExports.join(',\n ')}
|
||||
};
|
||||
// BOTTOM OF METADATA
|
||||
});
|
||||
`;
|
||||
}
|
||||
exports.generateCordovaPluginsJSFile = generateCordovaPluginsJSFile;
|
||||
/**
|
||||
* Build the plugins/* files for each Cordova plugin installed.
|
||||
*/
|
||||
async function copyPluginsJS(config, cordovaPlugins, platform) {
|
||||
const webDir = await getWebDir(config, platform);
|
||||
const pluginsDir = (0, path_1.join)(webDir, 'plugins');
|
||||
const cordovaPluginsJSFile = (0, path_1.join)(webDir, 'cordova_plugins.js');
|
||||
await removePluginFiles(config, platform);
|
||||
await Promise.all(cordovaPlugins.map(async (p) => {
|
||||
const pluginId = p.xml.$.id;
|
||||
const pluginDir = (0, path_1.join)(pluginsDir, pluginId, 'www');
|
||||
await (0, utils_fs_1.ensureDir)(pluginDir);
|
||||
const jsModules = (0, plugin_1.getJSModules)(p, platform);
|
||||
await Promise.all(jsModules.map(async (jsModule) => {
|
||||
const filePath = (0, path_1.join)(webDir, 'plugins', pluginId, jsModule.$.src);
|
||||
await (0, utils_fs_1.copy)((0, path_1.join)(p.rootPath, jsModule.$.src), filePath);
|
||||
let data = await (0, utils_fs_1.readFile)(filePath, { encoding: 'utf-8' });
|
||||
data = data.trim();
|
||||
// mimics Cordova's module name logic if the name attr is missing
|
||||
const name = pluginId +
|
||||
'.' +
|
||||
(jsModule.$.name ||
|
||||
(0, path_1.basename)(jsModule.$.src, (0, path_1.extname)(jsModule.$.src)));
|
||||
data = `cordova.define("${name}", function(require, exports, module) { \n${data}\n});`;
|
||||
data = data.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script\s*>/gi, '');
|
||||
await (0, utils_fs_1.writeFile)(filePath, data, { encoding: 'utf-8' });
|
||||
}));
|
||||
const assets = (0, plugin_1.getAssets)(p, platform);
|
||||
await Promise.all(assets.map(async (asset) => {
|
||||
const filePath = (0, path_1.join)(webDir, asset.$.target);
|
||||
await (0, utils_fs_1.copy)((0, path_1.join)(p.rootPath, asset.$.src), filePath);
|
||||
}));
|
||||
}));
|
||||
await (0, utils_fs_1.writeFile)(cordovaPluginsJSFile, generateCordovaPluginsJSFile(config, cordovaPlugins, platform));
|
||||
}
|
||||
exports.copyPluginsJS = copyPluginsJS;
|
||||
async function copyCordovaJS(config, platform) {
|
||||
const cordovaPath = (0, node_1.resolveNode)(config.app.rootDir, '@capacitor/core', 'cordova.js');
|
||||
if (!cordovaPath) {
|
||||
(0, errors_1.fatal)(`Unable to find ${colors_1.default.strong('node_modules/@capacitor/core/cordova.js')}.\n` + `Are you sure ${colors_1.default.strong('@capacitor/core')} is installed?`);
|
||||
}
|
||||
return (0, utils_fs_1.copy)(cordovaPath, (0, path_1.join)(await getWebDir(config, platform), 'cordova.js'));
|
||||
}
|
||||
exports.copyCordovaJS = copyCordovaJS;
|
||||
async function createEmptyCordovaJS(config, platform) {
|
||||
const webDir = await getWebDir(config, platform);
|
||||
await (0, utils_fs_1.writeFile)((0, path_1.join)(webDir, 'cordova.js'), '');
|
||||
await (0, utils_fs_1.writeFile)((0, path_1.join)(webDir, 'cordova_plugins.js'), '');
|
||||
}
|
||||
exports.createEmptyCordovaJS = createEmptyCordovaJS;
|
||||
async function removePluginFiles(config, platform) {
|
||||
const webDir = await getWebDir(config, platform);
|
||||
const pluginsDir = (0, path_1.join)(webDir, 'plugins');
|
||||
const cordovaPluginsJSFile = (0, path_1.join)(webDir, 'cordova_plugins.js');
|
||||
await (0, utils_fs_1.remove)(pluginsDir);
|
||||
await (0, utils_fs_1.remove)(cordovaPluginsJSFile);
|
||||
}
|
||||
exports.removePluginFiles = removePluginFiles;
|
||||
async function autoGenerateConfig(config, cordovaPlugins, platform) {
|
||||
var _a, _b, _c, _d;
|
||||
let xmlDir = (0, path_1.join)(config.android.resDirAbs, 'xml');
|
||||
const fileName = 'config.xml';
|
||||
if (platform === 'ios') {
|
||||
xmlDir = config.ios.nativeTargetDirAbs;
|
||||
}
|
||||
await (0, utils_fs_1.ensureDir)(xmlDir);
|
||||
const cordovaConfigXMLFile = (0, path_1.join)(xmlDir, fileName);
|
||||
await (0, utils_fs_1.remove)(cordovaConfigXMLFile);
|
||||
const pluginEntries = [];
|
||||
cordovaPlugins.map(p => {
|
||||
const currentPlatform = (0, plugin_1.getPluginPlatform)(p, platform);
|
||||
if (currentPlatform) {
|
||||
const configFiles = currentPlatform['config-file'];
|
||||
if (configFiles) {
|
||||
const configXMLEntries = configFiles.filter(function (item) {
|
||||
var _a;
|
||||
return (_a = item.$) === null || _a === void 0 ? void 0 : _a.target.includes(fileName);
|
||||
});
|
||||
configXMLEntries.map((entry) => {
|
||||
if (entry.feature) {
|
||||
const feature = { feature: entry.feature };
|
||||
pluginEntries.push(feature);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
let accessOriginString = [];
|
||||
if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.accessOrigins) {
|
||||
accessOriginString = await Promise.all(config.app.extConfig.cordova.accessOrigins.map(async (host) => {
|
||||
return `
|
||||
<access origin="${host}" />`;
|
||||
}));
|
||||
}
|
||||
else {
|
||||
accessOriginString.push(`<access origin="*" />`);
|
||||
}
|
||||
const pluginEntriesString = await Promise.all(pluginEntries.map(async (item) => {
|
||||
const xmlString = await (0, xml_1.writeXML)(item);
|
||||
return xmlString;
|
||||
}));
|
||||
let pluginPreferencesString = [];
|
||||
if ((_d = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova) === null || _d === void 0 ? void 0 : _d.preferences) {
|
||||
pluginPreferencesString = await Promise.all(Object.entries(config.app.extConfig.cordova.preferences).map(async ([key, value]) => {
|
||||
return `
|
||||
<preference name="${key}" value="${value}" />`;
|
||||
}));
|
||||
}
|
||||
const content = `<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
${accessOriginString.join('')}
|
||||
${pluginEntriesString.join('')}
|
||||
${pluginPreferencesString.join('')}
|
||||
</widget>`;
|
||||
await (0, utils_fs_1.writeFile)(cordovaConfigXMLFile, content);
|
||||
}
|
||||
exports.autoGenerateConfig = autoGenerateConfig;
|
||||
async function getWebDir(config, platform) {
|
||||
if (platform === 'ios') {
|
||||
return config.ios.webDirAbs;
|
||||
}
|
||||
if (platform === 'android') {
|
||||
return config.android.webDirAbs;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
async function handleCordovaPluginsJS(cordovaPlugins, config, platform) {
|
||||
const webDir = await getWebDir(config, platform);
|
||||
await (0, utils_fs_1.mkdirp)(webDir);
|
||||
if (cordovaPlugins.length > 0) {
|
||||
(0, plugin_1.printPlugins)(cordovaPlugins, platform, 'cordova');
|
||||
await copyCordovaJS(config, platform);
|
||||
await copyPluginsJS(config, cordovaPlugins, platform);
|
||||
}
|
||||
else {
|
||||
await removePluginFiles(config, platform);
|
||||
await createEmptyCordovaJS(config, platform);
|
||||
}
|
||||
await autoGenerateConfig(config, cordovaPlugins, platform);
|
||||
}
|
||||
exports.handleCordovaPluginsJS = handleCordovaPluginsJS;
|
||||
async function getCordovaPlugins(config, platform) {
|
||||
const allPlugins = await (0, plugin_1.getPlugins)(config, platform);
|
||||
let plugins = [];
|
||||
if (platform === config.ios.name) {
|
||||
plugins = await (0, common_2.getIOSPlugins)(allPlugins);
|
||||
}
|
||||
else if (platform === config.android.name) {
|
||||
plugins = await (0, common_1.getAndroidPlugins)(allPlugins);
|
||||
}
|
||||
return plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
|
||||
}
|
||||
exports.getCordovaPlugins = getCordovaPlugins;
|
||||
async function logCordovaManualSteps(cordovaPlugins, config, platform) {
|
||||
cordovaPlugins.map(p => {
|
||||
const editConfig = (0, plugin_1.getPlatformElement)(p, platform, 'edit-config');
|
||||
const configFile = (0, plugin_1.getPlatformElement)(p, platform, 'config-file');
|
||||
editConfig.concat(configFile).map(async (configElement) => {
|
||||
if (configElement.$ && !configElement.$.target.includes('config.xml')) {
|
||||
if (platform === config.ios.name) {
|
||||
if (configElement.$.target.includes('Info.plist')) {
|
||||
logiOSPlist(configElement, config, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.logCordovaManualSteps = logCordovaManualSteps;
|
||||
async function logiOSPlist(configElement, config, plugin) {
|
||||
var _a, _b;
|
||||
let plistPath = (0, path_1.resolve)(config.ios.nativeTargetDirAbs, 'Info.plist');
|
||||
if ((_a = config.app.extConfig.ios) === null || _a === void 0 ? void 0 : _a.scheme) {
|
||||
plistPath = (0, path_1.resolve)(config.ios.nativeProjectDirAbs, `${(_b = config.app.extConfig.ios) === null || _b === void 0 ? void 0 : _b.scheme}-Info.plist`);
|
||||
}
|
||||
if (!(await (0, utils_fs_1.pathExists)(plistPath))) {
|
||||
plistPath = (0, path_1.resolve)(config.ios.nativeTargetDirAbs, 'Base.lproj', 'Info.plist');
|
||||
}
|
||||
if (await (0, utils_fs_1.pathExists)(plistPath)) {
|
||||
const xmlMeta = await (0, xml_1.readXML)(plistPath);
|
||||
const data = await (0, utils_fs_1.readFile)(plistPath, { encoding: 'utf-8' });
|
||||
const trimmedPlistData = data.replace(/(\t|\r|\n)/g, '');
|
||||
const plistData = plist_1.default.parse(data);
|
||||
const dict = xmlMeta.plist.dict.pop();
|
||||
if (!dict.key.includes(configElement.$.parent)) {
|
||||
let xml = buildConfigFileXml(configElement);
|
||||
xml = `<key>${configElement.$.parent}</key>${getConfigFileTagContent(xml)}`;
|
||||
log_1.logger.warn(`Configuration required for ${colors_1.default.strong(plugin.id)}.\n` +
|
||||
`Add the following to Info.plist:\n` +
|
||||
xml);
|
||||
}
|
||||
else if (configElement.array || configElement.dict) {
|
||||
if (configElement.array &&
|
||||
configElement.array.length > 0 &&
|
||||
configElement.array[0].string) {
|
||||
let xml = '';
|
||||
configElement.array[0].string.map((element) => {
|
||||
const d = plistData[configElement.$.parent];
|
||||
if (Array.isArray(d) && !d.includes(element)) {
|
||||
xml = xml.concat(`<string>${element}</string>\n`);
|
||||
}
|
||||
});
|
||||
if (xml.length > 0) {
|
||||
log_1.logger.warn(`Configuration required for ${colors_1.default.strong(plugin.id)}.\n` +
|
||||
`Add the following in the existing ${colors_1.default.strong(configElement.$.parent)} array of your Info.plist:\n` +
|
||||
xml);
|
||||
}
|
||||
}
|
||||
else {
|
||||
let xml = buildConfigFileXml(configElement);
|
||||
xml = `<key>${configElement.$.parent}</key>${getConfigFileTagContent(xml)}`;
|
||||
xml = `<plist version="1.0"><dict>${xml}</dict></plist>`;
|
||||
const parseXmlToSearchable = (childElementsObj, arrayToAddTo) => {
|
||||
for (const childElement of childElementsObj) {
|
||||
const childElementName = childElement['#name'];
|
||||
const toAdd = { name: childElementName };
|
||||
if (childElementName === 'key' || childElementName === 'string') {
|
||||
toAdd.value = childElement['_'];
|
||||
}
|
||||
else {
|
||||
if (childElement['$']) {
|
||||
toAdd.attrs = { ...childElement['$'] };
|
||||
}
|
||||
if (childElement['$$']) {
|
||||
toAdd.children = [];
|
||||
parseXmlToSearchable(childElement['$$'], toAdd['children']);
|
||||
}
|
||||
}
|
||||
arrayToAddTo.push(toAdd);
|
||||
}
|
||||
};
|
||||
const existingElements = (0, xml_1.parseXML)(trimmedPlistData, {
|
||||
explicitChildren: true,
|
||||
trim: true,
|
||||
preserveChildrenOrder: true,
|
||||
});
|
||||
const parsedExistingElements = [];
|
||||
const rootKeyOfExistingElements = Object.keys(existingElements)[0];
|
||||
const rootOfExistingElementsToAdd = { name: rootKeyOfExistingElements, children: [] };
|
||||
if (existingElements[rootKeyOfExistingElements]['$']) {
|
||||
rootOfExistingElementsToAdd.attrs = {
|
||||
...existingElements[rootKeyOfExistingElements]['$'],
|
||||
};
|
||||
}
|
||||
parseXmlToSearchable(existingElements[rootKeyOfExistingElements]['$$'], rootOfExistingElementsToAdd['children']);
|
||||
parsedExistingElements.push(rootOfExistingElementsToAdd);
|
||||
const requiredElements = (0, xml_1.parseXML)(xml, {
|
||||
explicitChildren: true,
|
||||
trim: true,
|
||||
preserveChildrenOrder: true,
|
||||
});
|
||||
const parsedRequiredElements = [];
|
||||
const rootKeyOfRequiredElements = Object.keys(requiredElements)[0];
|
||||
const rootOfRequiredElementsToAdd = { name: rootKeyOfRequiredElements, children: [] };
|
||||
if (requiredElements[rootKeyOfRequiredElements]['$']) {
|
||||
rootOfRequiredElementsToAdd.attrs = {
|
||||
...requiredElements[rootKeyOfRequiredElements]['$'],
|
||||
};
|
||||
}
|
||||
parseXmlToSearchable(requiredElements[rootKeyOfRequiredElements]['$$'], rootOfRequiredElementsToAdd['children']);
|
||||
parsedRequiredElements.push(rootOfRequiredElementsToAdd);
|
||||
const doesContainElements = (requiredElementsArray, existingElementsArray) => {
|
||||
for (const requiredElement of requiredElementsArray) {
|
||||
if (requiredElement.name === 'key' ||
|
||||
requiredElement.name === 'string') {
|
||||
let foundMatch = false;
|
||||
for (const existingElement of existingElementsArray) {
|
||||
if (existingElement.name === requiredElement.name &&
|
||||
(existingElement.value === requiredElement.value ||
|
||||
/^[$].{1,}$/.test(requiredElement.value.trim()))) {
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
let foundMatch = false;
|
||||
for (const existingElement of existingElementsArray) {
|
||||
if (existingElement.name === requiredElement.name) {
|
||||
if ((requiredElement.children !== undefined) ===
|
||||
(existingElement.children !== undefined)) {
|
||||
if (doesContainElements(requiredElement.children, existingElement.children)) {
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!doesContainElements(parsedRequiredElements, parsedExistingElements)) {
|
||||
logPossibleMissingItem(configElement, plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logPossibleMissingItem(configElement, plugin);
|
||||
}
|
||||
}
|
||||
function logPossibleMissingItem(configElement, plugin) {
|
||||
let xml = buildConfigFileXml(configElement);
|
||||
xml = getConfigFileTagContent(xml);
|
||||
xml = removeOuterTags(xml);
|
||||
log_1.logger.warn(`Configuration might be missing for ${colors_1.default.strong(plugin.id)}.\n` +
|
||||
`Add the following to the existing ${colors_1.default.strong(configElement.$.parent)} entry of Info.plist:\n` +
|
||||
xml);
|
||||
}
|
||||
function buildConfigFileXml(configElement) {
|
||||
return (0, xml_1.buildXmlElement)(configElement, 'config-file');
|
||||
}
|
||||
function getConfigFileTagContent(str) {
|
||||
return str.replace(/<config-file.+">|<\/config-file>/g, '');
|
||||
}
|
||||
function removeOuterTags(str) {
|
||||
const start = str.indexOf('>') + 1;
|
||||
const end = str.lastIndexOf('<');
|
||||
return str.substring(start, end);
|
||||
}
|
||||
async function checkPluginDependencies(plugins, platform) {
|
||||
const pluginDeps = new Map();
|
||||
const cordovaPlugins = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 1 /* PluginType.Cordova */);
|
||||
const incompatible = plugins.filter(p => (0, plugin_1.getPluginType)(p, platform) === 2 /* PluginType.Incompatible */);
|
||||
await Promise.all(cordovaPlugins.map(async (p) => {
|
||||
let allDependencies = [];
|
||||
allDependencies = allDependencies.concat((0, plugin_1.getPlatformElement)(p, platform, 'dependency'));
|
||||
if (p.xml['dependency']) {
|
||||
allDependencies = allDependencies.concat(p.xml['dependency']);
|
||||
}
|
||||
allDependencies = allDependencies.filter((dep) => !getIncompatibleCordovaPlugins(platform).includes(dep.$.id) &&
|
||||
incompatible.filter(p => p.id === dep.$.id || p.xml.$.id === dep.$.id)
|
||||
.length === 0);
|
||||
if (allDependencies) {
|
||||
await Promise.all(allDependencies.map(async (dep) => {
|
||||
var _a;
|
||||
let plugin = dep.$.id;
|
||||
let version = dep.$.version;
|
||||
if (plugin.includes('@') && plugin.indexOf('@') !== 0) {
|
||||
[plugin, version] = plugin.split('@');
|
||||
}
|
||||
if (cordovaPlugins.filter(p => p.id === plugin || p.xml.$.id === plugin).length === 0) {
|
||||
if ((_a = dep.$.url) === null || _a === void 0 ? void 0 : _a.startsWith('http')) {
|
||||
plugin = dep.$.url;
|
||||
version = dep.$.commit;
|
||||
}
|
||||
const deps = pluginDeps.get(p.id) || [];
|
||||
deps.push(`${plugin}${version ? colors_1.default.weak(` (${version})`) : ''}`);
|
||||
pluginDeps.set(p.id, deps);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
if (pluginDeps.size > 0) {
|
||||
let msg = `${colors_1.default.failure(colors_1.default.strong('Plugins are missing dependencies.'))}\n` +
|
||||
`Cordova plugin dependencies must be installed in your project (e.g. w/ ${colors_1.default.input('npm install')}).\n`;
|
||||
for (const [plugin, deps] of pluginDeps.entries()) {
|
||||
msg +=
|
||||
`\n ${colors_1.default.strong(plugin)} is missing dependencies:\n` +
|
||||
deps.map(d => ` - ${d}`).join('\n');
|
||||
}
|
||||
log_1.logger.warn(`${msg}\n`);
|
||||
}
|
||||
}
|
||||
exports.checkPluginDependencies = checkPluginDependencies;
|
||||
function getIncompatibleCordovaPlugins(platform) {
|
||||
const pluginList = [
|
||||
'cordova-plugin-splashscreen',
|
||||
'cordova-plugin-ionic-webview',
|
||||
'cordova-plugin-crosswalk-webview',
|
||||
'cordova-plugin-wkwebview-engine',
|
||||
'cordova-plugin-console',
|
||||
'cordova-plugin-music-controls',
|
||||
'cordova-plugin-add-swift-support',
|
||||
'cordova-plugin-ionic-keyboard',
|
||||
'cordova-plugin-braintree',
|
||||
'@ionic-enterprise/filesystem',
|
||||
'@ionic-enterprise/keyboard',
|
||||
'@ionic-enterprise/splashscreen',
|
||||
'cordova-support-google-services',
|
||||
];
|
||||
if (platform === 'ios') {
|
||||
pluginList.push('cordova-plugin-statusbar', '@ionic-enterprise/statusbar', 'SalesforceMobileSDK-CordovaPlugin');
|
||||
}
|
||||
if (platform === 'android') {
|
||||
pluginList.push('cordova-plugin-compat');
|
||||
}
|
||||
return pluginList;
|
||||
}
|
||||
exports.getIncompatibleCordovaPlugins = getIncompatibleCordovaPlugins;
|
||||
function needsStaticPod(plugin, config) {
|
||||
var _a, _b, _c, _d;
|
||||
let pluginList = [
|
||||
'phonegap-plugin-push',
|
||||
'@batch.com/cordova-plugin',
|
||||
'onesignal-cordova-plugin',
|
||||
];
|
||||
if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.staticPlugins) {
|
||||
log_1.logger.warn('cordova.staticPlugins is deprecated, make sure you are using latest version of the plugin');
|
||||
pluginList = pluginList.concat((_d = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova) === null || _d === void 0 ? void 0 : _d.staticPlugins);
|
||||
}
|
||||
return pluginList.includes(plugin.id) || useFrameworks(plugin);
|
||||
}
|
||||
exports.needsStaticPod = needsStaticPod;
|
||||
function useFrameworks(plugin) {
|
||||
const podspecs = (0, plugin_1.getPlatformElement)(plugin, 'ios', 'podspec');
|
||||
const frameworkPods = podspecs.filter((podspec) => podspec.pods.filter((pods) => pods.$ && pods.$['use-frameworks'] === 'true').length > 0);
|
||||
return frameworkPods.length > 0;
|
||||
}
|
||||
async function getCordovaPreferences(config) {
|
||||
var _a, _b, _c, _d, _e;
|
||||
const configXml = (0, path_1.join)(config.app.rootDir, 'config.xml');
|
||||
let cordova = {};
|
||||
if (await (0, utils_fs_1.pathExists)(configXml)) {
|
||||
cordova.preferences = {};
|
||||
const xmlMeta = await (0, xml_1.readXML)(configXml);
|
||||
if (xmlMeta.widget.preference) {
|
||||
xmlMeta.widget.preference.map((pref) => {
|
||||
cordova.preferences[pref.$.name] = pref.$.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (cordova.preferences && Object.keys(cordova.preferences).length > 0) {
|
||||
if ((0, term_1.isInteractive)()) {
|
||||
const answers = await (0, log_1.logPrompt)(`${colors_1.default.strong(`Cordova preferences can be automatically ported to ${colors_1.default.strong(config.app.extConfigName)}.`)}\n` +
|
||||
`Keep in mind: Not all values can be automatically migrated from ${colors_1.default.strong('config.xml')}. There may be more work to do.\n` +
|
||||
`More info: ${colors_1.default.strong('https://capacitorjs.com/docs/cordova/migrating-from-cordova-to-capacitor')}`, {
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `Migrate Cordova preferences from config.xml?`,
|
||||
initial: true,
|
||||
});
|
||||
if (answers.confirm) {
|
||||
if ((_b = (_a = config.app.extConfig) === null || _a === void 0 ? void 0 : _a.cordova) === null || _b === void 0 ? void 0 : _b.preferences) {
|
||||
const answers = await (0, prompts_1.default)([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `${config.app.extConfigName} already contains Cordova preferences. Overwrite?`,
|
||||
},
|
||||
], { onCancel: () => process.exit(1) });
|
||||
if (!answers.confirm) {
|
||||
cordova = (_c = config.app.extConfig) === null || _c === void 0 ? void 0 : _c.cordova;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
cordova = (_d = config.app.extConfig) === null || _d === void 0 ? void 0 : _d.cordova;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
cordova = (_e = config.app.extConfig) === null || _e === void 0 ? void 0 : _e.cordova;
|
||||
}
|
||||
return cordova;
|
||||
}
|
||||
exports.getCordovaPreferences = getCordovaPreferences;
|
||||
async function writeCordovaAndroidManifest(cordovaPlugins, config, platform, cleartext) {
|
||||
var _a;
|
||||
const manifestPath = (0, path_1.join)(config.android.cordovaPluginsDirAbs, 'src', 'main', 'AndroidManifest.xml');
|
||||
const rootXMLEntries = [];
|
||||
const applicationXMLEntries = [];
|
||||
const applicationXMLAttributes = [];
|
||||
let prefsArray = [];
|
||||
cordovaPlugins.map(async (p) => {
|
||||
const editConfig = (0, plugin_1.getPlatformElement)(p, platform, 'edit-config');
|
||||
const configFile = (0, plugin_1.getPlatformElement)(p, platform, 'config-file');
|
||||
prefsArray = prefsArray.concat((0, plugin_1.getAllElements)(p, platform, 'preference'));
|
||||
editConfig.concat(configFile).map(async (configElement) => {
|
||||
var _a, _b;
|
||||
if (configElement.$ &&
|
||||
(((_a = configElement.$.target) === null || _a === void 0 ? void 0 : _a.includes('AndroidManifest.xml')) ||
|
||||
((_b = configElement.$.file) === null || _b === void 0 ? void 0 : _b.includes('AndroidManifest.xml')))) {
|
||||
const keys = Object.keys(configElement).filter(k => k !== '$');
|
||||
keys.map(k => {
|
||||
configElement[k].map(async (e) => {
|
||||
const xmlElement = (0, xml_1.buildXmlElement)(e, k);
|
||||
const pathParts = getPathParts(configElement.$.parent || configElement.$.target);
|
||||
if (pathParts.length > 1) {
|
||||
if (pathParts.pop() === 'application') {
|
||||
if (configElement.$.mode &&
|
||||
configElement.$.mode === 'merge' &&
|
||||
xmlElement.startsWith('<application')) {
|
||||
Object.keys(e.$).map((ek) => {
|
||||
applicationXMLAttributes.push(`${ek}="${e.$[ek]}"`);
|
||||
});
|
||||
}
|
||||
else if (!applicationXMLEntries.includes(xmlElement) &&
|
||||
!contains(applicationXMLEntries, xmlElement, k)) {
|
||||
applicationXMLEntries.push(xmlElement);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const manifestPathOfCapApp = (0, path_1.join)(config.android.appDirAbs, 'src', 'main', 'AndroidManifest.xml');
|
||||
const manifestContentTrimmed = (await (0, utils_fs_1.readFile)(manifestPathOfCapApp))
|
||||
.toString()
|
||||
.trim()
|
||||
.replace(/\n|\t|\r/g, '')
|
||||
.replace(/[\s]{1,}</g, '<')
|
||||
.replace(/>[\s]{1,}/g, '>')
|
||||
.replace(/[\s]{2,}/g, ' ');
|
||||
const requiredManifestContentTrimmed = xmlElement
|
||||
.trim()
|
||||
.replace(/\n|\t|\r/g, '')
|
||||
.replace(/[\s]{1,}</g, '<')
|
||||
.replace(/>[\s]{1,}/g, '>')
|
||||
.replace(/[\s]{2,}/g, ' ');
|
||||
const pathPartList = getPathParts(configElement.$.parent || configElement.$.target);
|
||||
const doesXmlManifestContainRequiredInfo = (requiredElements, existingElements, pathTarget) => {
|
||||
const findElementsToSearchIn = (existingElements, pathTarget) => {
|
||||
const parts = [...pathTarget];
|
||||
const elementsToSearchNextIn = [];
|
||||
for (const existingElement of existingElements) {
|
||||
if (existingElement.name === pathTarget[0]) {
|
||||
if (existingElement.children) {
|
||||
for (const el of existingElement.children) {
|
||||
elementsToSearchNextIn.push(el);
|
||||
}
|
||||
}
|
||||
else {
|
||||
elementsToSearchNextIn.push(existingElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (elementsToSearchNextIn.length === 0) {
|
||||
return [];
|
||||
}
|
||||
else {
|
||||
parts.splice(0, 1);
|
||||
if (parts.length <= 0) {
|
||||
return elementsToSearchNextIn;
|
||||
}
|
||||
else {
|
||||
return findElementsToSearchIn(elementsToSearchNextIn, parts);
|
||||
}
|
||||
}
|
||||
};
|
||||
const parseXmlToSearchable = (childElementsObj, arrayToAddTo) => {
|
||||
for (const childElementKey of Object.keys(childElementsObj)) {
|
||||
for (const occurannceOfElement of childElementsObj[childElementKey]) {
|
||||
const toAdd = { name: childElementKey };
|
||||
if (occurannceOfElement['$']) {
|
||||
toAdd.attrs = { ...occurannceOfElement['$'] };
|
||||
}
|
||||
if (occurannceOfElement['$$']) {
|
||||
toAdd.children = [];
|
||||
parseXmlToSearchable(occurannceOfElement['$$'], toAdd['children']);
|
||||
}
|
||||
arrayToAddTo.push(toAdd);
|
||||
}
|
||||
}
|
||||
};
|
||||
const doesElementMatch = (requiredElement, existingElement) => {
|
||||
var _a;
|
||||
if (requiredElement.name !== existingElement.name) {
|
||||
return false;
|
||||
}
|
||||
if ((requiredElement.attrs !== undefined) !==
|
||||
(existingElement.attrs !== undefined)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (requiredElement.attrs !== undefined) {
|
||||
const requiredELementAttrKeys = Object.keys(requiredElement.attrs);
|
||||
for (const key of requiredELementAttrKeys) {
|
||||
if (!/^[$].{1,}$/.test(requiredElement.attrs[key].trim())) {
|
||||
if (requiredElement.attrs[key] !==
|
||||
existingElement.attrs[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((requiredElement.children !== undefined) !==
|
||||
(existingElement.children !== undefined) &&
|
||||
((_a = requiredElement.children) === null || _a === void 0 ? void 0 : _a.length) !== 0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (requiredElement.children !== undefined) {
|
||||
// each req element is in existing element
|
||||
for (const requiredElementItem of requiredElement.children) {
|
||||
let foundRequiredElement = false;
|
||||
for (const existingElementItem of existingElement.children) {
|
||||
const foundRequiredElementIn = doesElementMatch(requiredElementItem, existingElementItem);
|
||||
if (foundRequiredElementIn) {
|
||||
foundRequiredElement = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundRequiredElement) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (requiredElement.children === undefined &&
|
||||
existingElement.children === undefined) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
let foundRequiredElement = false;
|
||||
for (const existingElementItem of existingElement.children) {
|
||||
const foundRequiredElementIn = doesElementMatch(requiredElement, existingElementItem);
|
||||
if (foundRequiredElementIn) {
|
||||
foundRequiredElement = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundRequiredElement) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const parsedExistingElements = [];
|
||||
const rootKeyOfExistingElements = Object.keys(existingElements)[0];
|
||||
const rootOfExistingElementsToAdd = { name: rootKeyOfExistingElements, children: [] };
|
||||
if (existingElements[rootKeyOfExistingElements]['$']) {
|
||||
rootOfExistingElementsToAdd.attrs = {
|
||||
...existingElements[rootKeyOfExistingElements]['$'],
|
||||
};
|
||||
}
|
||||
parseXmlToSearchable(existingElements[rootKeyOfExistingElements]['$$'], rootOfExistingElementsToAdd['children']);
|
||||
parsedExistingElements.push(rootOfExistingElementsToAdd);
|
||||
const parsedRequiredElements = [];
|
||||
const rootKeyOfRequiredElements = Object.keys(requiredElements)[0];
|
||||
const rootOfRequiredElementsToAdd = { name: rootKeyOfRequiredElements, children: [] };
|
||||
if (requiredElements[rootKeyOfRequiredElements]['$']) {
|
||||
rootOfRequiredElementsToAdd.attrs = {
|
||||
...requiredElements[rootKeyOfRequiredElements]['$'],
|
||||
};
|
||||
}
|
||||
if (requiredElements[rootKeyOfRequiredElements]['$$'] !==
|
||||
undefined) {
|
||||
parseXmlToSearchable(requiredElements[rootKeyOfRequiredElements]['$$'], rootOfRequiredElementsToAdd['children']);
|
||||
}
|
||||
parsedRequiredElements.push(rootOfRequiredElementsToAdd);
|
||||
const elementsToSearch = findElementsToSearchIn(parsedExistingElements, pathTarget);
|
||||
for (const requiredElement of parsedRequiredElements) {
|
||||
let foundMatch = false;
|
||||
for (const existingElement of elementsToSearch) {
|
||||
const doesContain = doesElementMatch(requiredElement, existingElement);
|
||||
if (doesContain) {
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!doesXmlManifestContainRequiredInfo((0, xml_1.parseXML)(requiredManifestContentTrimmed, {
|
||||
explicitChildren: true,
|
||||
trim: true,
|
||||
}), (0, xml_1.parseXML)(manifestContentTrimmed, {
|
||||
explicitChildren: true,
|
||||
trim: true,
|
||||
}), pathPartList)) {
|
||||
log_1.logger.warn(`Android Configuration required for ${colors_1.default.strong(p.id)}.\n` +
|
||||
`Add the following to AndroidManifest.xml:\n` +
|
||||
xmlElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!rootXMLEntries.includes(xmlElement) &&
|
||||
!contains(rootXMLEntries, xmlElement, k)) {
|
||||
rootXMLEntries.push(xmlElement);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
const cleartextString = 'android:usesCleartextTraffic="true"';
|
||||
const cleartextValue = (cleartext || ((_a = config.app.extConfig.server) === null || _a === void 0 ? void 0 : _a.cleartext)) &&
|
||||
!applicationXMLAttributes.includes(cleartextString)
|
||||
? cleartextString
|
||||
: '';
|
||||
let content = `<?xml version='1.0' encoding='utf-8'?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:amazon="http://schemas.amazon.com/apk/res/android">
|
||||
<application ${applicationXMLAttributes.join('\n')} ${cleartextValue}>
|
||||
${applicationXMLEntries.join('\n')}
|
||||
</application>
|
||||
${rootXMLEntries.join('\n')}
|
||||
</manifest>`;
|
||||
content = content.replace(new RegExp('$PACKAGE_NAME'.replace('$', '\\$&'), 'g'), '${applicationId}');
|
||||
for (const preference of prefsArray) {
|
||||
content = content.replace(new RegExp(('$' + preference.$.name).replace('$', '\\$&'), 'g'), preference.$.default);
|
||||
}
|
||||
if (await (0, utils_fs_1.pathExists)(manifestPath)) {
|
||||
await (0, utils_fs_1.writeFile)(manifestPath, content);
|
||||
}
|
||||
}
|
||||
exports.writeCordovaAndroidManifest = writeCordovaAndroidManifest;
|
||||
function getPathParts(path) {
|
||||
const rootPath = 'manifest';
|
||||
path = path.replace('/*', rootPath);
|
||||
const parts = path.split('/').filter(part => part !== '');
|
||||
if (parts.length > 1 || parts.includes(rootPath)) {
|
||||
return parts;
|
||||
}
|
||||
return [rootPath, path];
|
||||
}
|
||||
function contains(entries, obj, k) {
|
||||
const element = (0, xml_1.parseXML)(obj);
|
||||
for (const entry of entries) {
|
||||
const current = (0, xml_1.parseXML)(entry);
|
||||
if (element &&
|
||||
current &&
|
||||
current[k] &&
|
||||
element[k] &&
|
||||
current[k].$ &&
|
||||
element[k].$ &&
|
||||
element[k].$['android:name'] === current[k].$['android:name']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
592
node_modules/@capacitor/cli/dist/declarations.d.ts
generated
vendored
Normal file
592
node_modules/@capacitor/cli/dist/declarations.d.ts
generated
vendored
Normal file
@@ -0,0 +1,592 @@
|
||||
export interface CapacitorConfig {
|
||||
/**
|
||||
* The unique identifier of your packaged app.
|
||||
*
|
||||
* This is also known as the Bundle ID in iOS and the Application ID in
|
||||
* Android. It must be in reverse domain name notation, generally
|
||||
* representing a domain name that you or your company owns.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
appId?: string;
|
||||
/**
|
||||
* The human-friendly name of your app.
|
||||
*
|
||||
* This should be what you'd see in the App Store, but can be changed after
|
||||
* within each native platform after it is generated.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
appName?: string;
|
||||
/**
|
||||
* The directory of your compiled web assets.
|
||||
*
|
||||
* This directory should contain the final `index.html` of your app.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
webDir?: string;
|
||||
/**
|
||||
* Whether to copy the Capacitor runtime bundle or not.
|
||||
*
|
||||
* If your app is not using a bundler, set this to `true`, then Capacitor
|
||||
* will create a `capacitor.js` file that you'll need to add as a script in
|
||||
* your `index.html` file.
|
||||
*
|
||||
* It's deprecated and will be removed in Capacitor 6
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @deprecated 5.0.0
|
||||
* @default false
|
||||
*/
|
||||
bundledWebRuntime?: boolean;
|
||||
/**
|
||||
* The build configuration (as defined by the native app) under which Capacitor
|
||||
* will send statements to the log system. This applies to log statements in
|
||||
* native code as well as statements redirected from JavaScript (`console.debug`,
|
||||
* `console.error`, etc.). Enabling logging will let statements render in the
|
||||
* Xcode and Android Studio windows but can leak information on device if enabled
|
||||
* in released builds.
|
||||
*
|
||||
* 'none' = logs are never produced
|
||||
* 'debug' = logs are produced in debug builds but not production builds
|
||||
* 'production' = logs are always produced
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @default debug
|
||||
*/
|
||||
loggingBehavior?: 'none' | 'debug' | 'production';
|
||||
/**
|
||||
* User agent of Capacitor Web View.
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
overrideUserAgent?: string;
|
||||
/**
|
||||
* String to append to the original user agent of Capacitor Web View.
|
||||
*
|
||||
* This is disregarded if `overrideUserAgent` is used.
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
appendUserAgent?: string;
|
||||
/**
|
||||
* Background color of the Capacitor Web View.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
/**
|
||||
* Enable zooming within the Capacitor Web View.
|
||||
*
|
||||
* @default false
|
||||
* @since 6.0.0
|
||||
*/
|
||||
zoomEnabled?: boolean;
|
||||
android?: {
|
||||
/**
|
||||
* Specify a custom path to the native Android project.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @default android
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* User agent of Capacitor Web View on Android.
|
||||
*
|
||||
* Overrides global `overrideUserAgent` option.
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
overrideUserAgent?: string;
|
||||
/**
|
||||
* String to append to the original user agent of Capacitor Web View for Android.
|
||||
*
|
||||
* Overrides global `appendUserAgent` option.
|
||||
*
|
||||
* This is disregarded if `overrideUserAgent` is used.
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
appendUserAgent?: string;
|
||||
/**
|
||||
* Background color of the Capacitor Web View for Android.
|
||||
*
|
||||
* Overrides global `backgroundColor` option.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
/**
|
||||
* Enable zooming within the Capacitor Web View for Android.
|
||||
*
|
||||
* @default false
|
||||
* @since 6.0.0
|
||||
*/
|
||||
zoomEnabled?: boolean;
|
||||
/**
|
||||
* Enable mixed content in the Capacitor Web View for Android.
|
||||
*
|
||||
* [Mixed
|
||||
* content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content)
|
||||
* is disabled by default for security. During development, you may need to
|
||||
* enable it to allow the Web View to load files from different schemes.
|
||||
*
|
||||
* **This is not intended for use in production.**
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @default false
|
||||
*/
|
||||
allowMixedContent?: boolean;
|
||||
/**
|
||||
* This enables a simpler keyboard which may have some limitations.
|
||||
*
|
||||
* This will capture JS keys using an alternative
|
||||
* [`InputConnection`](https://developer.android.com/reference/android/view/inputmethod/InputConnection).
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @default false
|
||||
*/
|
||||
captureInput?: boolean;
|
||||
/**
|
||||
* Always enable debuggable web content.
|
||||
*
|
||||
* This is automatically enabled during development.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @default false
|
||||
*/
|
||||
webContentsDebuggingEnabled?: boolean;
|
||||
/**
|
||||
* The build configuration under which Capacitor will generate logs on Android.
|
||||
*
|
||||
* Overrides global `loggingBehavior` option.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @default debug
|
||||
*/
|
||||
loggingBehavior?: 'none' | 'debug' | 'production';
|
||||
/**
|
||||
* Allowlist of plugins to include during `npx cap sync` for Android.
|
||||
*
|
||||
* Overrides global `includePlugins` option.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
includePlugins?: string[];
|
||||
/**
|
||||
* Android flavor to use.
|
||||
*
|
||||
* If the app has flavors declared in the `build.gradle`
|
||||
* configure the flavor you want to run with `npx cap run` command.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
flavor?: string;
|
||||
/**
|
||||
* Whether to give the webview initial focus.
|
||||
*
|
||||
* @since 3.5.1
|
||||
* @default true
|
||||
*/
|
||||
initialFocus?: boolean;
|
||||
/**
|
||||
* The minimum supported webview version on Android supported by your app.
|
||||
*
|
||||
* The minimum supported cannot be lower than version `55`, which is required for Capacitor.
|
||||
*
|
||||
* If the device uses a lower WebView version, an error message will be shown on Logcat.
|
||||
* If `server.errorPath` is configured, the WebView will redirect to that file, so can be
|
||||
* used to show a custom error.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @default 60
|
||||
*/
|
||||
minWebViewVersion?: number;
|
||||
/**
|
||||
* The minimum supported Huawei webview version on Android supported by your app.
|
||||
*
|
||||
* The minimum supported cannot be lower than version `10`, which is required for Capacitor.
|
||||
*
|
||||
* If the device uses a lower WebView version, an error message will be shown on Logcat.
|
||||
* If `server.errorPath` is configured, the WebView will redirect to that file, so can be
|
||||
* used to show a custom error.
|
||||
*
|
||||
* @since 4.6.4
|
||||
* @default 10
|
||||
*/
|
||||
minHuaweiWebViewVersion?: number;
|
||||
buildOptions?: {
|
||||
/**
|
||||
* Path to your keystore
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
keystorePath?: string;
|
||||
/**
|
||||
* Password to your keystore
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
keystorePassword?: string;
|
||||
/**
|
||||
* Alias in the keystore to use
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
keystoreAlias?: string;
|
||||
/**
|
||||
* Password for the alias in the keystore to use
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
keystoreAliasPassword?: string;
|
||||
/**
|
||||
* Bundle type for your release build
|
||||
*
|
||||
* @since 4.4.0
|
||||
* @default "AAB"
|
||||
*/
|
||||
releaseType?: 'AAB' | 'APK';
|
||||
/**
|
||||
* Program to sign your build with
|
||||
*
|
||||
* @since 5.1.0
|
||||
* @default "jarsigner"
|
||||
*/
|
||||
signingType?: 'apksigner' | 'jarsigner';
|
||||
};
|
||||
/**
|
||||
* Use legacy [addJavascriptInterface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface(java.lang.Object,%20java.lang.String))
|
||||
* instead of the new and more secure [addWebMessageListener](https://developer.android.com/reference/androidx/webkit/WebViewCompat#addWebMessageListener(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E,androidx.webkit.WebViewCompat.WebMessageListener))
|
||||
*
|
||||
* @since 4.5.0
|
||||
* @default false
|
||||
*/
|
||||
useLegacyBridge?: boolean;
|
||||
};
|
||||
ios?: {
|
||||
/**
|
||||
* Specify a custom path to the native iOS project.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @default ios
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* iOS build scheme to use.
|
||||
*
|
||||
* Usually this matches your app's target in Xcode. You can use the
|
||||
* following command to list schemes:
|
||||
*
|
||||
* ```shell
|
||||
* xcodebuild -workspace ios/App/App.xcworkspace -list
|
||||
* ```
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @default App
|
||||
*/
|
||||
scheme?: string;
|
||||
/**
|
||||
* User agent of Capacitor Web View on iOS.
|
||||
*
|
||||
* Overrides global `overrideUserAgent` option.
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
overrideUserAgent?: string;
|
||||
/**
|
||||
* String to append to the original user agent of Capacitor Web View for iOS.
|
||||
*
|
||||
* Overrides global `appendUserAgent` option.
|
||||
*
|
||||
* This is disregarded if `overrideUserAgent` is used.
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
appendUserAgent?: string;
|
||||
/**
|
||||
* Background color of the Capacitor Web View for iOS.
|
||||
*
|
||||
* Overrides global `backgroundColor` option.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
backgroundColor?: string;
|
||||
/**
|
||||
* Enable zooming within the Capacitor Web View for iOS.
|
||||
*
|
||||
* @default false
|
||||
* @since 6.0.0
|
||||
*/
|
||||
zoomEnabled?: boolean;
|
||||
/**
|
||||
* Configure the scroll view's content inset adjustment behavior.
|
||||
*
|
||||
* This will set the
|
||||
* [`contentInsetAdjustmentBehavior`](https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior)
|
||||
* property on the Web View's
|
||||
* [`UIScrollView`](https://developer.apple.com/documentation/uikit/uiscrollview).
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @default never
|
||||
*/
|
||||
contentInset?: 'automatic' | 'scrollableAxes' | 'never' | 'always';
|
||||
/**
|
||||
* Configure whether the scroll view is scrollable.
|
||||
*
|
||||
* This will set the
|
||||
* [`isScrollEnabled`](https://developer.apple.com/documentation/uikit/uiscrollview/1619395-isscrollenabled)
|
||||
* property on the Web View's
|
||||
* [`UIScrollView`](https://developer.apple.com/documentation/uikit/uiscrollview).
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
scrollEnabled?: boolean;
|
||||
/**
|
||||
* Configure custom linker flags for compiling Cordova plugins.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @default []
|
||||
*/
|
||||
cordovaLinkerFlags?: string[];
|
||||
/**
|
||||
* Allow destination previews when pressing on links.
|
||||
*
|
||||
* This will set the
|
||||
* [`allowsLinkPreview`](https://developer.apple.com/documentation/webkit/wkwebview/1415000-allowslinkpreview)
|
||||
* property on the Web View, instead of using the default value.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
allowsLinkPreview?: boolean;
|
||||
/**
|
||||
* The build configuration under which Capacitor will generate logs on iOS.
|
||||
*
|
||||
* Overrides global `loggingBehavior` option.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @default debug
|
||||
*/
|
||||
loggingBehavior?: 'none' | 'debug' | 'production';
|
||||
/**
|
||||
* Allowlist of plugins to include during `npx cap sync` for iOS.
|
||||
*
|
||||
* Overrides global `includePlugins` option.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
includePlugins?: string[];
|
||||
/**
|
||||
* Sets WKWebView configuration for limitsNavigationsToAppBoundDomains.
|
||||
*
|
||||
* If the Info.plist file includes `WKAppBoundDomains` key, it's recommended to
|
||||
* set this option to true, otherwise some features won't work.
|
||||
* But as side effect, it blocks navigation outside the domains in the
|
||||
* `WKAppBoundDomains` list.
|
||||
* `localhost` (or the value configured as `server.hostname`) also needs to be
|
||||
* added to the `WKAppBoundDomains` list.
|
||||
*
|
||||
* @since 3.1.0
|
||||
* @default false
|
||||
*/
|
||||
limitsNavigationsToAppBoundDomains?: boolean;
|
||||
/**
|
||||
* The content mode for the web view to use when it loads and renders web content.
|
||||
*
|
||||
* - 'recommended': The content mode that is appropriate for the current device.
|
||||
* - 'desktop': The content mode that represents a desktop experience.
|
||||
* - 'mobile': The content mode that represents a mobile experience.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @default recommended
|
||||
*/
|
||||
preferredContentMode?: 'recommended' | 'desktop' | 'mobile';
|
||||
/**
|
||||
* Configure if Capacitor will handle local/push notifications.
|
||||
* Set to false if you want to use your own UNUserNotificationCenter to handle notifications.
|
||||
*
|
||||
* @since 4.5.0
|
||||
* @default true
|
||||
*/
|
||||
handleApplicationNotifications?: boolean;
|
||||
/**
|
||||
* Using Xcode 14.3, on iOS 16.4 and greater, enable debuggable web content for release builds.
|
||||
*
|
||||
* If not set, it's `true` for development builds.
|
||||
*
|
||||
* @since 4.8.0
|
||||
* @default false
|
||||
*/
|
||||
webContentsDebuggingEnabled?: boolean;
|
||||
};
|
||||
server?: {
|
||||
/**
|
||||
* Configure the local hostname of the device.
|
||||
*
|
||||
* It is recommended to keep this as `localhost` as it allows the use of
|
||||
* Web APIs that would otherwise require a [secure
|
||||
* context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)
|
||||
* such as
|
||||
* [`navigator.geolocation`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/geolocation)
|
||||
* and
|
||||
* [`MediaDevices.getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia).
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @default localhost
|
||||
*/
|
||||
hostname?: string;
|
||||
/**
|
||||
* Configure the local scheme on iOS.
|
||||
*
|
||||
* [Can't be set to schemes that the WKWebView already handles, such as http or https](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler)
|
||||
* This can be useful when migrating from
|
||||
* [`cordova-plugin-ionic-webview`](https://github.com/ionic-team/cordova-plugin-ionic-webview),
|
||||
* where the default scheme on iOS is `ionic`.
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @default capacitor
|
||||
*/
|
||||
iosScheme?: string;
|
||||
/**
|
||||
* Configure the local scheme on Android.
|
||||
*
|
||||
* Custom schemes on Android are unable to change the URL path as of Webview 117. Changing this value from anything other than `http` or `https` can result in your
|
||||
* application unable to resolve routing. If you must change this for some reason, consider using a hash-based url strategy, but there are no guarentees that this
|
||||
* will continue to work long term as allowing non-standard schemes to modify query parameters and url fragments is only allowed for compatibility reasons.
|
||||
* https://ionic.io/blog/capacitor-android-customscheme-issue-with-chrome-117
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @default https
|
||||
*/
|
||||
androidScheme?: string;
|
||||
/**
|
||||
* Load an external URL in the Web View.
|
||||
*
|
||||
* This is intended for use with live-reload servers.
|
||||
*
|
||||
* **This is not intended for use in production.**
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
url?: string;
|
||||
/**
|
||||
* Allow cleartext traffic in the Web View.
|
||||
*
|
||||
* On Android, all cleartext traffic is disabled by default as of API 28.
|
||||
*
|
||||
* This is intended for use with live-reload servers where unencrypted HTTP
|
||||
* traffic is often used.
|
||||
*
|
||||
* **This is not intended for use in production.**
|
||||
*
|
||||
* @since 1.5.0
|
||||
* @default false
|
||||
*/
|
||||
cleartext?: boolean;
|
||||
/**
|
||||
* Set additional URLs the Web View can navigate to.
|
||||
*
|
||||
* By default, all external URLs are opened in the external browser (not
|
||||
* the Web View).
|
||||
*
|
||||
* **This is not intended for use in production.**
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @default []
|
||||
*/
|
||||
allowNavigation?: string[];
|
||||
/**
|
||||
* Specify path to a local html page to display in case of errors.
|
||||
* On Android the html file won't have access to Capacitor plugins.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @default null
|
||||
*/
|
||||
errorPath?: string;
|
||||
};
|
||||
cordova?: {
|
||||
/**
|
||||
* Populates <access> tags in the config.xml with the origin set to
|
||||
* the values entered here.
|
||||
* If not provided, a single <access origin="*" /> tag gets included.
|
||||
* It only has effect on a few Cordova plugins that respect the whitelist.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
accessOrigins?: string[];
|
||||
/**
|
||||
* Configure Cordova preferences.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
preferences?: {
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
/**
|
||||
* List of Cordova plugins that need to be static but are not
|
||||
* already in the static plugin list.
|
||||
*
|
||||
* It's deprecated and will be removed in Capacitor 7
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @deprecated 6.1.1
|
||||
*/
|
||||
staticPlugins?: string[];
|
||||
};
|
||||
/**
|
||||
* Configure plugins.
|
||||
*
|
||||
* This is an object with configuration values specified by plugin class
|
||||
* name.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
plugins?: PluginsConfig;
|
||||
/**
|
||||
* Allowlist of plugins to include during `npx cap sync`.
|
||||
*
|
||||
* This should be an array of strings representing the npm package name of
|
||||
* plugins to include when running `npx cap sync`. If unset, Capacitor will
|
||||
* inspect `package.json` for a list of potential plugins.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
includePlugins?: string[];
|
||||
}
|
||||
export interface PluginsConfig {
|
||||
/**
|
||||
* Plugin configuration by class name.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
[key: string]: {
|
||||
[key: string]: any;
|
||||
} | undefined;
|
||||
/**
|
||||
* Capacitor Cookies plugin configuration
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
CapacitorCookies?: {
|
||||
/**
|
||||
* Enable CapacitorCookies to override the global `document.cookie` on native.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
enabled?: boolean;
|
||||
};
|
||||
/**
|
||||
* Capacitor Http plugin configuration
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
CapacitorHttp?: {
|
||||
/**
|
||||
* Enable CapacitorHttp to override the global `fetch` and `XMLHttpRequest` on native.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
enabled?: boolean;
|
||||
};
|
||||
}
|
||||
2
node_modules/@capacitor/cli/dist/declarations.js
generated
vendored
Normal file
2
node_modules/@capacitor/cli/dist/declarations.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
2
node_modules/@capacitor/cli/dist/definitions.js
generated
vendored
Normal file
2
node_modules/@capacitor/cli/dist/definitions.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
27
node_modules/@capacitor/cli/dist/errors.js
generated
vendored
Normal file
27
node_modules/@capacitor/cli/dist/errors.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isFatal = exports.fatal = exports.FatalException = exports.BaseException = void 0;
|
||||
class BaseException extends Error {
|
||||
constructor(message, code) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
exports.BaseException = BaseException;
|
||||
class FatalException extends BaseException {
|
||||
constructor(message, exitCode = 1) {
|
||||
super(message, 'FATAL');
|
||||
this.message = message;
|
||||
this.exitCode = exitCode;
|
||||
}
|
||||
}
|
||||
exports.FatalException = FatalException;
|
||||
function fatal(message) {
|
||||
throw new FatalException(message);
|
||||
}
|
||||
exports.fatal = fatal;
|
||||
function isFatal(e) {
|
||||
return e && e instanceof FatalException;
|
||||
}
|
||||
exports.isFatal = isFatal;
|
||||
109
node_modules/@capacitor/cli/dist/framework-configs.js
generated
vendored
Normal file
109
node_modules/@capacitor/cli/dist/framework-configs.js
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.detectFramework = void 0;
|
||||
const FRAMEWORK_CONFIGS = [
|
||||
{
|
||||
name: 'Angular',
|
||||
isMatch: config => hasDependency(config, '@angular/cli'),
|
||||
webDir: 'dist',
|
||||
priority: 3,
|
||||
},
|
||||
{
|
||||
name: 'Create React App',
|
||||
isMatch: config => hasDependency(config, 'react-scripts'),
|
||||
webDir: 'build',
|
||||
priority: 3,
|
||||
},
|
||||
{
|
||||
name: 'Ember',
|
||||
isMatch: config => hasDependency(config, 'ember-cli'),
|
||||
webDir: 'dist',
|
||||
priority: 3,
|
||||
},
|
||||
{
|
||||
name: 'Gatsby',
|
||||
isMatch: config => hasDependency(config, 'gatsby'),
|
||||
webDir: 'public',
|
||||
priority: 2,
|
||||
},
|
||||
{
|
||||
name: 'Ionic Angular',
|
||||
isMatch: config => hasDependency(config, '@ionic/angular'),
|
||||
webDir: 'www',
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
name: 'Ionic React',
|
||||
isMatch: config => hasDependency(config, '@ionic/react'),
|
||||
webDir: 'build',
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
name: 'Ionic Vue',
|
||||
isMatch: config => hasDependency(config, '@ionic/vue'),
|
||||
webDir: 'public',
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
name: 'Next',
|
||||
isMatch: config => hasDependency(config, 'next'),
|
||||
webDir: 'public',
|
||||
priority: 2,
|
||||
},
|
||||
{
|
||||
name: 'Preact',
|
||||
isMatch: config => hasDependency(config, 'preact-cli'),
|
||||
webDir: 'build',
|
||||
priority: 3,
|
||||
},
|
||||
{
|
||||
name: 'Stencil',
|
||||
isMatch: config => hasDependency(config, '@stencil/core'),
|
||||
webDir: 'www',
|
||||
priority: 3,
|
||||
},
|
||||
{
|
||||
name: 'Svelte',
|
||||
isMatch: config => hasDependency(config, 'svelte') && hasDependency(config, 'sirv-cli'),
|
||||
webDir: 'public',
|
||||
priority: 3,
|
||||
},
|
||||
{
|
||||
name: 'Vite',
|
||||
isMatch: config => hasDependency(config, 'vite'),
|
||||
webDir: 'dist',
|
||||
priority: 2,
|
||||
},
|
||||
{
|
||||
name: 'Vue',
|
||||
isMatch: config => hasDependency(config, '@vue/cli-service'),
|
||||
webDir: 'dist',
|
||||
priority: 3,
|
||||
},
|
||||
];
|
||||
function detectFramework(config) {
|
||||
const frameworks = FRAMEWORK_CONFIGS.filter(f => f.isMatch(config)).sort((a, b) => {
|
||||
if (a.priority < b.priority)
|
||||
return -1;
|
||||
if (a.priority > b.priority)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
return frameworks[0];
|
||||
}
|
||||
exports.detectFramework = detectFramework;
|
||||
function hasDependency(config, depName) {
|
||||
const deps = getDependencies(config);
|
||||
return deps.includes(depName);
|
||||
}
|
||||
function getDependencies(config) {
|
||||
var _a, _b, _c, _d;
|
||||
const deps = [];
|
||||
if ((_b = (_a = config === null || config === void 0 ? void 0 : config.app) === null || _a === void 0 ? void 0 : _a.package) === null || _b === void 0 ? void 0 : _b.dependencies) {
|
||||
deps.push(...Object.keys(config.app.package.dependencies));
|
||||
}
|
||||
if ((_d = (_c = config === null || config === void 0 ? void 0 : config.app) === null || _c === void 0 ? void 0 : _c.package) === null || _d === void 0 ? void 0 : _d.devDependencies) {
|
||||
deps.push(...Object.keys(config.app.package.devDependencies));
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
224
node_modules/@capacitor/cli/dist/index.js
generated
vendored
Normal file
224
node_modules/@capacitor/cli/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.runProgram = exports.run = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const commander_1 = require("commander");
|
||||
const path_1 = require("path");
|
||||
const colors_1 = tslib_1.__importDefault(require("./colors"));
|
||||
const config_1 = require("./config");
|
||||
const errors_1 = require("./errors");
|
||||
const ipc_1 = require("./ipc");
|
||||
const log_1 = require("./log");
|
||||
const telemetry_1 = require("./telemetry");
|
||||
const cli_1 = require("./util/cli");
|
||||
const emoji_1 = require("./util/emoji");
|
||||
process.on('unhandledRejection', error => {
|
||||
console.error(colors_1.default.failure('[fatal]'), error);
|
||||
});
|
||||
process.on('message', ipc_1.receive);
|
||||
async function run() {
|
||||
try {
|
||||
const config = await (0, config_1.loadConfig)();
|
||||
runProgram(config);
|
||||
}
|
||||
catch (e) {
|
||||
process.exitCode = (0, errors_1.isFatal)(e) ? e.exitCode : 1;
|
||||
log_1.logger.error(e.message ? e.message : String(e));
|
||||
}
|
||||
}
|
||||
exports.run = run;
|
||||
function runProgram(config) {
|
||||
commander_1.program.version(config.cli.package.version);
|
||||
commander_1.program
|
||||
.command('config', { hidden: true })
|
||||
.description(`print evaluated Capacitor config`)
|
||||
.option('--json', 'Print in JSON format')
|
||||
.action((0, cli_1.wrapAction)(async ({ json }) => {
|
||||
const { configCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/config')));
|
||||
await configCommand(config, json);
|
||||
}));
|
||||
commander_1.program
|
||||
.command('create [directory] [name] [id]', { hidden: true })
|
||||
.description('Creates a new Capacitor project')
|
||||
.action((0, cli_1.wrapAction)(async () => {
|
||||
const { createCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/create')));
|
||||
await createCommand();
|
||||
}));
|
||||
commander_1.program
|
||||
.command('init [appName] [appId]')
|
||||
.description(`Initialize Capacitor configuration`)
|
||||
.option('--web-dir <value>', 'Optional: Directory of your projects built web assets')
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (appName, appId, { webDir }) => {
|
||||
const { initCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/init')));
|
||||
await initCommand(config, appName, appId, webDir);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('serve', { hidden: true })
|
||||
.description('Serves a Capacitor Progressive Web App in the browser')
|
||||
.action((0, cli_1.wrapAction)(async () => {
|
||||
const { serveCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/serve')));
|
||||
await serveCommand();
|
||||
}));
|
||||
commander_1.program
|
||||
.command('sync [platform]')
|
||||
.description(`${colors_1.default.input('copy')} + ${colors_1.default.input('update')}`)
|
||||
.option('--deployment', 'Optional: if provided, pod install will use --deployment option')
|
||||
.option('--inline', 'Optional: if true, all source maps will be inlined for easier debugging on mobile devices', false)
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { deployment, inline }) => {
|
||||
(0, config_1.checkExternalConfig)(config.app);
|
||||
const { syncCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/sync')));
|
||||
await syncCommand(config, platform, deployment, inline);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('update [platform]')
|
||||
.description(`updates the native plugins and dependencies based on ${colors_1.default.strong('package.json')}`)
|
||||
.option('--deployment', 'Optional: if provided, pod install will use --deployment option')
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { deployment }) => {
|
||||
(0, config_1.checkExternalConfig)(config.app);
|
||||
const { updateCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/update')));
|
||||
await updateCommand(config, platform, deployment);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('copy [platform]')
|
||||
.description('copies the web app build into the native app')
|
||||
.option('--inline', 'Optional: if true, all source maps will be inlined for easier debugging on mobile devices', false)
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { inline }) => {
|
||||
(0, config_1.checkExternalConfig)(config.app);
|
||||
const { copyCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/copy')));
|
||||
await copyCommand(config, platform, inline);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('build <platform>')
|
||||
.description('builds the release version of the selected platform')
|
||||
.option('--scheme <schemeToBuild>', 'iOS Scheme to build')
|
||||
.option('--flavor <flavorToBuild>', 'Android Flavor to build')
|
||||
.option('--keystorepath <keystorePath>', 'Path to the keystore')
|
||||
.option('--keystorepass <keystorePass>', 'Password to the keystore')
|
||||
.option('--keystorealias <keystoreAlias>', 'Key Alias in the keystore')
|
||||
.option('--configuration <name>', 'Configuration name of the iOS Scheme')
|
||||
.option('--keystorealiaspass <keystoreAliasPass>', 'Password for the Key Alias')
|
||||
.addOption(new commander_1.Option('--androidreleasetype <androidreleasetype>', 'Android release type; APK or AAB').choices(['AAB', 'APK']))
|
||||
.addOption(new commander_1.Option('--signing-type <signingtype>', 'Program used to sign apps (default: jarsigner)').choices(['apksigner', 'jarsigner']))
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { scheme, flavor, keystorepath, keystorepass, keystorealias, keystorealiaspass, androidreleasetype, signingType, configuration, }) => {
|
||||
const { buildCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/build')));
|
||||
await buildCommand(config, platform, {
|
||||
scheme,
|
||||
flavor,
|
||||
keystorepath,
|
||||
keystorepass,
|
||||
keystorealias,
|
||||
keystorealiaspass,
|
||||
androidreleasetype,
|
||||
signingtype: signingType,
|
||||
configuration,
|
||||
});
|
||||
})));
|
||||
commander_1.program
|
||||
.command(`run [platform]`)
|
||||
.description(`runs ${colors_1.default.input('sync')}, then builds and deploys the native app`)
|
||||
.option('--scheme <schemeName>', 'set the scheme of the iOS project')
|
||||
.option('--flavor <flavorName>', 'set the flavor of the Android project (flavor dimensions not yet supported)')
|
||||
.option('--list', 'list targets, then quit')
|
||||
// TODO: remove once --json is a hidden option (https://github.com/tj/commander.js/issues/1106)
|
||||
.allowUnknownOption(true)
|
||||
.option('--target <id>', 'use a specific target')
|
||||
.option('--no-sync', `do not run ${colors_1.default.input('sync')}`)
|
||||
.option('--forwardPorts <port:port>', 'Automatically run "adb reverse" for better live-reloading support')
|
||||
.option('-l, --live-reload', 'Enable Live Reload')
|
||||
.option('--host <host>', 'Host used for live reload')
|
||||
.option('--port <port>', 'Port used for live reload')
|
||||
.option('--configuration <name>', 'Configuration name of the iOS Scheme')
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { scheme, flavor, list, target, sync, forwardPorts, liveReload, host, port, configuration, }) => {
|
||||
const { runCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/run')));
|
||||
await runCommand(config, platform, {
|
||||
scheme,
|
||||
flavor,
|
||||
list,
|
||||
target,
|
||||
sync,
|
||||
forwardPorts,
|
||||
liveReload,
|
||||
host,
|
||||
port,
|
||||
configuration,
|
||||
});
|
||||
})));
|
||||
commander_1.program
|
||||
.command('open [platform]')
|
||||
.description('opens the native project workspace (Xcode for iOS)')
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform) => {
|
||||
const { openCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/open')));
|
||||
await openCommand(config, platform);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('add [platform]')
|
||||
.description('add a native platform project')
|
||||
.option('--packagemanager <packageManager>', 'The package manager to use for dependency installs (Cocoapods, SPM **experimental**)')
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { packagemanager }) => {
|
||||
(0, config_1.checkExternalConfig)(config.app);
|
||||
const { addCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/add')));
|
||||
const configWritable = config;
|
||||
if (packagemanager === 'SPM') {
|
||||
configWritable.cli.assets.ios.platformTemplateArchive =
|
||||
'ios-spm-template.tar.gz';
|
||||
configWritable.cli.assets.ios.platformTemplateArchiveAbs = (0, path_1.resolve)(configWritable.cli.assetsDirAbs, configWritable.cli.assets.ios.platformTemplateArchive);
|
||||
}
|
||||
await addCommand(configWritable, platform);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('ls [platform]')
|
||||
.description('list installed Cordova and Capacitor plugins')
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform) => {
|
||||
(0, config_1.checkExternalConfig)(config.app);
|
||||
const { listCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/list')));
|
||||
await listCommand(config, platform);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('doctor [platform]')
|
||||
.description('checks the current setup for common errors')
|
||||
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform) => {
|
||||
(0, config_1.checkExternalConfig)(config.app);
|
||||
const { doctorCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/doctor')));
|
||||
await doctorCommand(config, platform);
|
||||
})));
|
||||
commander_1.program
|
||||
.command('telemetry [on|off]', { hidden: true })
|
||||
.description('enable or disable telemetry')
|
||||
.action((0, cli_1.wrapAction)(async (onOrOff) => {
|
||||
const { telemetryCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/telemetry')));
|
||||
await telemetryCommand(onOrOff);
|
||||
}));
|
||||
commander_1.program
|
||||
.command('📡', { hidden: true })
|
||||
.description('IPC receiver command')
|
||||
.action(() => {
|
||||
// no-op: IPC messages are received via `process.on('message')`
|
||||
});
|
||||
commander_1.program
|
||||
.command('plugin:generate', { hidden: true })
|
||||
.description('start a new Capacitor plugin')
|
||||
.action((0, cli_1.wrapAction)(async () => {
|
||||
const { newPluginCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/new-plugin')));
|
||||
await newPluginCommand();
|
||||
}));
|
||||
commander_1.program
|
||||
.command('migrate')
|
||||
.option('--noprompt', 'do not prompt for confirmation')
|
||||
.option('--packagemanager <packageManager>', 'The package manager to use for dependency installs (npm, pnpm, yarn)')
|
||||
.description('Migrate your current Capacitor app to the latest major version of Capacitor.')
|
||||
.action((0, cli_1.wrapAction)(async ({ noprompt, packagemanager }) => {
|
||||
const { migrateCommand } = await Promise.resolve().then(() => tslib_1.__importStar(require('./tasks/migrate')));
|
||||
await migrateCommand(config, noprompt, packagemanager);
|
||||
}));
|
||||
commander_1.program.arguments('[command]').action((0, cli_1.wrapAction)(async (cmd) => {
|
||||
if (typeof cmd === 'undefined') {
|
||||
log_1.output.write(`\n ${(0, emoji_1.emoji)('⚡️', '--')} ${colors_1.default.strong('Capacitor - Cross-Platform apps with JavaScript and the Web')} ${(0, emoji_1.emoji)('⚡️', '--')}\n\n`);
|
||||
commander_1.program.outputHelp();
|
||||
}
|
||||
else {
|
||||
(0, errors_1.fatal)(`Unknown command: ${colors_1.default.input(cmd)}`);
|
||||
}
|
||||
}));
|
||||
commander_1.program.parse(process.argv);
|
||||
}
|
||||
exports.runProgram = runProgram;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user