Initial commit

mane
Mia Raindrops 3 months ago
commit 5d7695aa15
Signed by: Mia Raindrops
GPG Key ID: EFBDC68435A574B7

16
.gitignore vendored

@ -0,0 +1,16 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
app/google-services.json

3
.idea/.gitignore vendored

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

@ -0,0 +1 @@
Delta

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<targetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_4_API_TiramisuPrivacySandbox.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2022-12-27T21:19:50.930476Z" />
</component>
</project>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="/Volumes/Unicorn/delta-android" />
<option name="modules">
<set>
<option value="/Volumes/Unicorn/delta-android" />
<option value="/Volumes/Unicorn/delta-android/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.7.21" />
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -0,0 +1,3 @@
# TODO
- About dialog

1
app/.gitignore vendored

@ -0,0 +1 @@
/build

@ -0,0 +1,61 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.gms.google-services")
}
android {
namespace = "dev.equestria.delta"
compileSdk = 33
defaultConfig {
applicationId = "dev.equestria.delta"
minSdk = 27
targetSdk = 33
versionCode = 2
versionName = "5.0-8"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
resConfigs("en", "fr")
}
buildFeatures {
buildConfig = true
}
buildTypes {
getByName("release") {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation("com.android.volley:volley:1.2.1")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.5.1")
implementation("com.google.android.material:material:1.7.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
implementation("androidx.core:core-ktx:1.9.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.4")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
implementation("com.squareup.picasso:picasso:2.8")
implementation(platform("com.google.firebase:firebase-bom:31.1.1"))
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-messaging-ktx")
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Binary file not shown.

Binary file not shown.

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "dev.equestria.delta",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "5.0-8",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="dev.equestria.delta">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:localeConfig="@xml/locales_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Delta"
tools:targetApi="33">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_launch_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="delta.equestria.dev" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,61 @@
package dev.equestria.delta
import android.content.Context
import android.content.res.Resources
import android.util.Log
import com.android.volley.Request
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.equestria.delta.databinding.ActivityMainBinding
import org.json.JSONObject
import kotlin.system.exitProcess
class HTTPRequest {
companion object {
fun request(
url: String,
session: String?,
context: Context,
resources: Resources,
activity: MainActivity,
initial: Boolean,
binding: ActivityMainBinding,
positiveCallback: (JSONObject) -> Unit = { },
negativeCallback: (Unit) -> Unit = { }
) {
val volleyQueue = Volley.newRequestQueue(context)
val data = JSONObject()
data.put("session", session)
val jsonObjectRequest = JsonObjectRequest(Request.Method.POST, url, data,
{ response ->
Log.i("HTTPRequest", response.toString())
positiveCallback(response)
},
{ error ->
MaterialAlertDialogBuilder(context).setCancelable(false)
.setTitle(resources.getString(R.string.offline_title))
.setMessage(resources.getString(R.string.offline_message))
.setPositiveButton(resources.getString(R.string.offline_retry)) { _, _ ->
if (initial) {
activity.initialCheck()
} else {
binding.webView.reload()
}
}.setNegativeButton(resources.getString(R.string.offline_close)) { _, _ ->
activity.moveTaskToBack(true)
exitProcess(0)
}.setNeutralButton(resources.getString(R.string.offline_status)) { _, _ ->
negativeCallback(Unit)
}.show()
Log.e("HTTPRequest", "Request error: ${error.localizedMessage}")
})
volleyQueue.add(jsonObjectRequest)
}
}
}

@ -0,0 +1,510 @@
package dev.equestria.delta
import android.Manifest
import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Intent
import android.content.res.Resources.getSystem
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.webkit.CookieManager
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.google.android.gms.tasks.OnCompleteListener
import com.google.android.material.color.DynamicColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.elevation.SurfaceColors
import com.google.firebase.messaging.FirebaseMessaging
import com.squareup.picasso.Picasso
import com.squareup.picasso.Target
import dev.equestria.delta.databinding.ActivityMainBinding
import org.json.JSONObject
import java.net.URL
import kotlin.system.exitProcess
val Int.dp: Int get() = (this / getSystem().displayMetrics.density).toInt()
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var appMenu: Menu
private lateinit var navigationMenu: Menu
private var changedNavItem: Boolean = false
private var deltaInformation: JSONObject? = null
private var lastRequestLoggedIn: Boolean = false
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.action_bar, menu)
if (menu != null) {
appMenu = menu
}
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.user -> {
binding.webView.loadUrl("${getString(R.string.delta_root)}/profile")
return true
}
R.id.btn_forward -> {
if (binding.webView.canGoForward()) binding.webView.goForward()
return true
}
R.id.btn_reload -> {
binding.webView.reload()
return true
}
R.id.btn_user_logout -> {
binding.webView.loadUrl("${getString(R.string.delta_root)}/logout")
return true
}
R.id.btn_about -> {
MaterialAlertDialogBuilder(binding.root.context).setTitle(
getString(
R.string.about_title,
getString(R.string.app_launch_name)
)
).setMessage(
getString(
R.string.about_message,
BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ", " + BuildConfig.BUILD_TYPE + ")",
deltaInformation?.get("version") ?: "-",
Build.VERSION.RELEASE + " " + Build.VERSION.CODENAME + " (" + Build.VERSION.SDK_INT + ")",
System.getProperty("os.version"),
Build.DEVICE + "/" + Build.MODEL,
Build.VERSION.SECURITY_PATCH,
Build.BOARD,
Build.BOOTLOADER
)
).setPositiveButton(getString(R.string.about_close)) { _, _ -> }.show()
return true
}
else -> return false
}
}
fun getCookie(siteName: String?, CookieName: String?): String? {
var cookieValue: String? = null
val cookieManager: CookieManager = CookieManager.getInstance()
val cookies: String? = cookieManager.getCookie(siteName)
return if (cookies != null) {
val temp = cookies.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (ar1 in temp) {
if (ar1.contains(CookieName!!)) {
val temp1 =
ar1.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
cookieValue = temp1[1]
}
}
cookieValue
} else {
""
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DynamicColors.applyToActivitiesIfAvailable(application)
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val mChannel = NotificationChannel("default", name, importance)
mChannel.description = descriptionText
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
if (Build.VERSION.SDK_INT >= 33) {
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1
)
}
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w("Firebase", "Fetching FCM registration token failed", task.exception)
return@OnCompleteListener
}
val token = task.result
Log.d("Firebase", token)
HTTPRequest.request(
"${getString(R.string.delta_root)}/handoff/fcm/",
getCookie(getString(R.string.delta_root), "DeltaSession") + "||||" + token,
binding.root.context,
resources,
this,
true,
binding
) {
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = Uri.parse("https://status.equestria.dev/")
startActivity(openURL)
}
})
val color = SurfaceColors.SURFACE_2.getColor(this)
window.statusBarColor = color
window.navigationBarColor = color
supportActionBar?.setDisplayHomeAsUpEnabled(true)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
navigationMenu = binding.navigationBar.menu
initialCheck()
}
fun initialCheck() {
HTTPRequest.request("${getString(R.string.delta_root)}/handoff/version/",
getCookie(getString(R.string.delta_root), "DeltaSession"),
binding.root.context,
resources,
this,
true,
binding,
{
deltaInformation = it
appMenu.getItem(3).isEnabled = deltaInformation!!.get("loggedIn") as Boolean
initialise()
}) {
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = Uri.parse("https://status.equestria.dev/")
startActivity(openURL)
}
}
fun routineCheck() {
HTTPRequest.request("${getString(R.string.delta_root)}/handoff/version/",
getCookie(getString(R.string.delta_root), "DeltaSession"),
binding.root.context,
resources,
this,
false,
binding,
{
deltaInformation = it
appMenu.getItem(3).isEnabled = deltaInformation!!.get("loggedIn") as Boolean
if (deltaInformation!!.get("loggedIn") as Boolean || lastRequestLoggedIn) {
refreshAvatar(deltaInformation!!)
lastRequestLoggedIn = deltaInformation!!.get("loggedIn") as Boolean
if (lastRequestLoggedIn) {
appMenu.getItem(0).title = deltaInformation!!.get("name") as CharSequence?
} else {
appMenu.getItem(0).title = getString(R.string.navigation_profile)
}
}
}) {
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = Uri.parse("https://status.equestria.dev/")
startActivity(openURL)
}
}
fun refreshAvatar(deltaInformation: JSONObject) {
val target = object : Target {
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
try {
if (bitmap != null) {
appMenu.getItem(0).icon = BitmapDrawable(resources, bitmap)
}
} catch (ex: IllegalArgumentException) {
Log.e("Picasso", ex.toString())
}
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
Log.e("Picasso", e.toString())
}
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}
}
Picasso.get().load(
"${getString(R.string.delta_root)}/handoff/avatar/?token=" + deltaInformation.get("session")
).into(target)
}
@SuppressLint("SetJavaScriptEnabled")
fun initialise() {
binding.webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
appMenu.getItem(2).isEnabled = true
binding.progressBar.visibility = View.INVISIBLE
binding.webView.visibility = View.VISIBLE
val pageTitle = binding.webView.title?.split("·")?.get(0)?.trim() ?: ""
if (pageTitle.startsWith("(") && pageTitle.split(") ").count() > 1) {
title = pageTitle.split(")")[1].trim()
binding.navigationBar.getOrCreateBadge(R.id.navigation_profile).isVisible = true
} else {
title = pageTitle
binding.navigationBar.getOrCreateBadge(R.id.navigation_profile).isVisible =
false
}
if (URL(url).path.startsWith("/login")) {
binding.navigationBar.visibility = View.GONE
val params = binding.webView.layoutParams as ViewGroup.MarginLayoutParams
params.setMargins(0, 0, 0, 80.dp)
supportActionBar?.hide()
val color = SurfaceColors.SURFACE_0.getColor(binding.root.context)
window.statusBarColor = color
window.navigationBarColor = color
} else {
val params = binding.webView.layoutParams as ViewGroup.MarginLayoutParams
params.setMargins(0, 0, 0, 0)
binding.navigationBar.visibility = View.VISIBLE
supportActionBar?.show()
val color = SurfaceColors.SURFACE_2.getColor(binding.root.context)
window.statusBarColor = color
window.navigationBarColor = color
}
routineCheck()
appMenu.getItem(1).isEnabled = binding.webView.canGoForward()
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
appMenu.getItem(2).isEnabled = false
binding.progressBar.visibility = View.VISIBLE
if (!changedNavItem) {
if (URL(url).path.startsWith("/articles/") || URL(url).path === "/articles" || URL(
url
).path.startsWith("/edit/") || URL(url).path === "/edit" || URL(url).path.startsWith(
"/request/"
) || URL(url).path === "/request" || URL(url).path.startsWith("/people/") || URL(
url
).path === "/people" || URL(url).path.startsWith("/gallery/") || URL(url).path === "/gallery"
) {
changedNavItem = true
binding.navigationBar.selectedItemId = R.id.navigation_content
} else if (URL(url).path.startsWith("/search/") || URL(url).path === "/search") {
changedNavItem = true
binding.navigationBar.selectedItemId = R.id.navigation_search
} else if (URL(url).path.startsWith("/admin/") || URL(url).path === "/admin" || URL(
url
).path.startsWith("/profile/") || URL(url).path === "/profile" || URL(url).path.startsWith(
"/requests/"
) || URL(url).path === "/requests" || URL(url).path.startsWith("/plus/") || URL(
url
).path === "/plus" || URL(url).path.startsWith("/alerts/") || URL(url).path === "/alerts" || URL(
url
).path.startsWith("/support/") || URL(url).path === "/support" || URL(url).path.startsWith(
"/mobile_profile/"
) || URL(url).path === "/mobile_profile"
) {
changedNavItem = true
binding.navigationBar.selectedItemId = R.id.navigation_profile
} else {
changedNavItem = true
binding.navigationBar.selectedItemId = R.id.navigation_dashboard
}
}
appMenu.getItem(1).isEnabled = binding.webView.canGoForward()
changedNavItem = false
}
override fun shouldInterceptRequest(
view: WebView, request: WebResourceRequest
): WebResourceResponse? {
request.url.host?.let { Log.i("MainActivity", '"' + it + '"') }
if (request.url.host != getString(R.string.delta_root).split("/")[2]) {
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = request.url
startActivity(openURL)
return WebResourceResponse("text/javascript", "UTF-8", null)
}
return null
}
}
binding.navigationBar.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_dashboard -> {
setNavigationBarSelected(item.itemId)
if (!changedNavItem) {
changedNavItem = true
binding.webView.loadUrl("${getString(R.string.delta_root)}/")
}
true
}
R.id.navigation_search -> {
setNavigationBarSelected(item.itemId)
if (!changedNavItem) {
changedNavItem = true
binding.webView.loadUrl("${getString(R.string.delta_root)}/search")
}
true
}
R.id.navigation_content -> {
setNavigationBarSelected(item.itemId)
if (!changedNavItem) {
changedNavItem = true
binding.webView.loadUrl("${getString(R.string.delta_root)}/content")
}
true
}
R.id.navigation_profile -> {
setNavigationBarSelected(item.itemId)
if (!changedNavItem) {
changedNavItem = true
binding.webView.loadUrl("${getString(R.string.delta_root)}/profile")
}
true
}
else -> false
}
}
binding.navigationBar.menu.findItem(R.id.navigation_profile).iconTintList = null
binding.webView.settings.javaScriptEnabled = true
handoffApi31()
}
fun setNavigationBarSelected(id: Int) {
binding.navigationBar.menu.findItem(R.id.navigation_dashboard)
.setIcon(R.drawable.outline_home_24)
binding.navigationBar.menu.findItem(R.id.navigation_content)
.setIcon(R.drawable.outline_text_snippet_24)
binding.navigationBar.menu.findItem(R.id.navigation_search)
.setIcon(R.drawable.outline_search_24)
binding.navigationBar.menu.findItem(R.id.navigation_profile)
.setIcon(R.drawable.outline_person_24)
when (id) {
R.id.navigation_dashboard -> {
binding.navigationBar.menu.findItem(R.id.navigation_dashboard)
.setIcon(R.drawable.baseline_home_24)
}
R.id.navigation_content -> {
binding.navigationBar.menu.findItem(R.id.navigation_content)
.setIcon(R.drawable.baseline_text_snippet_24)
}
R.id.navigation_search -> {
binding.navigationBar.menu.findItem(R.id.navigation_search)
.setIcon(R.drawable.baseline_search_24)
}
R.id.navigation_profile -> {
binding.navigationBar.menu.findItem(R.id.navigation_profile)
.setIcon(R.drawable.baseline_person_24)
}
else -> {}
}
}
fun handoffApi31() {
val colors: ArrayList<Int> = arrayListOf(
SurfaceColors.SURFACE_0.getColor(this),
SurfaceColors.SURFACE_1.getColor(this),
SurfaceColors.SURFACE_2.getColor(this),
SurfaceColors.SURFACE_3.getColor(this),
SurfaceColors.SURFACE_4.getColor(this),
SurfaceColors.SURFACE_5.getColor(this),
binding.textView.currentTextColor,
binding.textView.currentHintTextColor,
binding.button.currentTextColor,
binding.button.highlightColor
)
val appLinkAction = intent.action
val appLinkData: Uri? = intent.data
if (Intent.ACTION_VIEW == appLinkAction) {
if (appLinkData != null) {
binding.webView.loadUrl(
"${getString(R.string.delta_root)}/handoff/?version=" + BuildConfig.VERSION_CODE + "&colors=" + colors.joinToString(
","
) + "&return=" + java.net.URLEncoder.encode(appLinkData.path, "utf-8")
)
} else {
binding.webView.loadUrl(
"${getString(R.string.delta_root)}/handoff/?version=" + BuildConfig.VERSION_CODE + "&colors=" + colors.joinToString(
","
) + "&return=/"
)
}
} else {
binding.webView.loadUrl(
"${getString(R.string.delta_root)}/handoff/?version=" + BuildConfig.VERSION_CODE + "&colors=" + colors.joinToString(
","
) + "&return=/"
)
}
}
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
if (binding.webView.canGoBack()) {
binding.webView.goBack()
} else {
moveTaskToBack(true)
exitProcess(0)
}
}
override fun onSupportNavigateUp(): Boolean {
if (binding.webView.canGoBack()) {
binding.webView.goBack()
} else {
moveTaskToBack(true)
exitProcess(0)
}
return true
}
}

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="512"
android:viewportHeight="512">
<group
android:scaleX="0.92"
android:scaleY="0.92"
android:translateX="20.48"
android:translateY="20.48">
<path
android:fillColor="#FF000000"
android:pathData="M255.6,100.4l175,350.6H80.5L255.6,100.4M255.6,13.3c-7.1,0 -14.2,3.7 -17.9,11L14.1,472.1C7.5,485.4 17.2,501 32,501h447.1c14.9,0 24.5,-15.6 17.9,-28.9L273.5,24.3C269.8,16.9 262.7,13.3 255.6,13.3L255.6,13.3z" />
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20.41,8.41l-4.83,-4.83C15.21,3.21 14.7,3 14.17,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V9.83C21,9.3 20.79,8.79 20.41,8.41zM7,7h7v2H7V7zM17,17H7v-2h10V17zM17,13H7v-2h10V13z" />
</vector>

@ -0,0 +1,46 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
xmlns:tools="http://schemas.android.com/tools"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512"
tools:ignore="VectorRaster">
<path android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
<aapt:attr name="android:fillColor">
<gradient
android:endX="437.02"
android:endY="74.98"
android:startX="74.98"
android:startY="437.02"
android:type="linear">
<item
android:color="#FFE6E6E6"
android:offset="0" />
<item
android:color="#FFF2F2F2"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
<path android:pathData="M373.4,296.6c25.4,0 46,20.6 46,45.9c0,0 0,0 0,0v18.8c0,11.7 -3.7,23.1 -10.5,32.7c-31.6,44.2 -83.2,66.1 -153,66.1c-69.9,0 -121.5,-21.9 -153,-66.1c-6.8,-9.5 -10.4,-20.9 -10.4,-32.6v-18.8c0,-25.4 20.6,-46 45.9,-46c0,0 0,0 0,0L373.4,296.6L373.4,296.6zM255.8,51.5c56.4,0 102.2,45.7 102.2,102.1c0,56.4 -45.7,102.2 -102.1,102.2c0,0 0,0 0,0c-56.4,0 -102.2,-45.7 -102.2,-102.2S199.4,51.5 255.8,51.5z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="391"
android:endY="414.61"
android:startX="120.67"
android:startY="144.28"
android:type="linear">
<item
android:color="#E3E3E3"
android:offset="0" />
<item
android:color="#E0E0E0"
android:offset="0.5" />
<item
android:color="#FFFFFF"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
</vector>

@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="512"
android:viewportHeight="512">
<group
android:scaleX="0.34"
android:scaleY="0.34"
android:translateX="168.96"
android:translateY="168.96">
<path android:pathData="M237.7,24.3L14.1,472.1C7.5,485.4 17.2,501 32,501h447.1c14.9,0 24.5,-15.6 17.9,-28.9L273.5,24.3C266.1,9.6 245.1,9.6 237.7,24.3z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="437.58"
android:endY="550.88"
android:startX="73.62"
android:startY="186.92"
android:type="linear">
<item
android:color="#FFE44857"
android:offset="0" />
<item
android:color="#FFC711E1"
android:offset="0.5" />
<item
android:color="#FF7F52FF"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
</group>
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,5.69l5,4.5V18h-2v-6H9v6H7v-7.81l5,-4.5M12,3L2,12h3v8h6v-6h2v6h6v-8h3L12,3z" />
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,6c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2m0,10c2.7,0 5.8,1.29 6,2L6,18c0.23,-0.72 3.31,-2 6,-2m0,-12C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M14.17,5L19,9.83V19H5V5L14.17,5L14.17,5M14.17,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V9.83c0,-0.53 -0.21,-1.04 -0.59,-1.41l-4.83,-4.83C15.21,3.21 14.7,3 14.17,3L14.17,3zM7,15h10v2H7V15zM7,11h10v2H7V11zM7,7h7v2H7V7z" />
</vector>

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="@+id/webView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="e" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navigationBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hapticFeedbackEnabled="true"
android:soundEffectsEnabled="true"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/navigation_bar" />
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="80dp"
android:saveEnabled="true"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="@id/navigationBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar">
</WebView>
<ProgressBar
android:id="@+id/progressBar"
style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_marginTop="-8dp"
android:indeterminate="true"
android:padding="0dp"
android:paddingHorizontal="0dp"
android:paddingVertical="0dp"
android:paddingStart="0dp"
android:paddingLeft="0dp"
android:paddingTop="0dp"
android:paddingEnd="0dp"
android:paddingRight="0dp"
android:paddingBottom="0dp"
app:barrierMargin="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/user"
android:icon="@drawable/defaultuser"
android:title="@string/navigation_profile"
app:showAsAction="ifRoom" />
<item
android:id="@+id/btn_forward"
android:title="@string/navigation_forward"
app:showAsAction="never" />
<item
android:id="@+id/btn_reload"
android:title="@string/navigation_reload"
app:showAsAction="never" />
<item
android:id="@+id/btn_user_logout"
android:enabled="false"
android:title="@string/navigation_user_logout"
app:showAsAction="never" />
<item
android:id="@+id/btn_about"
android:enabled="true"
android:title="@string/navigation_user_about"
app:showAsAction="never" />
</menu>

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/navigation_dashboard"
android:icon="@drawable/baseline_home_24"
android:title="@string/pages_dashboard"
app:showAsAction="ifRoom" />
<item
android:id="@+id/navigation_content"
android:icon="@drawable/outline_text_snippet_24"
android:title="@string/pages_content"
app:showAsAction="ifRoom" />
<item
android:id="@+id/navigation_search"
android:icon="@drawable/outline_search_24"
android:title="@string/pages_search"
app:showAsAction="ifRoom" />
<item
android:id="@+id/navigation_profile"
android:icon="@drawable/baseline_person_24"
android:title="@string/pages_profile"
app:showAsAction="ifRoom" />
</menu>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,26 @@
<resources>
<string name="navigation_forward">Aller en avant</string>
<string name="navigation_search">Recherche</string>
<string name="navigation_profile">Utilisateur</string>
<string name="navigation_reload">Recharger</string>
<string name="navigation_user_profile">Profil</string>
<string name="navigation_user_logout">Se déconnecter</string>
<string name="navigation_user_about">À propos</string>
<string name="about_title">À propos de %1$s</string>
<string name="about_message">Une application Android pour la plate-forme Delta.\n\nApplication : %1$s\nServeur : %2$s\nAndroid : %3$s\nMàJ : %6$s\nNoyau : %4$s\nAppareil : %5$s\nCarte : %7$s\nAmorceur : %8$s</string>
<string name="about_close">Fermer</string>
<string name="pages_dashboard">Accueil</string>
<string name="pages_search">Recherche</string>
<string name="pages_profile">Profil</string>
<string name="pages_alerts">Alertes</string>
<string name="pages_content">Contenu</string>
<string name="offline_title">Impossible de se connecter à Delta</string>
<string name="offline_message">Nous ne parvenons pas à joindre les serveurs de Delta. Vérifiez votre connexion Internet et réessayez.</string>
<string name="offline_close">Quitter</string>
<string name="offline_retry">Réessayer</string>
<string name="channel_name">Alertes Delta</string>
<string name="channel_description">Pour les utilisateurs Delta Ultra uniquement. Alertes qui apparaissent sur votre profil.</string>
<string name="about_copy">Copier</string>
<string name="about_copy_done">Informations copiées dans le presse-papiers</string>
<string name="offline_status">Ouvrir l\'état des services</string>
</resources>

