NLDClient-yudde/ProjectNLD/Assets/ThirdParty/UniWebView/Editor/AndroidManifest.cs

251 lines
11 KiB
C#
Raw Normal View History

using System.Xml;
using System.Text;
using System;
using UnityEngine;
internal class UniWebViewAndroidXmlDocument : XmlDocument {
private readonly string path;
protected readonly XmlNamespaceManager nameSpaceManager;
protected const string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
protected UniWebViewAndroidXmlDocument(string path) {
this.path = path;
using (var reader = new XmlTextReader(path)) {
reader.Read();
Load(reader);
}
nameSpaceManager = new XmlNamespaceManager(NameTable);
nameSpaceManager.AddNamespace("android", AndroidXmlNamespace);
}
public void Save() {
SaveAs(path);
}
private void SaveAs(string path)
{
using var writer = new XmlTextWriter(path, new UTF8Encoding(false));
writer.Formatting = Formatting.Indented;
Save(writer);
}
}
internal class UniWebViewAndroidManifest : UniWebViewAndroidXmlDocument {
private readonly XmlElement manifestElement;
private readonly XmlElement applicationElement;
public UniWebViewAndroidManifest(string path) : base(path) {
manifestElement = SelectSingleNode("/manifest") as XmlElement;
applicationElement = SelectSingleNode("/manifest/application") as XmlElement;
}
private XmlAttribute CreateAndroidAttribute(string key, string value) {
XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace);
attr.Value = value;
return attr;
}
internal XmlNode GetActivityWithLaunchIntent() {
return
SelectSingleNode(
"/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and "
+ "intent-filter/category/@android:name='android.intent.category.LAUNCHER']",
nameSpaceManager);
}
internal bool SetUsesCleartextTraffic() {
var changed = false;
if (applicationElement.GetAttribute("usesCleartextTraffic", AndroidXmlNamespace) != "true") {
applicationElement.SetAttribute("usesCleartextTraffic", AndroidXmlNamespace, "true");
changed = true;
}
return changed;
}
internal bool SetHardwareAccelerated() {
var changed = false;
var activity = GetActivityWithLaunchIntent() as XmlElement;
if (activity == null)
{
Debug.LogError(
"There is no launch intent activity in the AndroidManifest.xml." +
" Please check your AndroidManifest.xml file and make sure it has a main activity with intent filter");
return false;
}
if (activity.GetAttribute("hardwareAccelerated", AndroidXmlNamespace) != "true") {
activity.SetAttribute("hardwareAccelerated", AndroidXmlNamespace, "true");
changed = true;
}
return changed;
}
internal bool AddCameraPermission() {
var changed = false;
var cameraPermission = "/manifest/uses-permission[@android:name='android.permission.CAMERA']";
var cameraPermissionNode = SelectNodes(cameraPermission, nameSpaceManager);
if (cameraPermissionNode == null || cameraPermissionNode.Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.CAMERA"));
manifestElement.AppendChild(elem);
changed = true;
}
var hardwareCamera = "/manifest/uses-feature[@android:name='android.hardware.camera']";
var hardwareCameraNode = SelectNodes(hardwareCamera, nameSpaceManager);
if (hardwareCameraNode == null || hardwareCameraNode.Count == 0) {
var elem = CreateElement("uses-feature");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.hardware.camera"));
manifestElement.AppendChild(elem);
changed = true;
}
return changed;
}
internal bool AddMicrophonePermission() {
bool changed = false;
var microphonePermission = "/manifest/uses-permission[@android:name='android.permission.MICROPHONE']";
var microphonePermissionNode = SelectNodes(microphonePermission, nameSpaceManager);
if (microphonePermissionNode == null || microphonePermissionNode.Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.MICROPHONE"));
manifestElement.AppendChild(elem);
changed = true;
}
var microphoneHardware = "/manifest/uses-feature[@android:name='android.hardware.microphone']";
var microphoneHardwareNode = SelectNodes(microphoneHardware, nameSpaceManager);
if (microphoneHardwareNode == null || microphoneHardwareNode.Count == 0) {
var elem = CreateElement("uses-feature");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.hardware.microphone"));
manifestElement.AppendChild(elem);
changed = true;
}
return changed;
}
internal bool AddReadExternalStoragePermission() {
var changed = false;
var externalPermission = "/manifest/uses-permission[@android:name='android.permission.READ_EXTERNAL_STORAGE']";
var externalNode = SelectNodes(externalPermission, nameSpaceManager);
if (externalNode == null || externalNode.Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.READ_EXTERNAL_STORAGE"));
manifestElement.AppendChild(elem);
changed = true;
}
return changed;
}
internal bool AddWriteExternalStoragePermission() {
var changed = false;
var externalPermission = "/manifest/uses-permission[@android:name='android.permission.WRITE_EXTERNAL_STORAGE']";
var externalNode = SelectNodes(externalPermission, nameSpaceManager);
if (externalNode == null || externalNode.Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.WRITE_EXTERNAL_STORAGE"));
manifestElement.AppendChild(elem);
changed = true;
}
return changed;
}
internal bool AddAccessFineLocationPermission() {
var changed = false;
var locationPermission = "/manifest/uses-permission[@android:name='android.permission.ACCESS_FINE_LOCATION']";
var locationNode = SelectNodes(locationPermission, nameSpaceManager);
if (locationNode == null || locationNode.Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.ACCESS_FINE_LOCATION"));
manifestElement.AppendChild(elem);
changed = true;
}
return changed;
}
internal bool AddAuthCallbacksIntentFilter(string[] authCallbackUrls) {
var changed = false;
XmlElement authActivityNode;
if (authCallbackUrls.Length > 0) {
var authActivity = "/manifest/application/activity[@android:name='com.onevcat.uniwebview.UniWebViewAuthenticationActivity']";
var list = SelectNodes(authActivity, nameSpaceManager);
if (list == null || list.Count == 0) {
var created = CreateElement("activity");
created.SetAttribute("name", AndroidXmlNamespace, "com.onevcat.uniwebview.UniWebViewAuthenticationActivity");
created.SetAttribute("exported", AndroidXmlNamespace, "true");
created.SetAttribute("launchMode", AndroidXmlNamespace, "singleTask");
created.SetAttribute("configChanges", AndroidXmlNamespace, "orientation|screenSize|keyboardHidden");
authActivityNode = created;
} else {
authActivityNode = list[0] as XmlElement;
}
} else {
return false;
}
foreach (var url in authCallbackUrls) {
var intentFilter = CreateIntentFilter(url);
if (intentFilter != null) {
authActivityNode?.AppendChild(intentFilter);
changed = true;
}
}
if (authActivityNode != null) {
applicationElement.AppendChild(authActivityNode);
}
return changed;
}
private XmlElement CreateIntentFilter(string url) {
var uri = new Uri(url);
var scheme = uri.Scheme;
if (string.IsNullOrEmpty(scheme)) {
Debug.LogError("<UniWebView> Auth callback url contains an empty scheme. Please check the url: " + url);
return null;
}
var filter = CreateElement("intent-filter");
// Check if the scheme is HTTPS
// For Android App Links, we need to set the autoVerify attribute to true
// This allows Android to automatically verify the association between the app and the website
// Note: Only applies to HTTPS URLs, although Android App Links also support HTTP, we do not want it in
// UniWebView.
// https://developer.android.com/training/app-links
if (scheme == "https") {
filter.SetAttribute("autoVerify", AndroidXmlNamespace, "true");
}
var action = CreateElement("action");
action.SetAttribute("name", AndroidXmlNamespace, "android.intent.action.VIEW");
filter.AppendChild(action);
var defaultCategory = CreateElement("category");
defaultCategory.SetAttribute("name", AndroidXmlNamespace, "android.intent.category.DEFAULT");
filter.AppendChild(defaultCategory);
var browseCategory = CreateElement("category");
browseCategory.SetAttribute("name", AndroidXmlNamespace, "android.intent.category.BROWSABLE");
filter.AppendChild(browseCategory);
var data = CreateElement("data");
data.SetAttribute("scheme", AndroidXmlNamespace, scheme);
if (!String.IsNullOrEmpty(uri.Host)) {
data.SetAttribute("host", AndroidXmlNamespace, uri.Host);
}
if (uri.Port != -1) {
// For HTTPS, the default port is 443. We only need to set the port explicitly
// if we're not using HTTPS or if we're using a non-standard HTTPS port.
if (scheme != "https" || uri.Port != 443) {
data.SetAttribute("port", AndroidXmlNamespace, uri.Port.ToString());
}
}
if (!string.IsNullOrEmpty(uri.PathAndQuery) && uri.PathAndQuery != "/") {
data.SetAttribute("path", AndroidXmlNamespace, uri.PathAndQuery);
}
filter.AppendChild(data);
return filter;
}
}