Merge branch 'dev' into feature-jump-to-reply-message
Signed-off-by: Alex Yong <alexjyong@gmail.com>
This commit is contained in:
commit
7f07a5c5b8
|
|
@ -19,3 +19,7 @@ revoltbuild.properties
|
|||
sentry.properties
|
||||
/.kotlin/sessions
|
||||
app/src/main/assets/embedded
|
||||
/app/src/main/jniLibs/arm64-v8a/
|
||||
/app/src/main/jniLibs/armeabi-v7a/
|
||||
/app/src/main/jniLibs/x86/
|
||||
/app/src/main/jniLibs/x86_64/
|
||||
|
|
|
|||
39
README.md
39
README.md
|
|
@ -1,21 +1,20 @@
|
|||
# Revolt on Android
|
||||
<div align="center">
|
||||
<h1>Revolt for Android</h1>
|
||||
<p>Official <a href="https://revolt.chat">Revolt</a> Android app.</p>
|
||||
<br/><br/>
|
||||
<div>
|
||||
<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt="Get it on Google Play" width="200">
|
||||
<br/>
|
||||
</div>
|
||||
<small>Google Play is a trademark of Google LLC.</small>
|
||||
<br/><br/><br/>
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
This is the official Android app for the [Revolt](https://revolt.chat) chat platform.
|
||||
The codebase includes the app itself, as well as an internal library for interacting with the Revolt
|
||||
API.
|
||||
|
||||
| Module | Package | Description |
|
||||
|--------|---------------|----------------------|
|
||||
| `:app` | `chat.revolt` | The main app module. |
|
||||
|
||||
The API library is part of the `app` module, and is not intended to be used as a standalone library,
|
||||
as it makes liberal use of Android-specific APIs for reactivity.
|
||||
|
||||
The app is written in Kotlin, and uses
|
||||
the [Jetpack Compose](https://developer.android.com/jetpack/compose) UI toolkit, the current state
|
||||
of the art for Android UI development.
|
||||
API. The app is written in Kotlin, and wholly
|
||||
uses [Jetpack Compose](https://developer.android.com/jetpack/compose).
|
||||
|
||||
## Stack
|
||||
|
||||
|
|
@ -29,9 +28,10 @@ of the art for Android UI development.
|
|||
|
||||
## Resources
|
||||
|
||||
### Revolt on Android
|
||||
### Revolt for Android
|
||||
|
||||
- [Revolt on Android Technical Documentation](https://revoltchat.github.io/android/)
|
||||
- [Roadmap](https://op.revolt.wtf/projects/revolt-for-android/work_packages)
|
||||
- [Revolt for Android Technical Documentation](https://revoltchat.github.io/android/)
|
||||
- [Android-specific Contribution Guide](https://revoltchat.github.io/android/contributing/guidelines/)
|
||||
—**read carefully before contributing!**
|
||||
|
||||
|
|
@ -39,8 +39,9 @@ of the art for Android UI development.
|
|||
|
||||
- [Revolt Project Board](https://github.com/revoltchat/revolt/discussions) (Submit feature requests
|
||||
here)
|
||||
- [Revolt Testers Server](https://app.revolt.chat/invite/Testers)
|
||||
- [General Revolt Contribution Guide](https://developers.revolt.chat/contributing)
|
||||
- [Revolt Development Server](https://app.revolt.chat/invite/API)
|
||||
- [Revolt Server](https://app.revolt.chat/invite/Testers)
|
||||
- [General Revolt Contribution Guide](https://developers.revolt.chat/contrib.html)
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
|
@ -48,4 +49,4 @@ Open the project in Android Studio. You can then run the app on an emulator or a
|
|||
running the `app` module.
|
||||
|
||||
In-depth setup instructions can be found
|
||||
at [Setting up your Development Environment](https://revoltchat.github.io/android/contributing/setup/)
|
||||
at [Setting up your Development Environment](https://revoltchat.github.io/android/contributing/setup/)
|
||||
|
|
|
|||
|
|
@ -73,12 +73,12 @@ fun buildproperty(propertyName: String, fallbackEnv: String? = null): String? {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdk = 35
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "chat.revolt"
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
targetSdk = 36
|
||||
versionCode = Integer.parseInt("001_003_206".replace("_", ""), 10)
|
||||
versionName = "1.3.6b"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package chat.revolt
|
||||
|
||||
import android.app.Application
|
||||
import android.os.StrictMode
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import logcat.AndroidLogcatLogger
|
||||
|
|
@ -15,6 +16,21 @@ class RevoltApplication : Application() {
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
AndroidLogcatLogger.installOnDebuggableApp(this, minPriority = LogPriority.VERBOSE)
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
// Enable strict mode primarily to catch non-API usage, although we detect all
|
||||
// violations for our reference.
|
||||
// https://developer.android.com/reference/android/os/StrictMode
|
||||
StrictMode.setVmPolicy(
|
||||
StrictMode.VmPolicy
|
||||
.Builder()
|
||||
.apply {
|
||||
detectAll()
|
||||
penaltyLog()
|
||||
}
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ object Experiments {
|
|||
val useKotlinBasedMarkdownRenderer = ExperimentInstance(false)
|
||||
val usePolar = ExperimentInstance(false)
|
||||
val enableServerIdentityOptions = ExperimentInstance(false)
|
||||
val useFinalMarkdownRenderer = ExperimentInstance(false)
|
||||
|
||||
suspend fun hydrateWithKv() {
|
||||
val kvStorage = KVStorage(RevoltApplication.instance)
|
||||
|
|
@ -48,5 +49,14 @@ object Experiments {
|
|||
enableServerIdentityOptions.setEnabled(
|
||||
kvStorage.getBoolean("exp/enableServerIdentityOptions") == true
|
||||
)
|
||||
useFinalMarkdownRenderer.setEnabled(
|
||||
kvStorage.getBoolean("exp/useFinalMarkdownRenderer") == true
|
||||
)
|
||||
|
||||
if (useFinalMarkdownRenderer.isEnabled && useKotlinBasedMarkdownRenderer.isEnabled) {
|
||||
// if jbm and fm are enabled, fm takes precedence. this should not be possible in practice
|
||||
useKotlinBasedMarkdownRenderer.setEnabled(false)
|
||||
kvStorage.set("exp/useKotlinBasedMarkdownRenderer", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +56,19 @@ sealed class VoiceChannels2_0Variates {
|
|||
object Disabled : VoiceChannels2_0Variates()
|
||||
}
|
||||
|
||||
@FeatureFlag("FinalMarkdown")
|
||||
sealed class FinalMarkdownVariates {
|
||||
@Treatment(
|
||||
"Enable the new FinalMarkdown library for all users"
|
||||
)
|
||||
object Enabled : FinalMarkdownVariates()
|
||||
|
||||
@Treatment(
|
||||
"Disable the new FinalMarkdown library for all users"
|
||||
)
|
||||
object Disabled : FinalMarkdownVariates()
|
||||
}
|
||||
|
||||
object FeatureFlags {
|
||||
@FeatureFlag("LabsAccessControl")
|
||||
var labsAccessControl by mutableStateOf<LabsAccessControlVariates>(
|
||||
|
|
@ -101,4 +114,14 @@ object FeatureFlags {
|
|||
is VoiceChannels2_0Variates.Enabled -> true
|
||||
is VoiceChannels2_0Variates.Disabled -> false
|
||||
}
|
||||
|
||||
@FeatureFlag("FinalMarkdown")
|
||||
var finalMarkdown by mutableStateOf<FinalMarkdownVariates>(
|
||||
FinalMarkdownVariates.Disabled
|
||||
)
|
||||
val finalMarkdownGranted: Boolean
|
||||
get() = when (finalMarkdown) {
|
||||
is FinalMarkdownVariates.Enabled -> true
|
||||
is FinalMarkdownVariates.Disabled -> false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package chat.revolt.ndk
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class FinalMarkdownNodeTest(
|
||||
val test: Int,
|
||||
)
|
||||
|
||||
@Suppress("KotlinJniMissingFunction")
|
||||
object FinalMarkdown {
|
||||
external fun init()
|
||||
external fun process(input: String): FinalMarkdownNodeTest
|
||||
}
|
||||
|
|
@ -3,12 +3,15 @@ package chat.revolt.ndk
|
|||
annotation class NativeLibrary(val name: String) {
|
||||
companion object {
|
||||
const val LIB_NAME_NATIVE_MARKDOWN = "stendal"
|
||||
const val LIB_NAME_NATIVE_MARKDOWN_V2 = "finalmarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
object NativeLibraries {
|
||||
fun init() {
|
||||
System.loadLibrary(NativeLibrary.LIB_NAME_NATIVE_MARKDOWN)
|
||||
System.loadLibrary(NativeLibrary.LIB_NAME_NATIVE_MARKDOWN_V2)
|
||||
Stendal.init()
|
||||
FinalMarkdown.init()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,21 +2,36 @@ package chat.revolt.screens.settings
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonGroupDefaults
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.ToggleButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
|
@ -24,18 +39,35 @@ import androidx.navigation.NavController
|
|||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.RevoltApplication
|
||||
import chat.revolt.api.settings.Experiments
|
||||
import chat.revolt.api.settings.FeatureFlags
|
||||
import chat.revolt.api.settings.LoadedSettings
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import chat.revolt.settings.dsl.SettingsPage
|
||||
import chat.revolt.settings.dsl.SubcategoryContentInsets
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
enum class MarkdownRenderer {
|
||||
Stendal, JetBrains, FinalMarkdown
|
||||
}
|
||||
|
||||
class ExperimentsSettingsScreenViewModel : ViewModel() {
|
||||
private val kv = KVStorage(RevoltApplication.instance)
|
||||
|
||||
fun init() {
|
||||
viewModelScope.launch {
|
||||
useKotlinMdRendererChecked.value = Experiments.useKotlinBasedMarkdownRenderer.isEnabled
|
||||
when {
|
||||
Experiments.useKotlinBasedMarkdownRenderer.isEnabled -> {
|
||||
mdRenderer.value = MarkdownRenderer.JetBrains
|
||||
}
|
||||
|
||||
Experiments.useFinalMarkdownRenderer.isEnabled -> {
|
||||
mdRenderer.value = MarkdownRenderer.FinalMarkdown
|
||||
}
|
||||
|
||||
else -> {
|
||||
mdRenderer.value = MarkdownRenderer.Stendal
|
||||
}
|
||||
}
|
||||
usePolarChecked.value = Experiments.usePolar.isEnabled
|
||||
enableServerIdentityOptionsChecked.value =
|
||||
Experiments.enableServerIdentityOptions.isEnabled
|
||||
|
|
@ -66,13 +98,33 @@ class ExperimentsSettingsScreenViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
val useKotlinMdRendererChecked = mutableStateOf(false)
|
||||
val mdRenderer = mutableStateOf(MarkdownRenderer.Stendal)
|
||||
|
||||
fun setUseKotlinMdRendererChecked(value: Boolean) {
|
||||
fun setMdRenderer(value: MarkdownRenderer) {
|
||||
viewModelScope.launch {
|
||||
kv.set("exp/useKotlinBasedMarkdownRenderer", value)
|
||||
Experiments.useKotlinBasedMarkdownRenderer.setEnabled(value)
|
||||
useKotlinMdRendererChecked.value = value
|
||||
when (value) {
|
||||
MarkdownRenderer.Stendal -> {
|
||||
kv.set("exp/useKotlinBasedMarkdownRenderer", false)
|
||||
Experiments.useKotlinBasedMarkdownRenderer.setEnabled(false)
|
||||
kv.set("exp/useFinalMarkdownRenderer", false)
|
||||
Experiments.useFinalMarkdownRenderer.setEnabled(false)
|
||||
}
|
||||
|
||||
MarkdownRenderer.JetBrains -> {
|
||||
kv.set("exp/useKotlinBasedMarkdownRenderer", true)
|
||||
Experiments.useKotlinBasedMarkdownRenderer.setEnabled(true)
|
||||
kv.set("exp/useFinalMarkdownRenderer", false)
|
||||
Experiments.useFinalMarkdownRenderer.setEnabled(false)
|
||||
}
|
||||
|
||||
MarkdownRenderer.FinalMarkdown -> {
|
||||
kv.set("exp/useKotlinBasedMarkdownRenderer", false)
|
||||
Experiments.useKotlinBasedMarkdownRenderer.setEnabled(false)
|
||||
kv.set("exp/useFinalMarkdownRenderer", true)
|
||||
Experiments.useFinalMarkdownRenderer.setEnabled(true)
|
||||
}
|
||||
}
|
||||
mdRenderer.value = value
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +150,7 @@ class ExperimentsSettingsScreenViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun ExperimentsSettingsScreen(
|
||||
navController: NavController,
|
||||
|
|
@ -147,21 +200,63 @@ fun ExperimentsSettingsScreen(
|
|||
Text("Experiments", maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("New Message Markdown Renderer")
|
||||
},
|
||||
supportingContent = {
|
||||
Text("Use a Kotlin-based Markdown renderer for messages rather than the C++ one. Missing features may be present.")
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = viewModel.useKotlinMdRendererChecked.value,
|
||||
onCheckedChange = null
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable { viewModel.setUseKotlinMdRendererChecked(!viewModel.useKotlinMdRendererChecked.value) }
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("Markdown Renderer")
|
||||
},
|
||||
supportingContent = {
|
||||
when (viewModel.mdRenderer.value) {
|
||||
MarkdownRenderer.Stendal -> Text("Use the original C++ Markdown renderer for messages.")
|
||||
MarkdownRenderer.JetBrains -> Text("Use the Kotlin-based JetBrains Markdown renderer for messages. This renderer is more feature-complete and has better results.")
|
||||
MarkdownRenderer.FinalMarkdown -> Text("Use a new. blazingly fast markdown renderer for messages. This renderer is experimental and may have missing features.")
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.animateContentSize()
|
||||
.weight(1f)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(IntrinsicSize.Max)
|
||||
.padding(end = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween),
|
||||
) {
|
||||
ToggleButton(
|
||||
checked = viewModel.mdRenderer.value == MarkdownRenderer.Stendal,
|
||||
onCheckedChange = { viewModel.setMdRenderer(MarkdownRenderer.Stendal) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.semantics { role = Role.RadioButton }
|
||||
) {
|
||||
Text("Default")
|
||||
}
|
||||
ToggleButton(
|
||||
checked = viewModel.mdRenderer.value == MarkdownRenderer.JetBrains,
|
||||
onCheckedChange = { viewModel.setMdRenderer(MarkdownRenderer.JetBrains) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.semantics { role = Role.RadioButton }
|
||||
) {
|
||||
Text("Kotlin")
|
||||
}
|
||||
if (FeatureFlags.finalMarkdownGranted || viewModel.mdRenderer.value == MarkdownRenderer.FinalMarkdown) {
|
||||
ToggleButton(
|
||||
checked = viewModel.mdRenderer.value == MarkdownRenderer.FinalMarkdown,
|
||||
onCheckedChange = { viewModel.setMdRenderer(MarkdownRenderer.FinalMarkdown) },
|
||||
enabled = FeatureFlags.finalMarkdownGranted,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.semantics { role = Role.RadioButton }
|
||||
) {
|
||||
Text("Final")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
<string name="mfa_password_lead">Enter your password to continue.</string>
|
||||
|
||||
<string name="about">About</string>
|
||||
<string name="about_full_name">Revolt on Android</string>
|
||||
<string name="about_full_name">Revolt for Android</string>
|
||||
<string name="about_brought_to_you_by">Brought to you with ❤️ by the Revolt team.</string>
|
||||
<string name="about_tab_version">Version</string>
|
||||
<string name="about_tab_details">Details</string>
|
||||
|
|
|
|||
|
|
@ -26,20 +26,7 @@ export default defineConfig({
|
|||
link: "/contributing/setup",
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Beta Test",
|
||||
items: [
|
||||
{
|
||||
label: "Geographic Availability",
|
||||
link: "/beta/availability-regions",
|
||||
},
|
||||
{
|
||||
label: "Join the Beta Test",
|
||||
link: "/beta/joining-the-public-beta-test",
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
/* {
|
||||
label: "Reference",
|
||||
autogenerate: { directory: "reference" },
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
title: Geographic Availability of the Beta Test
|
||||
description: Find out where you can join the beta test for Revolt on Android.
|
||||
template: doc
|
||||
---
|
||||
|
||||
The beta test for Revolt on Android is available for testers in the following jurisdictions:
|
||||
|
||||
- Armenia
|
||||
- Australia
|
||||
- Austria
|
||||
- Azerbaijan
|
||||
- Belgium
|
||||
- Brazil
|
||||
- Bulgaria
|
||||
- Canada
|
||||
- Croatia
|
||||
- Cyprus
|
||||
- Czechia
|
||||
- Denmark (Kingdom of)
|
||||
- Dominican Republic
|
||||
- Estonia
|
||||
- Finland (including Åland)
|
||||
- France (including overseas départements and other outre-mer territories)
|
||||
- Georgia
|
||||
- Germany
|
||||
- Gibraltar
|
||||
- Greece
|
||||
- Hungary
|
||||
- Iceland
|
||||
- Ireland
|
||||
- Israel
|
||||
- Italy
|
||||
- Japan
|
||||
- Latvia
|
||||
- Liechtenstein
|
||||
- Lithuania
|
||||
- Luxembourg
|
||||
- Malaysia
|
||||
- Malta
|
||||
- Moldova
|
||||
- Monaco
|
||||
- Netherlands
|
||||
- New Zealand
|
||||
- Norway (Kingdom of)
|
||||
- Poland
|
||||
- Portugal
|
||||
- Romania
|
||||
- San Marino
|
||||
- Slovakia
|
||||
- Slovenia
|
||||
- South Africa
|
||||
- South Korea
|
||||
- Spain
|
||||
- Sweden
|
||||
- Switzerland
|
||||
- Türkiye
|
||||
- Ukraine
|
||||
- United Kingdom
|
||||
- United States (including Territories of the U.S.)
|
||||
- Vatican City
|
||||
|
||||
## My region isn't listed!
|
||||
|
||||
While we don't offer the beta test in *all* regions of the world, we may and most likely will add regions that are not listed upon request. Please request the addition on [Jenvolt](https://rvlt.gg/jen) (if you beta test, you are probably already in the server!).
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
title: Joining the Public Beta Test
|
||||
description: Learn how to join the public beta test for Revolt for Android.
|
||||
template: doc
|
||||
---
|
||||
|
||||
The public beta test for Revolt for Android is available to anyone with an Android device
|
||||
in [those jurisdictions](https://revoltchat.github.io/android/beta/availability-regions/) where the
|
||||
beta test is available. The beta test is open to everyone, and you can join the beta test by
|
||||
following these steps:
|
||||
|
||||
1. [Join the Revolt for Android Alpha Track on Google Groups](https://groups.google.com/g/revolt-android-alpha-track).
|
||||
2. [Opt-in to the Revolt for Android Beta Test on Google Play](https://play.google.com/apps/testing/chat.revolt).
|
||||
3. [Download Revolt for Android from Google Play](https://play.google.com/store/apps/details?id=chat.revolt).
|
||||
|
||||
You're all set! You can now use Revolt on Android and help us test new features and improvements.
|
||||
|
||||
If you encounter any issues or have any feedback, please let us know
|
||||
by [reporting an issue](https://androidtasks.starlightjen.com/)
|
||||
or [joining the Revolt Developers server](https://rvlt.gg/api).
|
||||
|
|
@ -107,6 +107,18 @@ import { Tabs, TabItem, Steps } from "@astrojs/starlight/components"
|
|||
deno run -A scripts/download_deps.ts
|
||||
```
|
||||
|
||||
:::tip
|
||||
You can run this script with the `-y` flag to skip confirmation prompts.
|
||||
:::
|
||||
|
||||
:::note
|
||||
This script will query the Revolt version control server for the latest version of the
|
||||
final-markdown native library and download it. If you do not wish to use pre-built libraries,
|
||||
you can build final-markdown yourself from the source code at https://git.revolt.chat/android/final-markdown,
|
||||
and copy the files to the `app/src/main/jniLibs` folder. Note the files that are downloaded are
|
||||
exactly the same as the ones we use for the app on Google Play, so you can be sure they are safe to use.
|
||||
:::
|
||||
|
||||
8. Copy the `revoltbuild.properties.example` file to `revoltbuild.properties` and fill in the required values.
|
||||
|
||||
```sh
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { resolve } from "jsr:@std/path"
|
||||
import { parseArgs } from "jsr:@std/cli/parse-args"
|
||||
import { ZipReader, Uint8ArrayReader, Uint8ArrayWriter } from "jsr:@zip-js/zip-js"
|
||||
|
||||
const args = parseArgs(Deno.args, {
|
||||
boolean: ["yes", "y", "auto-accept"],
|
||||
|
|
@ -11,9 +12,11 @@ const args = parseArgs(Deno.args, {
|
|||
const autoAccept = args.yes || args["auto-accept"]
|
||||
|
||||
const outputFolderParent = resolve(Deno.cwd(), "app", "src", "main", "assets")
|
||||
const jniLibsFolder = resolve(Deno.cwd(), "app", "src", "main", "jniLibs")
|
||||
|
||||
try {
|
||||
Deno.statSync(outputFolderParent)
|
||||
Deno.statSync(jniLibsFolder)
|
||||
} catch (_) {
|
||||
console.error(
|
||||
"\x1b[31m" + // red
|
||||
|
|
@ -330,4 +333,80 @@ for (const dep of deps) {
|
|||
console.log(`Downloaded ${dep.file} to ${file}`)
|
||||
}
|
||||
|
||||
console.log("Done.")
|
||||
const libsQuery = "https://git.revolt.chat/api/v1/repos/android/final-markdown/releases/latest"
|
||||
|
||||
console.log("The script will now query the Revolt version control server for the latest version"
|
||||
+ " of the final-markdown native library and download it. If you do not wish to use pre-built"
|
||||
+ " libraries, you can build final-markdown yourself from the source code at"
|
||||
+ " https://git.revolt.chat/android/final-markdown, and copy the files to the"
|
||||
+ " app/src/main/jniLibs folder. Note the files that are downloaded are exactly the same as"
|
||||
+ " the ones we use for the app on Google Play, so you can be sure they are safe to use.")
|
||||
|
||||
if (!autoAccept) {
|
||||
console.log(`Will now query ${libsQuery} for the latest version of the library and download it.`)
|
||||
if (!confirm("Continue?")) {
|
||||
console.log("Aborted.")
|
||||
Deno.exit(0)
|
||||
}
|
||||
} else {
|
||||
console.log(`Will now query ${libsQuery} for the latest version of the library and download it. (auto-accepted)`)
|
||||
}
|
||||
|
||||
const queryLibsRes = await fetch(libsQuery)
|
||||
if (!queryLibsRes.ok) {
|
||||
console.error(`Failed to fetch the latest library version: ${queryLibsRes.status} ${queryLibsRes.statusText}`)
|
||||
Deno.exit(1)
|
||||
}
|
||||
|
||||
const libsJson = await queryLibsRes.json()
|
||||
const zipUrl = libsJson
|
||||
.assets
|
||||
.find((asset: { name: string }) => asset.name === "jniLibs.zip")?.browser_download_url
|
||||
|
||||
if (!zipUrl) {
|
||||
console.error("No jniLibs.zip found in the latest release.")
|
||||
Deno.exit(1)
|
||||
}
|
||||
|
||||
const libsRes = await fetch(zipUrl)
|
||||
if (!libsRes.ok) {
|
||||
console.error(`Failed to fetch the jniLibs.zip: ${libsRes.status} ${libsRes.statusText}`)
|
||||
Deno.exit(1)
|
||||
}
|
||||
const libsData = await libsRes.arrayBuffer()
|
||||
|
||||
const libArchitectures = ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"]
|
||||
|
||||
const zipFileBytes = new Uint8Array(libsData)
|
||||
|
||||
const zipReader = new ZipReader(new Uint8ArrayReader(zipFileBytes))
|
||||
|
||||
const entries = await zipReader.getEntries()
|
||||
|
||||
const createDirPromises = libArchitectures.map(arch => {
|
||||
const dirPath = resolve(jniLibsFolder, arch)
|
||||
return Deno.mkdir(dirPath, { recursive: true })
|
||||
})
|
||||
await Promise.all(createDirPromises)
|
||||
|
||||
const writeFilePromises = libArchitectures.map(async arch => {
|
||||
const filePathInZip = `${arch}/libfinalmarkdown.so`
|
||||
|
||||
const entry = entries.find(e => e.filename === filePathInZip)
|
||||
|
||||
if (!entry) {
|
||||
throw new Error(`Expected file not found in zip: ${filePathInZip}`)
|
||||
}
|
||||
|
||||
const writer = new Uint8ArrayWriter()
|
||||
const fileData = await entry.getData!(writer)
|
||||
|
||||
const destinationPath = resolve(jniLibsFolder, filePathInZip)
|
||||
return Deno.writeFile(destinationPath, fileData)
|
||||
})
|
||||
|
||||
await Promise.all(writeFilePromises)
|
||||
|
||||
// Close the zip reader
|
||||
await zipReader.close()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue