i give up im just pushing everything
This commit is contained in:
parent
0e48db5921
commit
f182a98b69
20 changed files with 519 additions and 407 deletions
|
@ -3,11 +3,11 @@ plugins {
|
|||
}
|
||||
|
||||
android {
|
||||
namespace 'dev.vencord.vendroid'
|
||||
namespace 'com.nin0dev.vendroid'
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId "dev.vencord.vendroid"
|
||||
applicationId "com.nin0dev.vendroid"
|
||||
minSdk 21
|
||||
targetSdk 34
|
||||
versionCode 3
|
||||
|
@ -16,7 +16,7 @@ android {
|
|||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix ".dbg"
|
||||
|
||||
}
|
||||
|
||||
release {
|
||||
|
@ -32,5 +32,6 @@ android {
|
|||
dependencies {
|
||||
implementation 'androidx.annotation:annotation:1.7.1'
|
||||
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.28'
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
}
|
||||
|
||||
|
|
|
@ -5,28 +5,34 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/LoadingTheme"
|
||||
tools:targetApi="34"
|
||||
>
|
||||
tools:targetApi="34">
|
||||
<activity
|
||||
android:name=".WelcomeActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Material3.DayNight.NoActionBar" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/LoadingTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="http" />
|
||||
<data android:host="discord.com" />
|
||||
|
|
5
app/src/main/java/com/nin0dev/vendroid/Constants.kt
Normal file
5
app/src/main/java/com/nin0dev/vendroid/Constants.kt
Normal file
|
@ -0,0 +1,5 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
object Constants {
|
||||
const val JS_BUNDLE_URL = "https://github.com/VendroidEnhanced/Vencord/releases/latest/download/mobile.js"
|
||||
}
|
71
app/src/main/java/com/nin0dev/vendroid/HttpClient.kt
Normal file
71
app/src/main/java/com/nin0dev/vendroid/HttpClient.kt
Normal file
|
@ -0,0 +1,71 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
import android.app.Activity
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.Locale
|
||||
|
||||
object HttpClient {
|
||||
@JvmField
|
||||
var VencordRuntime: String? = null
|
||||
@JvmField
|
||||
var VencordMobileRuntime: String? = null
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun fetchVencord(activity: Activity) {
|
||||
if (VencordRuntime != null) return
|
||||
val res = activity.resources
|
||||
res.openRawResource(R.raw.vencord_mobile).use { `is` -> VencordMobileRuntime = readAsText(`is`) }
|
||||
val conn = fetch(Constants.JS_BUNDLE_URL)
|
||||
conn.inputStream.use { `is` -> VencordRuntime = readAsText(`is`) }
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun fetch(url: String): HttpURLConnection {
|
||||
val conn = URL(url).openConnection() as HttpURLConnection
|
||||
if (conn.getResponseCode() >= 300) {
|
||||
throw HttpException(conn)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readAsText(`is`: InputStream): String {
|
||||
ByteArrayOutputStream().use { baos ->
|
||||
var n: Int
|
||||
val buf = ByteArray(16384) // 16 KB
|
||||
while (`is`.read(buf).also { n = it } > -1) {
|
||||
baos.write(buf, 0, n)
|
||||
}
|
||||
baos.flush()
|
||||
return baos.toString("UTF-8")
|
||||
}
|
||||
}
|
||||
|
||||
class HttpException(private val conn: HttpURLConnection) : IOException() {
|
||||
override var message: String? = null
|
||||
get() {
|
||||
if (field == null) {
|
||||
try {
|
||||
conn.errorStream.use { es ->
|
||||
field = String.format(
|
||||
Locale.ENGLISH,
|
||||
"%d: %s (%s)\n%s",
|
||||
conn.getResponseCode(),
|
||||
conn.getResponseMessage(),
|
||||
conn.url.toString(),
|
||||
readAsText(es)
|
||||
)
|
||||
}
|
||||
} catch (ex: IOException) {
|
||||
field = "Error while building message lmao. Url is " + conn.url.toString()
|
||||
}
|
||||
}
|
||||
return field
|
||||
}
|
||||
private set
|
||||
}
|
||||
}
|
31
app/src/main/java/com/nin0dev/vendroid/Logger.kt
Normal file
31
app/src/main/java/com/nin0dev/vendroid/Logger.kt
Normal file
|
@ -0,0 +1,31 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
import android.util.Log
|
||||
|
||||
object Logger {
|
||||
private const val TAG = "Vencord"
|
||||
@JvmStatic
|
||||
fun e(message: String?) {
|
||||
Log.e(TAG, message!!)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun e(message: String?, e: Throwable?) {
|
||||
Log.e(TAG, message, e)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun w(message: String?) {
|
||||
Log.w(TAG, message!!)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun i(message: String?) {
|
||||
Log.i(TAG, message!!)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun d(message: String?) {
|
||||
Log.d(TAG, message!!)
|
||||
}
|
||||
}
|
115
app/src/main/java/com/nin0dev/vendroid/MainActivity.kt
Normal file
115
app/src/main/java/com/nin0dev/vendroid/MainActivity.kt
Normal file
|
@ -0,0 +1,115 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.StrictMode
|
||||
import android.os.StrictMode.ThreadPolicy
|
||||
import android.view.KeyEvent
|
||||
import android.webkit.ValueCallback
|
||||
import android.webkit.WebView
|
||||
import com.nin0dev.vendroid.HttpClient.fetchVencord
|
||||
import com.nin0dev.vendroid.Logger.e
|
||||
import java.io.IOException
|
||||
|
||||
class MainActivity : Activity() {
|
||||
private var wvInitialized = false
|
||||
private var wv: WebView? = null
|
||||
@JvmField
|
||||
var filePathCallback: ValueCallback<Array<Uri?>?>? = null
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled") // mad? watch this swag
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
startActivity(Intent(this@MainActivity, WelcomeActivity::class.java))
|
||||
// https://developer.chrome.com/docs/devtools/remote-debugging/webviews/
|
||||
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
|
||||
setContentView(R.layout.activity_main)
|
||||
wv = findViewById(R.id.webview)!!
|
||||
explodeAndroid()
|
||||
wv!!.setWebViewClient(VWebviewClient())
|
||||
wv!!.setWebChromeClient(VChromeClient(this))
|
||||
val s = wv?.getSettings()!!
|
||||
s.javaScriptEnabled = true
|
||||
s.domStorageEnabled = true
|
||||
s.allowFileAccess = true
|
||||
wv?.addJavascriptInterface(VencordNative(this, wv!!), "VencordMobileNative")
|
||||
try {
|
||||
fetchVencord(this)
|
||||
} catch (ex: IOException) {
|
||||
|
||||
}
|
||||
val intent = intent
|
||||
if (intent.action == Intent.ACTION_VIEW) {
|
||||
val data = intent.data
|
||||
if (data != null) handleUrl(intent.data)
|
||||
} else {
|
||||
wv!!.loadUrl("https://discord.com/app")
|
||||
}
|
||||
wvInitialized = true
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && wv != null) {
|
||||
runOnUiThread { wv!!.evaluateJavascript("VencordMobile.onBackPress()") { r: String -> if ("false" == r) onBackPressed() } }
|
||||
return true
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent) {
|
||||
if (requestCode != FILECHOOSER_RESULTCODE || filePathCallback == null) return
|
||||
if (resultCode != RESULT_OK || intent == null) {
|
||||
filePathCallback!!.onReceiveValue(null)
|
||||
} else {
|
||||
var uris: Array<Uri?>?
|
||||
try {
|
||||
val clipData = intent.clipData
|
||||
if (clipData != null) { // multiple items
|
||||
uris = arrayOfNulls(clipData.itemCount)
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
uris[i] = clipData.getItemAt(i).uri
|
||||
}
|
||||
} else { // single item
|
||||
uris = arrayOf(intent.data)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
e("Error during file upload", ex)
|
||||
uris = null
|
||||
}
|
||||
filePathCallback!!.onReceiveValue(uris)
|
||||
}
|
||||
filePathCallback = null
|
||||
}
|
||||
|
||||
private fun explodeAndroid() {
|
||||
StrictMode.setThreadPolicy(
|
||||
ThreadPolicy.Builder() // trolley
|
||||
.permitNetwork()
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
fun handleUrl(url: Uri?) {
|
||||
if (url != null) {
|
||||
if (url.authority != "discord.com") return
|
||||
if (!wvInitialized) {
|
||||
wv!!.loadUrl(url.toString())
|
||||
} else {
|
||||
wv!!.evaluateJavascript("Vencord.Webpack.Common.NavigationRouter.transitionTo(\"" + url.path + "\")", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
val data = intent.data
|
||||
data?.let { handleUrl(it) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FILECHOOSER_RESULTCODE = 8485
|
||||
}
|
||||
}
|
40
app/src/main/java/com/nin0dev/vendroid/VChromeClient.kt
Normal file
40
app/src/main/java/com/nin0dev/vendroid/VChromeClient.kt
Normal file
|
@ -0,0 +1,40 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.net.Uri
|
||||
import android.webkit.ConsoleMessage
|
||||
import android.webkit.ConsoleMessage.MessageLevel
|
||||
import android.webkit.ValueCallback
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebView
|
||||
import com.nin0dev.vendroid.Logger.d
|
||||
import com.nin0dev.vendroid.Logger.e
|
||||
import com.nin0dev.vendroid.Logger.i
|
||||
import com.nin0dev.vendroid.Logger.w
|
||||
import java.util.Locale
|
||||
|
||||
class VChromeClient(private val activity: MainActivity) : WebChromeClient() {
|
||||
override fun onConsoleMessage(msg: ConsoleMessage): Boolean {
|
||||
val m = String.format(Locale.ENGLISH, "[Javascript] %s @ %d: %s", msg.message(), msg.lineNumber(), msg.sourceId())
|
||||
when (msg.messageLevel()) {
|
||||
MessageLevel.DEBUG -> d(m)
|
||||
MessageLevel.ERROR -> e(m)
|
||||
MessageLevel.WARNING -> w(m)
|
||||
else -> i(m)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: FileChooserParams): Boolean {
|
||||
if (activity.filePathCallback != null) activity.filePathCallback?.onReceiveValue(null)
|
||||
activity.filePathCallback = filePathCallback as ValueCallback<Array<Uri?>?>
|
||||
val i = fileChooserParams.createIntent()
|
||||
try {
|
||||
activity.startActivityForResult(i, MainActivity.FILECHOOSER_RESULTCODE)
|
||||
} catch (ex: ActivityNotFoundException) {
|
||||
activity.filePathCallback = null
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
68
app/src/main/java/com/nin0dev/vendroid/VWebviewClient.kt
Normal file
68
app/src/main/java/com/nin0dev/vendroid/VWebviewClient.kt
Normal file
|
@ -0,0 +1,68 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.view.View
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import com.nin0dev.vendroid.Logger.e
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
class VWebviewClient : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
val url = request.url
|
||||
if ("discord.com" == url.authority || "about:blank" == url.toString()) {
|
||||
return false
|
||||
}
|
||||
val intent = Intent(Intent.ACTION_VIEW, url)
|
||||
view.context.startActivity(intent)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||
view.evaluateJavascript(HttpClient.VencordRuntime!!, null)
|
||||
view.evaluateJavascript(HttpClient.VencordMobileRuntime!!, null)
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
view.visibility = View.VISIBLE
|
||||
super.onPageFinished(view, url)
|
||||
}
|
||||
|
||||
override fun shouldInterceptRequest(view: WebView, req: WebResourceRequest): WebResourceResponse? {
|
||||
val uri = req.url
|
||||
if (req.isForMainFrame || req.url.path!!.endsWith(".css")) {
|
||||
try {
|
||||
return doFetch(req)
|
||||
} catch (ex: IOException) {
|
||||
e("Error during shouldInterceptRequest", ex)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun doFetch(req: WebResourceRequest): WebResourceResponse {
|
||||
val url = req.url.toString()
|
||||
val conn = URL(url).openConnection() as HttpURLConnection
|
||||
conn.setRequestMethod(req.method)
|
||||
for ((key, value) in req.requestHeaders) {
|
||||
conn.setRequestProperty(key, value)
|
||||
}
|
||||
val code = conn.getResponseCode()
|
||||
val msg = conn.getResponseMessage()
|
||||
val headers = conn.headerFields
|
||||
val modifiedHeaders = HashMap<String, String>(headers.size)
|
||||
for ((key, value) in headers) {
|
||||
if (!"Content-Security-Policy".equals(key, ignoreCase = true)) {
|
||||
modifiedHeaders[key] = value[0]
|
||||
}
|
||||
}
|
||||
if (url.endsWith(".css")) modifiedHeaders["Content-Type"] = "text/css"
|
||||
return WebResourceResponse(modifiedHeaders.getOrDefault("Content-Type", "application/octet-stream"), "utf-8", code, msg, modifiedHeaders, conn.inputStream)
|
||||
}
|
||||
}
|
15
app/src/main/java/com/nin0dev/vendroid/VencordNative.kt
Normal file
15
app/src/main/java/com/nin0dev/vendroid/VencordNative.kt
Normal file
|
@ -0,0 +1,15 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
import android.app.Activity
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
|
||||
class VencordNative(private val activity: Activity, private val wv: WebView) {
|
||||
@JavascriptInterface
|
||||
fun goBack() {
|
||||
activity.runOnUiThread {
|
||||
if (wv.canGoBack()) wv.goBack() else // no idea what i was smoking when I wrote this
|
||||
activity.getActionBar()
|
||||
}
|
||||
}
|
||||
}
|
11
app/src/main/java/com/nin0dev/vendroid/WelcomeActivity.kt
Normal file
11
app/src/main/java/com/nin0dev/vendroid/WelcomeActivity.kt
Normal file
|
@ -0,0 +1,11 @@
|
|||
package com.nin0dev.vendroid
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class WelcomeActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_welcome)
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package dev.vencord.vendroid;
|
||||
|
||||
public class Constants {
|
||||
public static final String JS_BUNDLE_URL = "https://github.com/Vendicated/Vencord/releases/download/devbuild/browser.js";
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package dev.vencord.vendroid;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
public class HttpClient {
|
||||
public static final class HttpException extends IOException {
|
||||
private final HttpURLConnection conn;
|
||||
private String message;
|
||||
|
||||
public HttpException(HttpURLConnection conn) {
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getMessage() {
|
||||
if (message == null) {
|
||||
try(var es = conn.getErrorStream()) {
|
||||
message = String.format(
|
||||
Locale.ENGLISH,
|
||||
"%d: %s (%s)\n%s",
|
||||
conn.getResponseCode(),
|
||||
conn.getResponseMessage(),
|
||||
conn.getURL().toString(),
|
||||
readAsText(es)
|
||||
);
|
||||
} catch (IOException ex) {
|
||||
message = "Error while building message lmao. Url is " + conn.getURL().toString();
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
public static String VencordRuntime;
|
||||
public static String VencordMobileRuntime;
|
||||
|
||||
public static void fetchVencord(Activity activity) throws IOException {
|
||||
if (VencordRuntime != null) return;
|
||||
|
||||
var res = activity.getResources();
|
||||
try (var is = res.openRawResource(R.raw.vencord_mobile)) {
|
||||
VencordMobileRuntime = readAsText(is);
|
||||
}
|
||||
|
||||
var conn = fetch(Constants.JS_BUNDLE_URL);
|
||||
try (var is = conn.getInputStream()) {
|
||||
VencordRuntime = readAsText(is);
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpURLConnection fetch(String url) throws IOException {
|
||||
var conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
if (conn.getResponseCode() >= 300) {
|
||||
throw new HttpException(conn);
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
private static String readAsText(InputStream is) throws IOException {
|
||||
try (var baos = new ByteArrayOutputStream()) {
|
||||
int n;
|
||||
byte[] buf = new byte[16384]; // 16 KB
|
||||
while ((n = is.read(buf)) > -1) {
|
||||
baos.write(buf, 0, n);
|
||||
}
|
||||
baos.flush();
|
||||
|
||||
//noinspection CharsetObjectCanBeUsed thank you so much android studio but no i do not want to use an sdk33 api ._.
|
||||
return baos.toString("UTF-8");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package dev.vencord.vendroid;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public final class Logger {
|
||||
private static final String TAG = "Vencord";
|
||||
|
||||
public static void e(String message) {
|
||||
Log.e(TAG, message);
|
||||
}
|
||||
public static void e(String message, Throwable e) {
|
||||
Log.e(TAG, message, e);
|
||||
}
|
||||
|
||||
public static void w(String message) {
|
||||
Log.w(TAG, message);
|
||||
}
|
||||
|
||||
public static void i(String message) {
|
||||
Log.i(TAG, message);
|
||||
}
|
||||
|
||||
public static void d(String message) {
|
||||
Log.d(TAG, message);
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
package dev.vencord.vendroid;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.StrictMode;
|
||||
import android.view.KeyEvent;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
public static final int FILECHOOSER_RESULTCODE = 8485;
|
||||
private boolean wvInitialized = false;
|
||||
private WebView wv;
|
||||
|
||||
public ValueCallback<Uri[]> filePathCallback;
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled") // mad? watch this swag
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// https://developer.chrome.com/docs/devtools/remote-debugging/webviews/
|
||||
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
wv = findViewById(R.id.webview);
|
||||
|
||||
explodeAndroid();
|
||||
|
||||
wv.setWebViewClient(new VWebviewClient());
|
||||
wv.setWebChromeClient(new VChromeClient(this));
|
||||
|
||||
var s = wv.getSettings();
|
||||
s.setJavaScriptEnabled(true);
|
||||
s.setDomStorageEnabled(true);
|
||||
s.setAllowFileAccess(true);
|
||||
|
||||
wv.addJavascriptInterface(new VencordNative(this, wv), "VencordMobileNative");
|
||||
|
||||
try {
|
||||
HttpClient.fetchVencord(this);
|
||||
} catch (IOException ex) {
|
||||
Logger.e("Failed to fetch Vencord", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (Objects.equals(intent.getAction(), Intent.ACTION_VIEW)) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) handleUrl(intent.getData());
|
||||
} else {
|
||||
wv.loadUrl("https://discord.com/app");
|
||||
}
|
||||
|
||||
wvInitialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && wv != null) {
|
||||
runOnUiThread(() -> wv.evaluateJavascript("VencordMobile.onBackPress()", r -> {
|
||||
if ("false".equals(r))
|
||||
this.onBackPressed ();
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
if (requestCode != FILECHOOSER_RESULTCODE || filePathCallback == null)
|
||||
return;
|
||||
|
||||
if (resultCode != RESULT_OK || intent == null) {
|
||||
filePathCallback.onReceiveValue(null);
|
||||
} else {
|
||||
Uri[] uris;
|
||||
try {
|
||||
var clipData = intent.getClipData();
|
||||
if (clipData != null) { // multiple items
|
||||
uris = new Uri[clipData.getItemCount()];
|
||||
for (int i = 0; i < clipData.getItemCount(); i++) {
|
||||
uris[i] = clipData.getItemAt(i).getUri();
|
||||
}
|
||||
} else { // single item
|
||||
uris = new Uri[] { intent.getData() };
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.e("Error during file upload", ex);
|
||||
uris = null;
|
||||
}
|
||||
|
||||
filePathCallback.onReceiveValue(uris);
|
||||
}
|
||||
filePathCallback = null;
|
||||
}
|
||||
|
||||
private void explodeAndroid() {
|
||||
StrictMode.setThreadPolicy(
|
||||
new StrictMode.ThreadPolicy.Builder()
|
||||
// trolley
|
||||
.permitNetwork()
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public void handleUrl(Uri url) {
|
||||
if (url != null) {
|
||||
if (!url.getAuthority().equals("discord.com")) return;
|
||||
if (!wvInitialized) {
|
||||
wv.loadUrl(url.toString());
|
||||
} else {
|
||||
wv.evaluateJavascript("Vencord.Webpack.Common.NavigationRouter.transitionTo(\"" + url.getPath() + "\")", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
Uri data = intent.getData();
|
||||
if (data != null) handleUrl(data);
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package dev.vencord.vendroid;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.net.Uri;
|
||||
import android.webkit.*;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class VChromeClient extends WebChromeClient {
|
||||
private final MainActivity activity;
|
||||
|
||||
public VChromeClient(MainActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage msg) {
|
||||
var m = String.format(Locale.ENGLISH, "[Javascript] %s @ %d: %s", msg.message(), msg.lineNumber(), msg.sourceId());
|
||||
switch (msg.messageLevel()) {
|
||||
case DEBUG:
|
||||
Logger.d(m);
|
||||
break;
|
||||
case ERROR:
|
||||
Logger.e(m);
|
||||
break;
|
||||
case WARNING:
|
||||
Logger.w(m);
|
||||
break;
|
||||
default:
|
||||
Logger.i(m);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
||||
if (activity.filePathCallback != null)
|
||||
activity.filePathCallback.onReceiveValue(null);
|
||||
|
||||
activity.filePathCallback = filePathCallback;
|
||||
|
||||
var i = fileChooserParams.createIntent();
|
||||
try {
|
||||
activity.startActivityForResult(i, MainActivity.FILECHOOSER_RESULTCODE);
|
||||
} catch (ActivityNotFoundException ex) {
|
||||
activity.filePathCallback = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package dev.vencord.vendroid;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.View;
|
||||
import android.webkit.*;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
public class VWebviewClient extends WebViewClient {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
var url = request.getUrl();
|
||||
if ("discord.com".equals(url.getAuthority()) || "about:blank".equals(url.toString())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, url);
|
||||
view.getContext().startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
view.evaluateJavascript(HttpClient.VencordRuntime, null);
|
||||
view.evaluateJavascript(HttpClient.VencordMobileRuntime, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
super.onPageFinished(view, url);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest req) {
|
||||
var uri = req.getUrl();
|
||||
if (req.isForMainFrame() || req.getUrl().getPath().endsWith(".css")) {
|
||||
try {
|
||||
return doFetch(req);
|
||||
} catch (IOException ex) {
|
||||
Logger.e("Error during shouldInterceptRequest", ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private WebResourceResponse doFetch(WebResourceRequest req) throws IOException {
|
||||
var url = req.getUrl().toString();
|
||||
var conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setRequestMethod(req.getMethod());
|
||||
for (var h : req.getRequestHeaders().entrySet()) {
|
||||
conn.setRequestProperty(h.getKey(), h.getValue());
|
||||
}
|
||||
|
||||
var code = conn.getResponseCode();
|
||||
var msg = conn.getResponseMessage();
|
||||
|
||||
var headers = conn.getHeaderFields();
|
||||
var modifiedHeaders = new HashMap<String, String>(headers.size());
|
||||
for (var header : headers.entrySet()) {
|
||||
if (!"Content-Security-Policy".equalsIgnoreCase(header.getKey())) {
|
||||
modifiedHeaders.put(header.getKey(), header.getValue().get(0));
|
||||
}
|
||||
}
|
||||
if (url.endsWith(".css")) modifiedHeaders.put("Content-Type", "text/css");
|
||||
|
||||
return new WebResourceResponse(modifiedHeaders.getOrDefault("Content-Type", "application/octet-stream"), "utf-8", code, msg, modifiedHeaders, conn.getInputStream());
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package dev.vencord.vendroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class VencordNative {
|
||||
private final WebView wv;
|
||||
private final Activity activity;
|
||||
|
||||
public VencordNative(Activity activity, WebView wv) {
|
||||
this.activity = activity;
|
||||
this.wv = wv;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void goBack() {
|
||||
activity.runOnUiThread(() -> {
|
||||
if (wv.canGoBack())
|
||||
wv.goBack();
|
||||
else
|
||||
// no idea what i was smoking when I wrote this
|
||||
activity.getActionBar();
|
||||
});
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/baseline_check_24.xml
Normal file
5
app/src/main/res/drawable/baseline_check_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
|
||||
</vector>
|
139
app/src/main/res/layout/activity_welcome.xml
Normal file
139
app/src/main/res/layout/activity_welcome.xml
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="20dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/updates_title"
|
||||
android:textColor="@color/splash_text"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/check_vendroid_updates"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:checked="true"
|
||||
android:text="@string/autocheck_vendroid" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/autoupdate_vencord"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:checked="true"
|
||||
android:enabled="false"
|
||||
android:text="@string/autocheck_vencord" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Discord branch"
|
||||
android:textColor="@color/splash_text"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/stable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="Stable" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/ptb"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="PTB" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/canary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Canary" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Vencord location"
|
||||
android:textColor="@color/splash_text"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/allow_custom_location"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Set custom location (Only check this if you know what you're doing!)" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Custom location URL">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/custom_location"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
style="?attr/collapsingToolbarLayoutLargeStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
|
||||
app:contentScrim="?attr/colorSurfaceContainer">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="Welcome to Vendroid"
|
||||
app:layout_collapseMode="pin"
|
||||
android:elevation="0dp" />
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/start_vendroid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:contentDescription="Start Vendroid"
|
||||
android:text="Start Vendroid"
|
||||
app:icon="@drawable/baseline_check_24" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -1,4 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name">Vendroid</string>
|
||||
<string name="splashscreen_text">Loading Vendroid…</string>
|
||||
<string name="autocheck_vencord">Automatically update Vencord (Coming soon)</string>
|
||||
<string name="autocheck_vendroid">Automatically check for Vendroid updates</string>
|
||||
<string name="updates_title">Updates</string>
|
||||
</resources>
|
Loading…
Reference in a new issue