@ -0,0 +1,3 @@
<resources>
<style name="Theme.Delta" parent="Theme.Material3.DynamicColors.DayNight" />
</resources>

@ -0,0 +1,4 @@
<resources>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#E3E3E3</color>
</resources>

@ -0,0 +1,26 @@
<resources>
<string name="navigation_forward">Navigate down</string>
<string name="navigation_search">Search</string>
<string name="navigation_profile">User</string>
<string name="navigation_reload">Reload</string>
<string name="navigation_user_profile">Profile</string>
<string name="navigation_user_logout">Log out</string>
<string name="navigation_user_about">About</string>
<string name="about_title">About %1$s</string>
<string name="about_message">An Android app for the Delta platform.\n\nApp: %1$s\nServer: %2$s\nAndroid: %3$s\nSecurity: %6$s\nKernel: %4$s\nDevice: %5$s\nBoard: %7$s\nBootloader: %8$s</string>
<string name="about_close">Close</string>
<string name="about_copy">Copy</string>
<string name="about_copy_done">Copied info to clipboard</string>
<string name="pages_dashboard">Home</string>
<string name="pages_search">Search</string>
<string name="pages_profile">Profile</string>
<string name="pages_alerts">Alerts</string>
<string name="pages_content">Content</string>
<string name="offline_title">Unable to connect to Delta</string>
<string name="offline_message">We are currently unable to connect to Delta\'s servers. Check your network connection and try again later.</string>
<string name="offline_close">Quit</string>
<string name="offline_status">Open status page</string>
<string name="offline_retry">Retry</string>
<string name="channel_name">Delta alerts</string>
<string name="channel_description">For Delta Ultra users only. Alerts that appear on your Delta profile.</string>
</resources>

