移动端浏览器

This commit is contained in:
Caiyanpeng committed 2021-12-23 09:54:14 +08:00
1 parent 5c2c22b675
commit 4260cf05d3
82 files changed
+7477 -1083

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
using System.Xml;
using System.Collections;
using System.Text;
using System.IO;
internal class UniWebViewAndroidXmlDocument : XmlDocument {
private string path;
protected XmlNamespaceManager nameSpaceManager;
public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
public 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 string Save() {
return SaveAs(path);
}
public string SaveAs(string path) {
using (var writer = new XmlTextWriter(path, new UTF8Encoding(false))) {
writer.Formatting = Formatting.Indented;
Save(writer);
}
return path;
}
}
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() {
bool changed = false;
if (ApplicationElement.GetAttribute("usesCleartextTraffic", AndroidXmlNamespace) != "true") {
ApplicationElement.SetAttribute("usesCleartextTraffic", AndroidXmlNamespace, "true");
changed = true;
}
return changed;
}
internal bool SetHardwareAccelerated() {
bool changed = false;
var activity = GetActivityWithLaunchIntent() as XmlElement;
if (activity.GetAttribute("hardwareAccelerated", AndroidXmlNamespace) != "true") {
activity.SetAttribute("hardwareAccelerated", AndroidXmlNamespace, "true");
changed = true;
}
return changed;
}
internal bool AddCameraPermission() {
bool changed = false;
if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.CAMERA']", nameSpaceManager).Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.CAMERA"));
ManifestElement.AppendChild(elem);
changed = true;
}
if (SelectNodes("/manifest/uses-feature[@android:name='android.hardware.camera']", nameSpaceManager).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;
if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.MICROPHONE']", nameSpaceManager).Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.MICROPHONE"));
ManifestElement.AppendChild(elem);
changed = true;
}
if (SelectNodes("/manifest/uses-feature[@android:name='android.hardware.microphone']", nameSpaceManager).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() {
bool changed = false;
if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.READ_EXTERNAL_STORAGE']", nameSpaceManager).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() {
bool changed = false;
if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.WRITE_EXTERNAL_STORAGE']", nameSpaceManager).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() {
bool changed = false;
if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.ACCESS_FINE_LOCATION']", nameSpaceManager).Count == 0) {
var elem = CreateElement("uses-permission");
elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.ACCESS_FINE_LOCATION"));
ManifestElement.AppendChild(elem);
changed = true;
}
return changed;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d09054fcc76964295a49868566075973
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+344
View File
@@ -0,0 +1,344 @@
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using System;
public class UniWebViewGradleConfig
{
private UniWebViewGradleNode m_root;
private String m_filePath;
private UniWebViewGradleNode m_curNode;
public UniWebViewGradleNode ROOT
{
get { return m_root; }
}
public UniWebViewGradleConfig(string filePath)
{
string file = File.ReadAllText(filePath);
TextReader reader = new StringReader(file);
m_filePath = filePath;
m_root = new UniWebViewGradleNode("root");
m_curNode = m_root;
StringBuilder str = new StringBuilder();
bool inDoubleQuote = false;
bool inSingleQuote = false;
while (reader.Peek() > 0)
{
char c = (char)reader.Read();
switch (c)
{
// case '/':
// if (reader.Peek() == '/')
// {
// reader.Read();
// string comment = reader.ReadLine();
// Debug.Log("Comment line: " + comment);
// m_curNode.AppendChildNode(new UniWebViewGradleCommentNode(comment, m_curNode));
// }
// else
// {
// str.Append('/');
// }
// break;
case '\n':
case '\r':
{
var strf = FormatStr(str);
if (!string.IsNullOrEmpty(strf))
{
m_curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, m_curNode));
}
}
str = new StringBuilder();
break;
case '\t':
{
var strf = FormatStr(str);
if (!string.IsNullOrEmpty(strf))
{
str.Append(" ");
}
break;
}
case '{':
{
if (inDoubleQuote || inSingleQuote) {
break;
}
var n = FormatStr(str);
if (!string.IsNullOrEmpty(n))
{
UniWebViewGradleNode node = new UniWebViewGradleNode(n, m_curNode);
m_curNode.AppendChildNode(node);
m_curNode = node;
}
}
str = new StringBuilder();
break;
case '}':
{
if (inDoubleQuote || inSingleQuote) {
break;
}
var strf = FormatStr(str);
if (!string.IsNullOrEmpty(strf))
{
m_curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, m_curNode));
}
m_curNode = m_curNode.PARENT;
}
str = new StringBuilder();
break;
case '\"':
inDoubleQuote = !inDoubleQuote;
str.Append(c);
break;
case '\'':
inSingleQuote = !inSingleQuote;
str.Append(c);
break;
default:
str.Append(c);
break;
}
}
//Debug.Log("Gradle parse done!");
}
public void Save(string path = null)
{
if (path == null)
path = m_filePath;
File.WriteAllText(path, Print());
//Debug.Log("Gradle parse done! " + path);
}
private string FormatStr(StringBuilder sb)
{
string str = sb.ToString();
str = str.Trim();
return str;
}
public string Print()
{
StringBuilder sb = new StringBuilder();
printNode(sb, m_root, -1);
return sb.ToString();
}
private string GetLevelIndent(int level)
{
if (level <= 0) return "";
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < level; i++)
{
sb.Append('\t');
}
return sb.ToString();
}
private void printNode(StringBuilder stringBuilder, UniWebViewGradleNode node, int level)
{
if (node.PARENT != null)
{
if (node is UniWebViewGradleCommentNode)
{
stringBuilder.Append("\n" + GetLevelIndent(level) + @"//" + node.NAME);
}
else
{
stringBuilder.Append("\n" + GetLevelIndent(level) + node.NAME);
}
}
if (!(node is UniWebViewGradleContentNode) && !(node is UniWebViewGradleCommentNode))
{
if (node.PARENT != null)
{
stringBuilder.Append(" {");
}
foreach (var c in node.CHILDREN)
{
printNode(stringBuilder, c, level + 1);
}
if (node.PARENT != null)
{
stringBuilder.Append("\n" + GetLevelIndent(level) + "}");
}
}
}
}
public class UniWebViewGradleNode
{
protected List<UniWebViewGradleNode> m_children = new List<UniWebViewGradleNode>();
protected UniWebViewGradleNode m_parent;
protected String m_name;
public UniWebViewGradleNode PARENT
{
get { return m_parent; }
}
public string NAME
{
get { return m_name; }
}
public List<UniWebViewGradleNode> CHILDREN
{
get { return m_children; }
}
public UniWebViewGradleNode(string name, UniWebViewGradleNode parent = null)
{
m_parent = parent;
m_name = name;
}
public void Each(Action<UniWebViewGradleNode> f)
{
f(this);
foreach (var n in m_children)
{
n.Each(f);
}
}
public void AppendChildNode(UniWebViewGradleNode node)
{
if (m_children == null) m_children = new List<UniWebViewGradleNode>();
m_children.Add(node);
node.m_parent = this;
}
public UniWebViewGradleNode TryGetNode(string path)
{
string[] subpath = path.Split('/');
UniWebViewGradleNode cnode = this;
for (int i = 0; i < subpath.Length; i++)
{
var p = subpath[i];
if (string.IsNullOrEmpty(p)) continue;
UniWebViewGradleNode tnode = cnode.FindChildNodeByName(p);
if (tnode == null)
{
Debug.Log("Can't find Node:" + p);
return null;
}
cnode = tnode;
tnode = null;
}
return cnode;
}
public UniWebViewGradleNode FindChildNodeByName(string name)
{
foreach (var n in m_children)
{
if (n is UniWebViewGradleCommentNode || n is UniWebViewGradleContentNode)
continue;
if (n.NAME == name)
return n;
}
return null;
}
public bool ReplaceContenStartsWith(string patten, string value)
{
foreach (var n in m_children)
{
if (!(n is UniWebViewGradleContentNode)) continue;
if (n.m_name.StartsWith(patten))
{
n.m_name = value;
return true;
}
}
return false;
}
public UniWebViewGradleContentNode ReplaceContenOrAddStartsWith(string patten, string value)
{
foreach (var n in m_children)
{
if (!(n is UniWebViewGradleContentNode)) continue;
if (n.m_name.StartsWith(patten))
{
n.m_name = value;
return (UniWebViewGradleContentNode)n;
}
}
return AppendContentNode(value);
}
/// <summary>
/// 添加子节点
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
public UniWebViewGradleContentNode AppendContentNode(string content)
{
foreach (var n in m_children)
{
if (!(n is UniWebViewGradleContentNode)) continue;
if (n.m_name == content)
{
Debug.Log("UniWebViewGradleContentNode with " + content + " already exists!");
return null;
}
}
UniWebViewGradleContentNode cnode = new UniWebViewGradleContentNode(content, this);
AppendChildNode(cnode);
return cnode;
}
public bool RemoveContentNode(string contentPattern)
{
for(int i=0;i<m_children.Count;i++)
{
if (!(m_children[i] is UniWebViewGradleContentNode)) continue;
if(m_children[i].m_name.Contains(contentPattern))
{
m_children.RemoveAt(i);
return true;
}
}
return false;
}
}
public sealed class UniWebViewGradleContentNode : UniWebViewGradleNode
{
public UniWebViewGradleContentNode(String content, UniWebViewGradleNode parent) : base(content, parent)
{
}
public void SetContent(string content)
{
m_name = content;
}
}
public sealed class UniWebViewGradleCommentNode : UniWebViewGradleNode
{
public UniWebViewGradleCommentNode(String content, UniWebViewGradleNode parent) : base(content, parent)
{
}
public string GetComment()
{
return m_name;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a73a3268e19f44f7be818c5db9af457
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Text;
using System.IO;
public class UniWebViewGradlePropertyPatcher {
public static void Patch(string filePath) {
string[] lines = File.ReadAllLines(filePath);
bool hasAndroidXProperty = lines.Any(text => text.Contains("android.useAndroidX"));
bool hasJetifierProperty = lines.Any(text => text.Contains("android.enableJetifier"));
StringBuilder builder = new StringBuilder();
foreach(string each in lines) {
builder.AppendLine(each);
}
if (!hasAndroidXProperty) {
builder.AppendLine("android.useAndroidX=true");
}
if (!hasJetifierProperty && UniWebViewEditorSettings.GetOrCreateSettings().enableJetifier) {
builder.AppendLine("android.enableJetifier=true");
}
File.WriteAllText(filePath, builder.ToString());
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62bf94600ac0f42c09fc261d7ab19e12
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
{
"name": "UniWebView-CSharp.Editor",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b6d0e5b49a073436cbcd56804553ee20
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,114 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System;
using System.IO;
class UniWebViewEditorSettings: ScriptableObject
{
const string assetPath = "Assets/Editor/UniWebView/settings.asset";
[SerializeField]
internal bool usesCleartextTraffic = false;
[SerializeField]
internal bool writeExternalStorage = false;
[SerializeField]
internal bool accessFineLocation = false;
[SerializeField]
internal bool addsKotlin = true;
[SerializeField]
internal bool addsAndroidBrowser = true;
[SerializeField]
internal bool enableJetifier = true;
internal static UniWebViewEditorSettings GetOrCreateSettings() {
var settings = AssetDatabase.LoadAssetAtPath<UniWebViewEditorSettings>(assetPath);
if (settings == null) {
settings = ScriptableObject.CreateInstance<UniWebViewEditorSettings>();
Directory.CreateDirectory("Assets/Editor/UniWebView/");
AssetDatabase.CreateAsset(settings, assetPath);
AssetDatabase.SaveAssets();
}
return settings;
}
internal static SerializedObject GetSerializedSettings() {
return new SerializedObject(GetOrCreateSettings());
}
}
static class UniWebViewSettingsProvider {
static SerializedObject settings;
#if UNITY_2018_3_OR_NEWER
private class Provider : SettingsProvider {
public Provider(string path, SettingsScope scope = SettingsScope.User): base(path, scope) {}
public override void OnGUI(string searchContext) {
DrawPref();
}
}
[SettingsProvider]
static SettingsProvider UniWebViewPref() {
return new Provider("Preferences/UniWebView");
}
#else
[PreferenceItem("UniWebView")]
#endif
static void DrawPref() {
EditorGUIUtility.labelWidth = 320;
if (settings == null) {
settings = UniWebViewEditorSettings.GetSerializedSettings();
}
settings.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField("Android Manifest", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(settings.FindProperty("usesCleartextTraffic"));
DrawDetailLabel("If you need to load plain HTTP content.");
EditorGUILayout.PropertyField(settings.FindProperty("writeExternalStorage"));
DrawDetailLabel("If you need to download an image from web page.");
EditorGUILayout.PropertyField(settings.FindProperty("accessFineLocation"));
DrawDetailLabel("If you need to enable location support in web view.");
EditorGUI.indentLevel--;
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField("Gradle Build", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(settings.FindProperty("addsKotlin"));
DrawDetailLabel("Turn off this if another library is already adding Kotlin runtime.");
EditorGUILayout.PropertyField(settings.FindProperty("addsAndroidBrowser"));
DrawDetailLabel("Turn off this if another library is already adding 'androidx.browser:browser'.");
EditorGUILayout.PropertyField(settings.FindProperty("enableJetifier"));
DrawDetailLabel("Turn off this if you do not need Jetifier (for converting other legacy support dependencies to Android X).");
EditorGUILayout.EndVertical();
if (EditorGUI.EndChangeCheck()) {
settings.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
}
EditorGUIUtility.labelWidth = 0;
}
static void DrawDetailLabel(string text) {
EditorGUI.indentLevel++;
EditorGUILayout.LabelField(text, EditorStyles.miniLabel);
EditorGUI.indentLevel--;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a403a09e241a0480a957591ea60fb785
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,116 @@
using UnityEditor;
using UnityEditor.Android;
using UnityEngine;
using System.IO;
using System.Text;
class UniWebViewPostBuildProcessor : IPostGenerateGradleAndroidProject
{
public int callbackOrder { get { return 1; } }
public void OnPostGenerateGradleAndroidProject(string path) {
Debug.Log("<UniWebView> UniWebView Post Build Scirpt is patching manifest file and gradle file...");
PatchAndroidManifest(path);
PatchBuildGradle(path);
PatchGradleProperty(path);
}
private void PatchAndroidManifest(string root) {
var manifestFilePath = GetManifestFilePath(root);
var manifest = new UniWebViewAndroidManifest(manifestFilePath);
var changed = false;
Debug.Log("<UniWebView> Set hardware accelerated to enable smooth web view experience and HTML5 support like video and canvas.");
changed = manifest.SetHardwareAccelerated() || changed;
var settings = UniWebViewEditorSettings.GetOrCreateSettings();
if (settings.usesCleartextTraffic) {
changed = manifest.SetUsesCleartextTraffic() || changed;
}
if (settings.writeExternalStorage) {
changed = manifest.AddWriteExternalStoragePermission() || changed;
}
if (settings.accessFineLocation) {
changed = manifest.AddAccessFineLocationPermission() || changed;
}
if (changed) {
manifest.Save();
}
}
private void PatchBuildGradle(string root) {
var gradleFilePath = GetGradleFilePath(root);
var config = new UniWebViewGradleConfig(gradleFilePath);
var kotlinPrefix = "implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:";
var kotlinVersion = "1.4.31'";
var browserPrefix = "implementation 'androidx.browser:browser:";
var browserVersion = "1.2.0'";
var settings = UniWebViewEditorSettings.GetOrCreateSettings();
var dependenciesNode = config.ROOT.FindChildNodeByName("dependencies");
if (dependenciesNode != null) {
// Add kotlin
if (settings.addsKotlin) {
dependenciesNode.ReplaceContenOrAddStartsWith(kotlinPrefix, kotlinPrefix + kotlinVersion);
Debug.Log("<UniWebView> Updated Kotlin dependency in build.gradle.");
}
// Add browser package
if (settings.addsAndroidBrowser) {
dependenciesNode.ReplaceContenOrAddStartsWith(browserPrefix, browserPrefix + browserVersion);
Debug.Log("<UniWebView> Updated Browser dependency in build.gradle.");
}
} else {
Debug.LogError("UniWebViewPostBuildProcessor didn't find the `dependencies` field in build.gradle.");
Debug.LogError("Although we can continue to add a `dependencies`, make sure you have setup Gradle and the template correctly.");
var newNode = new UniWebViewGradleNode("dependencies", config.ROOT);
if (settings.addsKotlin) {
newNode.AppendContentNode(kotlinPrefix + kotlinVersion);
}
if (settings.addsAndroidBrowser) {
newNode.AppendContentNode(browserPrefix + browserVersion);
}
newNode.AppendContentNode("implementation(name: 'UniWebView', ext:'aar')");
config.ROOT.AppendChildNode(newNode);
}
config.Save();
}
private void PatchGradleProperty(string root) {
var gradlePropertyFilePath = GetGradlePropertyFilePath(root);
UniWebViewGradlePropertyPatcher.Patch(gradlePropertyFilePath);
}
private string CombinePaths(string[] paths) {
var path = "";
foreach (var item in paths) {
path = Path.Combine(path, item);
}
return path;
}
private string GetManifestFilePath(string root) {
string[] comps = {root, "src", "main", "AndroidManifest.xml"};
return CombinePaths(comps);
}
private string GetGradleFilePath(string root) {
string[] comps = {root, "build.gradle"};
return CombinePaths(comps);
}
private string GetGradlePropertyFilePath(string root) {
#if UNITY_2019_3_OR_NEWER
string[] compos = {root, "..", "gradle.properties"};
#else
string[] compos = {root, "gradle.properties"};
#endif
return CombinePaths(compos);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 59d4a8d85c95843719d8b9df823c3da3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: