1
0
Fork 0

i give up im just pushing everything

This commit is contained in:
nin0dev 2024-03-24 16:29:55 -04:00
parent 0e48db5921
commit f182a98b69
20 changed files with 519 additions and 407 deletions

View file

@ -3,11 +3,11 @@ plugins {
} }
android { android {
namespace 'dev.vencord.vendroid' namespace 'com.nin0dev.vendroid'
compileSdk 34 compileSdk 34
defaultConfig { defaultConfig {
applicationId "dev.vencord.vendroid" applicationId "com.nin0dev.vendroid"
minSdk 21 minSdk 21
targetSdk 34 targetSdk 34
versionCode 3 versionCode 3
@ -16,7 +16,7 @@ android {
buildTypes { buildTypes {
debug { debug {
applicationIdSuffix ".dbg"
} }
release { release {
@ -32,5 +32,6 @@ android {
dependencies { dependencies {
implementation 'androidx.annotation:annotation:1.7.1' implementation 'androidx.annotation:annotation:1.7.1'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.28' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.28'
implementation 'com.google.android.material:material:1.11.0'
} }

View file

@ -5,28 +5,34 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/LoadingTheme" 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 <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"
android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection" android:configChanges="orientation|keyboardHidden|screenSize|layoutDirection"
android:exported="true"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/LoadingTheme"> android:theme="@style/LoadingTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" /> <data android:scheme="https" />
<data android:scheme="http" /> <data android:scheme="http" />
<data android:host="discord.com" /> <data android:host="discord.com" />

View 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"
}

View 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
}
}

View 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!!)
}
}

View 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
}
}

View 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
}
}

View 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)
}
}

View 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()
}
}
}

View 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)
}
}

View file

@ -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";
}

View file

@ -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");
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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();
});
}
}

View 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>

View 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>

View file

@ -1,4 +1,7 @@
<resources> <resources>
<string name="app_name">Vendroid</string> <string name="app_name">Vendroid</string>
<string name="splashscreen_text">Loading Vendroid&#8230;</string> <string name="splashscreen_text">Loading Vendroid&#8230;</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> </resources>