@ -0,0 +1,3 @@
<resources>
<style name="Theme.Delta" parent="Theme.Material3.DynamicColors.DayNight" />
</resources>

@ -0,0 +1,5 @@
<resources>
<string name="app_name" translatable="false">Equestria.dev Delta</string>
<string name="app_launch_name" translatable="false">Delta</string>
<string name="delta_root" translatable="false">https://delta.equestria.dev</string>
</resources>

@ -0,0 +1 @@
<full-backup-content></full-backup-content>

@ -0,0 +1,3 @@
<data-extraction-rules>
<cloud-backup></cloud-backup>
</data-extraction-rules>

@ -0,0 +1,4 @@
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="fr" />
</locale-config>

@ -0,0 +1,31 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
// Make sure that you have the following two repositories
google() // Google's Maven repository
mavenCentral() // Maven Central repository
}
dependencies {
// Add the dependency for the Google services Gradle plugin
classpath("com.google.gms:google-services:4.3.14")
}
}
plugins {
id("com.android.application") version "8.0.0-alpha10" apply false
id("com.android.library") version "8.0.0-alpha10" apply false
id("org.jetbrains.kotlin.android") version "1.7.21" apply false
}
allprojects {
repositories {
// Make sure that you have the following two repositories
google() // Google's Maven repository
mavenCentral() // Maven Central repository
}
}

@ -0,0 +1,23 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects