Some progress on hacking the webview
This commit is contained in:
parent
a1caff328d
commit
7fb71c27a7
|
@ -1 +1,5 @@
|
||||||
|
.gradle/*
|
||||||
|
.idea/*
|
||||||
|
local.properties
|
||||||
app/build/*
|
app/build/*
|
||||||
|
build/*
|
||||||
|
|
|
@ -8,22 +8,16 @@ BleedingEdge:
|
||||||
interruptible: true
|
interruptible: true
|
||||||
stage: build
|
stage: build
|
||||||
script: |
|
script: |
|
||||||
mkdir -p ./AndroidSdk
|
StartDir="$(pwd)"
|
||||||
cd ./AndroidSdk
|
mkdir -p ../AndroidSdk
|
||||||
|
cd ../AndroidSdk
|
||||||
wget -O BuildTools.zip https://dl.google.com/android/repository/build-tools_r30.0.3-linux.zip
|
wget -O BuildTools.zip https://dl.google.com/android/repository/build-tools_r30.0.3-linux.zip
|
||||||
wget -O Platform.zip https://dl.google.com/android/repository/platform-30_r03.zip
|
wget -O Platform.zip https://dl.google.com/android/repository/platform-30_r03.zip
|
||||||
yes A | unzip BuildTools.zip || true
|
yes A | unzip BuildTools.zip || true
|
||||||
yes A | unzip Platform.zip || true
|
yes A | unzip Platform.zip || true
|
||||||
cd ..
|
cd "$StartDir"
|
||||||
|
|
||||||
mv ./app/src/main ./main
|
sh ./tools/Build.sh
|
||||||
cd ./main
|
|
||||||
mv ./java ./src
|
|
||||||
mv ../tools/tiny-android-template/* ./
|
|
||||||
echo "${SecEncodedKeystore}" | base64 --decode > ./Keystore.jks
|
|
||||||
|
|
||||||
perl ./link.pl
|
|
||||||
bash ./make.sh
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- main/app.apk
|
- build/app.apk
|
||||||
|
|
|
@ -9,7 +9,7 @@ android {
|
||||||
minSdkVersion 1
|
minSdkVersion 1
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0.0"
|
versionName "1.0.1"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
@ -20,5 +20,5 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
//compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
<manifest
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.eu.octt.browserocto"
|
package="org.eu.octt.browserocto"
|
||||||
android:versionCode="0"
|
android:versionCode="1"
|
||||||
android:versionName="0">
|
android:versionName="1.0.1">
|
||||||
<uses-sdk android:minSdkVersion="1" android:targetSdkVersion="30"/>
|
<uses-sdk android:minSdkVersion="1" android:targetSdkVersion="30"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
|
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
|
||||||
|
|
|
@ -48,10 +48,9 @@ public class MainActivity extends Activity {
|
||||||
};
|
};
|
||||||
|
|
||||||
private Intent MakeIntBrowse(String Url, Boolean Cache) {
|
private Intent MakeIntBrowse(String Url, Boolean Cache) {
|
||||||
Intent IntBrowse = new Intent(getApplicationContext(), WebWindowActivity.class);
|
Intent IntBrowse = new Intent(getApplicationContext(), WebWindowActivity.class)
|
||||||
// To get unlimited activities in general but only 1 per site, set only FLAG_ACTIVITY_NEW_DOCUMENT and put site URL in intent.setData
|
// To get unlimited activities in general but only 1 per site, set only FLAG_ACTIVITY_NEW_DOCUMENT and put site URL in intent.setData
|
||||||
// NOTE: This only gives a good UX on Android >= 7, for the lower versions we need to implement things differently
|
// NOTE: This only gives a good UX on Android >= 7, for the lower versions we need to implement things differently
|
||||||
IntBrowse
|
|
||||||
.setAction(Intent.ACTION_OPEN_DOCUMENT)
|
.setAction(Intent.ACTION_OPEN_DOCUMENT)
|
||||||
.setData(Uri.parse(Url))
|
.setData(Uri.parse(Url))
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT /*| Intent.FLAG_ACTIVITY_MULTIPLE_TASK*/)
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT /*| Intent.FLAG_ACTIVITY_MULTIPLE_TASK*/)
|
||||||
|
@ -84,50 +83,61 @@ public class MainActivity extends Activity {
|
||||||
};
|
};
|
||||||
|
|
||||||
private final void CreateShortcutFallback(String Url, String Name) {
|
private final void CreateShortcutFallback(String Url, String Name) {
|
||||||
Intent IntExt = new Intent();
|
Intent IntExt = new Intent()
|
||||||
IntExt.putExtra(Intent.EXTRA_SHORTCUT_INTENT, MakeIntBrowse(Url, true));
|
.putExtra(Intent.EXTRA_SHORTCUT_INTENT, MakeIntBrowse(Url, true))
|
||||||
IntExt.putExtra(Intent.EXTRA_SHORTCUT_NAME, Name);
|
.putExtra(Intent.EXTRA_SHORTCUT_NAME, Name)
|
||||||
IntExt.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(getApplicationContext(), R.drawable.ic_launcher));
|
.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(getApplicationContext(), R.drawable.ic_launcher))
|
||||||
|
.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
|
||||||
IntExt.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
|
//.putExtra("duplicate", false); //may it's already there so don't duplicate
|
||||||
//IntExt.putExtra("duplicate", false); //may it's already there so don't duplicate
|
|
||||||
getApplicationContext().sendBroadcast(IntExt);
|
getApplicationContext().sendBroadcast(IntExt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
private void ShowShortcutDial(String Url) {
|
private void ShowShortcutDial(String Url) {
|
||||||
final AlertDialog.Builder Dial = new AlertDialog.Builder(this);
|
final AlertDialog.Builder Dial = new AlertDialog.Builder(this);
|
||||||
final String Url_ = Url;
|
final String Url_ = Url;
|
||||||
|
|
||||||
final View v = getLayoutInflater().inflate(R.layout.dialmakeshortcut, null);
|
final View DialView = getLayoutInflater().inflate(R.layout.dialmakeshortcut, null);
|
||||||
Dial.setView(v);
|
Dial.setView(DialView);
|
||||||
|
|
||||||
final EditText EditName = v.findViewById(R.id.EditName);
|
final EditText EditName = DialView.findViewById(R.id.EditName);
|
||||||
//final EditText EditIcon = v.findViewById(R.id.EditIcon);
|
//final EditText EditIcon = DialView.findViewById(R.id.EditIcon);
|
||||||
|
|
||||||
Dial.setTitle("Create Home Shortcut");
|
Dial.setTitle("Create Home Shortcut");
|
||||||
//Dial.setCancelable(false);
|
Dial.setCancelable(false);
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/15619098
|
||||||
|
//Dial.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
|
||||||
|
// @Override
|
||||||
|
// public void onClick(DialogInterface Dial, int w) {
|
||||||
|
// // Do nothing here because we override this button later to change the behavior.
|
||||||
|
// // However, we still need this because of quirks on older versions of Android.
|
||||||
|
// };
|
||||||
|
//});
|
||||||
|
|
||||||
|
Dial.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface Dial, int w) {
|
||||||
|
Dial.dismiss();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//Dial.show();
|
||||||
|
|
||||||
|
// TODO: FIX it seems we just broke this with our crap idea of hacking the dialog cancellation
|
||||||
Dial.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
|
Dial.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface d, int w) {
|
public void onClick(DialogInterface Dial, int w) {
|
||||||
final String Name = EditName.getText().toString();
|
final String Name = EditName.getText().toString();
|
||||||
//String IconUrl = EditIcon.getText().toString();
|
//String IconUrl = EditIcon.getText().toString();
|
||||||
if (!Name.equals("")) {
|
if (!Name.equals("")) {
|
||||||
CreateShortcut(Url_, Name);
|
CreateShortcut(Url_, Name);
|
||||||
|
Dial.dismiss();
|
||||||
} else
|
} else
|
||||||
if (Name.equals("")) {
|
if (Name.equals("")) {
|
||||||
_Util.ToastMsg("Name can't be empty!", getApplicationContext());
|
_Util.ToastMsg("Name can't be empty!", getApplicationContext());
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
Dial.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface d, int w) {
|
|
||||||
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
Dial.show();
|
Dial.show();
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,26 +5,98 @@ import android.content.*;
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
import android.webkit.*;
|
import android.webkit.*;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
|
import android.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
|
||||||
public class WebWindowActivity extends Activity {
|
public class WebWindowActivity extends Activity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.webwindow);
|
setContentView(R.layout.webwindow);
|
||||||
|
|
||||||
Bundle Extra = getIntent().getExtras();
|
Bundle Extra = getIntent().getExtras();
|
||||||
LinearLayout LayMain = findViewById(R.id.LayMain);
|
LinearLayout LayMain = findViewById(R.id.LayMain);
|
||||||
|
|
||||||
WebView Web0 = findViewById(R.id.Web0);
|
WebView Web0 = findViewById(R.id.Web0);
|
||||||
Web0.setWebViewClient(new WebViewClient());
|
//Web0.setWebViewClient(new WebViewClient());
|
||||||
|
|
||||||
|
Web0.setWebViewClient(new WebViewClient() {
|
||||||
|
// https://gist.github.com/kmerrell42/b4ff31733c562a3262ee9a42f5704a89
|
||||||
|
@Override
|
||||||
|
public WebResourceResponse shouldInterceptRequest(WebView v, String Url) {
|
||||||
|
return HandleRequest(Url);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public WebResourceResponse shouldInterceptRequest(WebView v, WebResourceRequest Req) {
|
||||||
|
return HandleRequest(Req.getUrl().toString());
|
||||||
|
}
|
||||||
|
private WebResourceResponse HandleRequest(String Url) {
|
||||||
|
//Thread thread = new Thread(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
try {
|
||||||
|
//URL url;
|
||||||
|
//HttpURLConnection Connection = null;
|
||||||
|
URL Url_ = new URL(Url);
|
||||||
|
HttpURLConnection Connection = (HttpURLConnection)Url_.openConnection();
|
||||||
|
String ContentType = Connection.getContentType();
|
||||||
|
String ContentEncoding = null;
|
||||||
|
if (ContentType != null) {
|
||||||
|
ContentType = ContentType.split("\\;")[0];
|
||||||
|
ContentEncoding = ContentType.split("\\=")[1];
|
||||||
|
}
|
||||||
|
InputStream In = new BufferedInputStream(Connection.getInputStream());
|
||||||
|
return new WebResourceResponse(ContentType, ContentEncoding, In);
|
||||||
|
} catch (Exception Ex) {
|
||||||
|
Log.e("browserocto/Log", "", Ex);
|
||||||
|
};
|
||||||
|
// };//});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
Web0.getSettings().setJavaScriptEnabled(true);
|
Web0.getSettings().setJavaScriptEnabled(true);
|
||||||
Web0.getSettings().setDomStorageEnabled(true);
|
Web0.getSettings().setDomStorageEnabled(true);
|
||||||
Web0.getSettings().setCacheMode(WhichCacheMode(Extra.getBoolean("Cache")));
|
Web0.getSettings().setCacheMode(WhichCacheMode(Extra.getBoolean("Cache")));
|
||||||
|
|
||||||
|
// This is apparently not working like I want (force caching of all site resources in spite of bad Cache-Control HTTP headers)
|
||||||
|
//webView.getSettings().setAppCacheMaxSize(1024*1024*8);
|
||||||
|
//webView.getSettings().setAppCachePath("/data/data/" + getPackageName() + "/cache");
|
||||||
|
//webView.getSettings().setAllowFileAccess(true);
|
||||||
|
//webView.getSettings().setAppCacheEnabled(true);
|
||||||
|
|
||||||
Web0.loadUrl(Extra.getString("Url"));
|
Web0.loadUrl(Extra.getString("Url"));
|
||||||
|
_Util.ToastMsg(Extra.getString("Url"), this);
|
||||||
_Util.ToastMsg(Extra.getString("Url"), getApplicationContext());
|
|
||||||
|
// Thread thread = new Thread(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// URL url;
|
||||||
|
// HttpURLConnection urlConnection = null;
|
||||||
|
// try {
|
||||||
|
// url = new URL("http://www.android.com/");
|
||||||
|
// urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
|
// InputStream in = new BufferedInputStream(urlConnection.getInputStream());
|
||||||
|
// runOnUiThread(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// try {
|
||||||
|
// _Util.ToastMsg(_Util.StreamToString(in), getApplicationContext());
|
||||||
|
// } catch (Exception Ex) {
|
||||||
|
// Log.e("browserocto/Log", "", Ex);
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// } catch (Exception Ex) {
|
||||||
|
// Log.e("browserocto/Log", "", Ex);
|
||||||
|
// } finally {
|
||||||
|
// urlConnection.disconnect();
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// thread.start();
|
||||||
};
|
};
|
||||||
|
|
||||||
public int WhichCacheMode(boolean Opt) {
|
public int WhichCacheMode(boolean Opt) {
|
||||||
if (Opt) {
|
if (Opt) {
|
||||||
return WebSettings.LOAD_CACHE_ELSE_NETWORK;
|
return WebSettings.LOAD_CACHE_ELSE_NETWORK;
|
||||||
|
|
|
@ -3,20 +3,56 @@ package org.eu.octt.browserocto;
|
||||||
import android.app.*;
|
import android.app.*;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
public class _Util extends Activity {
|
public class _Util extends Activity {
|
||||||
|
public static boolean StartsWithOneOf(String Check, String[] With) {
|
||||||
|
for (int i=0; i<With.length; i++) {
|
||||||
|
if (Check.startsWith(With[i])) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
public static boolean EqualsOneOf(String Check, String[] With) {
|
||||||
|
for (int i=0; i<With.length; i++) {
|
||||||
|
if (Check.equals(With[i])) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
public static void ToastMsg(String Msg, Context c) {
|
public static void ToastMsg(String Msg, Context c) {
|
||||||
Toast.makeText(c, Msg, Toast.LENGTH_SHORT).show();
|
Toast.makeText(c, Msg, Toast.LENGTH_SHORT).show();
|
||||||
};
|
};
|
||||||
|
|
||||||
public static boolean IsUrlValid(String Url) {
|
public static boolean IsUrlValid(String Url) {
|
||||||
Url = Url.toLowerCase();
|
Url = Url.trim().toLowerCase();
|
||||||
if (
|
String[] Prefixes = {"file://", "http://", "https://", "data:", "javascript:"};
|
||||||
(Url.startsWith("file://") || Url.startsWith("http://") || Url.startsWith("https://"))
|
if (StartsWithOneOf(Url, Prefixes) && !EqualsOneOf(Url, Prefixes)) {
|
||||||
&& !(Url.equals("file://") || Url.equals("http://") || Url.equals("https://"))
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/5713929
|
||||||
|
public static String StreamToString(InputStream is) throws IOException {
|
||||||
|
if (is != null) {
|
||||||
|
Writer writer = new StringWriter();
|
||||||
|
char[] buffer = new char[1024];
|
||||||
|
try {
|
||||||
|
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
||||||
|
int n;
|
||||||
|
while ((n = reader.read(buffer)) != -1) {
|
||||||
|
writer.write(buffer, 0, n);
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
|
};
|
||||||
|
return writer.toString();
|
||||||
|
};
|
||||||
|
return "";
|
||||||
|
};
|
||||||
};
|
};
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- TODO: Make this be CheckBox on Android < ICS because Switch doesn't exist -->
|
||||||
<Switch
|
<Switch
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
rm -rf ./build || true
|
||||||
|
cp -r ./app/src/main ./build
|
||||||
|
cd ./build
|
||||||
|
mv ./java ./src
|
||||||
|
cp -r ../tools/tiny-android-template/* ./
|
||||||
|
echo "${SecEncodedKeystore}" | base64 --decode > ./Keystore.jks
|
||||||
|
|
||||||
|
perl ./link.pl
|
||||||
|
bash ./make.sh
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export SecEncodedKeystore="$(cat ../Test.jks | base64 -w0)"
|
||||||
|
export SecKeystorePassword="123456"
|
||||||
|
|
||||||
|
sh ./tools/Build.sh
|
|
@ -7,7 +7,7 @@ API_LEVEL_MIN="1"
|
||||||
ANDROID_VERSION="11"
|
ANDROID_VERSION="11"
|
||||||
NDK_VERSION="r23-beta4"
|
NDK_VERSION="r23-beta4"
|
||||||
TARGET_ARCHES=( "arm64-v8a" )
|
TARGET_ARCHES=( "arm64-v8a" )
|
||||||
SDK_DIR="../AndroidSdk"
|
SDK_DIR="../../AndroidSdk"
|
||||||
KOTLIN_LIB_DIR="/usr/share/kotlin/lib"
|
KOTLIN_LIB_DIR="/usr/share/kotlin/lib"
|
||||||
|
|
||||||
REPO="https://dl.google.com/dl/android/maven2"
|
REPO="https://dl.google.com/dl/android/maven2"
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
source includes.sh
|
||||||
|
|
||||||
|
MF=`cat AndroidManifest.xml`
|
||||||
|
TERM="package=[\'\"]([a-z0-9.]+)"
|
||||||
|
|
||||||
|
if [[ "$MF" =~ $TERM ]]
|
||||||
|
then
|
||||||
|
package="${BASH_REMATCH[1]}"
|
||||||
|
activity=".MainActivity"
|
||||||
|
|
||||||
|
TERM="<activity([^>]+)"
|
||||||
|
if [[ "$MF" =~ $TERM ]]; then
|
||||||
|
tag="${BASH_REMATCH[1]}"
|
||||||
|
TERM="[\'\"]([^\'\"]+)"
|
||||||
|
[[ "$tag" =~ $TERM ]] && activity="${BASH_REMATCH[1]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$CMD_ADB install -r -t app.apk && \
|
||||||
|
$CMD_ADB shell am start -n $package/$activity
|
||||||
|
else
|
||||||
|
echo Could not find a suitable package name inside AndroidManifest.xml
|
||||||
|
exit
|
||||||
|
fi
|
Loading…
Reference in New Issue