Restructure everything, native is now top level and app is written in kotlin with new Jetpack Compose ui
17
.github/workflows/build.yml
vendored
@ -44,8 +44,7 @@ jobs:
|
||||
- name: Build native libs
|
||||
run: |
|
||||
unset ANDROID_SDK_ROOT # Deprecated, will cause an error if left set.
|
||||
cd native
|
||||
cargo ndk --target ${{ matrix.android-abi }} --platform 26 -o ../jniLibs build --release
|
||||
cargo ndk --target ${{ matrix.android-abi }} --platform 26 -o jniLibs build --release
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@ -65,8 +64,8 @@ jobs:
|
||||
|
||||
- name: Copy native libs
|
||||
run: |
|
||||
mkdir app/ruffle/src/main/jniLibs
|
||||
cp -r native-libs/*/* app/ruffle/src/main/jniLibs/
|
||||
mkdir app/src/main/jniLibs
|
||||
cp -r native-libs/*/* app/src/main/jniLibs/
|
||||
|
||||
- name: Set up Java 17
|
||||
uses: actions/setup-java@v4
|
||||
@ -77,21 +76,19 @@ jobs:
|
||||
- name: Build debug APK
|
||||
# The native libs are always built in release mode, this is left in here just so if
|
||||
# something with the signing procedure below goes haywire, we still have something.
|
||||
run: |
|
||||
cd app
|
||||
./gradlew assembleDebug # The release version doesn't work without signing
|
||||
run: ./gradlew assembleDebug # The release version doesn't work without signing
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ruffle-debug-apks
|
||||
path: app/ruffle/build/outputs/apk/debug/*.apk
|
||||
path: app/build/outputs/apk/debug/*.apk
|
||||
|
||||
- name: Decode keystore
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
env:
|
||||
ENCODED_STRING: ${{ secrets.KEYSTORE }}
|
||||
run: |
|
||||
echo $ENCODED_STRING | base64 -di > app/ruffle/androidkey.jks
|
||||
echo $ENCODED_STRING | base64 -di > app/androidkey.jks
|
||||
|
||||
- name: Build release APK
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
@ -107,5 +104,5 @@ jobs:
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
name: ruffle-release-apks
|
||||
path: app/ruffle/build/outputs/apk/release/*.apk
|
||||
path: app/build/outputs/apk/release/*.apk
|
||||
|
||||
|
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
.idea
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/target
|
||||
*.jks
|
0
native/Cargo.lock → Cargo.lock
generated
15
app/.gitignore
vendored
@ -1,15 +1,2 @@
|
||||
*.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
|
||||
/src/main/jniLibs
|
||||
|
3
app/.idea/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
@ -1 +0,0 @@
|
||||
Ruffle
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<value>
|
||||
<entry key="ruffle">
|
||||
<State />
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
</project>
|
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/ruffle" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -1,30 +0,0 @@
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="../../../../../layout/custom_preview.xml" value="0.2520833333333333" />
|
||||
<entry key="ruffle/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.25" />
|
||||
<entry key="ruffle/src/main/res/drawable/ic_baseline_menu_24.xml" value="0.1875" />
|
||||
<entry key="ruffle/src/main/res/drawable/ic_launcher_background.xml" value="0.2555" />
|
||||
<entry key="ruffle/src/main/res/drawable/ic_launcher_foreground.xml" value="0.1875" />
|
||||
<entry key="ruffle/src/main/res/drawable/ic_logo.xml" value="0.1875" />
|
||||
<entry key="ruffle/src/main/res/layout-port-navhidden/keyboard.xml" value="0.14492753623188406" />
|
||||
<entry key="ruffle/src/main/res/layout-port-navhidden/ruffle_keyboard.xml" value="0.18" />
|
||||
<entry key="ruffle/src/main/res/layout/activity_main.xml" value="0.25" />
|
||||
<entry key="ruffle/src/main/res/layout/ggg.xml" value="0.11141304347826086" />
|
||||
<entry key="ruffle/src/main/res/layout/keyboard.xml" value="0.25" />
|
||||
<entry key="ruffle/src/main/res/layout/overlay_layout.xml" value="0.1480978260869565" />
|
||||
<entry key="ruffle/src/main/res/layout/pog.xml" value="0.11141304347826086" />
|
||||
<entry key="ruffle/src/main/res/layout/test.xml" value="0.10054347826086957" />
|
||||
<entry key="ruffle/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.1875" />
|
||||
<entry key="ruffle/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.2555" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,9 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '8.2.1' apply false
|
||||
id 'com.android.library' version '8.2.1' apply false
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
124
app/build.gradle.kts
Normal file
@ -0,0 +1,124 @@
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.jetbrainsKotlinAndroid)
|
||||
alias(libs.plugins.cargoNdkAndroid).apply(System.getenv("GITHUB_ACTIONS") == null)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "rs.ruffle"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "rs.ruffle"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
|
||||
ndk {
|
||||
abiFilters.addAll(listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86"))
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
storeFile = file("androidkey.jks")
|
||||
storePassword = System.getenv("SIGNING_STORE_PASSWORD")
|
||||
keyAlias = System.getenv("SIGNING_KEY_ALIAS")
|
||||
keyPassword = System.getenv("SIGNING_KEY_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
signingConfig = signingConfigs.findByName("release")
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
prefab = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
splits {
|
||||
// Configures multiple APKs based on ABI.
|
||||
abi {
|
||||
// Enables building multiple APKs per ABI.
|
||||
isEnable = true
|
||||
|
||||
// Resets the list of ABIs that Gradle should create APKs for to none.
|
||||
reset()
|
||||
|
||||
// Specifies a list of ABIs that Gradle should create APKs for.
|
||||
include("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
|
||||
|
||||
// Specifies that we also want to generate a universal APK that includes all ABIs.
|
||||
isUniversalApk = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.navigation.runtime.ktx)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.games.activity)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.androidx.appcompat)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
}
|
||||
|
||||
// On GHA, we prebuild the native libs separately for fasterness,
|
||||
// and this plugin doesn't recognize them, so would build them again.
|
||||
if (System.getenv("GITHUB_ACTIONS") == null) {
|
||||
cargoNdk {
|
||||
module = "."
|
||||
apiLevel = 26
|
||||
buildType = "release"
|
||||
}
|
||||
}
|
2
app/ruffle/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
/build
|
||||
src/main/jniLibs/*
|
@ -1,102 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id "com.github.willir.rust.cargo-ndk-android" version "0.3.4" apply false
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
|
||||
buildFeatures {
|
||||
prefab true
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId "rs.ruffle"
|
||||
minSdk 26
|
||||
targetSdk 33
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
ndk {
|
||||
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile = file("androidkey.jks")
|
||||
storePassword System.getenv("SIGNING_STORE_PASSWORD")
|
||||
keyAlias System.getenv("SIGNING_KEY_ALIAS")
|
||||
keyPassword System.getenv("SIGNING_KEY_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
|
||||
signingConfig signingConfigs.release
|
||||
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
namespace 'rs.ruffle'
|
||||
|
||||
splits {
|
||||
// Configures multiple APKs based on ABI.
|
||||
abi {
|
||||
// Enables building multiple APKs per ABI.
|
||||
enable true
|
||||
|
||||
// Resets the list of ABIs that Gradle should create APKs for to none.
|
||||
reset()
|
||||
|
||||
// Specifies a list of ABIs that Gradle should create APKs for.
|
||||
include 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'
|
||||
|
||||
// Specifies that we also want to generate a universal APK that includes all ABIs.
|
||||
universalApk true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
|
||||
// To use the Android Frame Pacing library
|
||||
//implementation "androidx.games:games-frame-pacing:1.9.1"
|
||||
|
||||
// To use the Android Performance Tuner
|
||||
//implementation "androidx.games:games-performance-tuner:1.5.0"
|
||||
|
||||
// To use the Games Activity library
|
||||
implementation "androidx.games:games-activity:2.0.2"
|
||||
|
||||
// To use the Games Controller Library
|
||||
//implementation "androidx.games:games-controller:1.1.0"
|
||||
|
||||
// To use the Games Text Input Library
|
||||
//implementation "androidx.games:games-text-input:1.1.0"
|
||||
|
||||
}
|
||||
|
||||
// On GHA, we prebuild the native libs separately for fasterness,
|
||||
// and this plugin doesn't recognize them, so would build them again.
|
||||
if (System.getenv("GITHUB_ACTIONS") == null) {
|
||||
apply plugin: "com.github.willir.rust.cargo-ndk-android"
|
||||
|
||||
cargoNdk {
|
||||
module = "../native"
|
||||
apiLevel = 26
|
||||
buildType = "release"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 13 KiB |
@ -1,137 +0,0 @@
|
||||
package rs.ruffle;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
|
||||
protected void startFromContentUri(Uri uri) {
|
||||
|
||||
ContentResolver resolver = getContentResolver();
|
||||
try {
|
||||
InputStream inputStream = resolver.openInputStream(uri);
|
||||
|
||||
int available = inputStream.available();
|
||||
byte[] bytes = new byte[available];
|
||||
// assuming the whole contents will be available at once
|
||||
int _num_bytes_read = inputStream.read(bytes);
|
||||
|
||||
FullscreenNativeActivity.SWF_BYTES = bytes;
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
||||
}
|
||||
|
||||
Intent intent = new Intent(MainActivity.this, FullscreenNativeActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
void startFromHttpUrl(String url) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
||||
urlConnection.connect();
|
||||
|
||||
int length = urlConnection.getContentLength();
|
||||
Log.i("rfl", "content length: " + length);
|
||||
byte[] bytes = new byte[length];
|
||||
int offs = 0;
|
||||
try {
|
||||
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
|
||||
|
||||
while (offs < length) {
|
||||
|
||||
int read = in.read(bytes, offs, length-offs);
|
||||
offs += read;
|
||||
if (read > 0)
|
||||
Log.i("rfl", "read " + read + " bytes");
|
||||
}
|
||||
Log.i("rfl", "read done: " + offs);
|
||||
|
||||
|
||||
|
||||
FullscreenNativeActivity.SWF_BYTES = bytes;
|
||||
|
||||
|
||||
Intent intent = new Intent(MainActivity.this, FullscreenNativeActivity.class);
|
||||
|
||||
startActivity(intent);
|
||||
} finally {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.i("rfl", "ioerror e " + e);
|
||||
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestNoStatusBarFeature();
|
||||
setContentView(R.layout.activity_main);
|
||||
hideActionBar();
|
||||
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(getIntent().getAction())) {
|
||||
Uri uri = getIntent().getData();
|
||||
if ("https".equals(uri.getScheme()) || "http".equals(uri.getScheme()))
|
||||
startFromHttpUrl(uri.toString());
|
||||
else
|
||||
startFromContentUri(uri);
|
||||
}
|
||||
|
||||
|
||||
ActivityResultLauncher launcher = registerForActivityResult(
|
||||
new ActivityResultContracts.GetContent(),
|
||||
uri -> startFromContentUri(uri)
|
||||
);
|
||||
|
||||
View button = findViewById(R.id.button);
|
||||
|
||||
button.setOnClickListener((event) -> {
|
||||
launcher.launch("application/x-shockwave-flash");
|
||||
});
|
||||
|
||||
|
||||
View button3 = findViewById(R.id.button3);
|
||||
button3.setOnClickListener((event) -> {
|
||||
EditText swfUrl = findViewById(R.id.editTextSwfUrl);
|
||||
startFromHttpUrl(swfUrl.getText().toString());
|
||||
});
|
||||
}
|
||||
|
||||
private void requestNoStatusBarFeature() {
|
||||
//Hiding the status bar this way makes it see through when pulled down
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
private void hideActionBar() {
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null)
|
||||
actionBar.hide();
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:ruffle="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#37528C"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="136dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="#FFAD33"
|
||||
android:backgroundTint="#FFAD33"
|
||||
android:foregroundTint="#FFAD33"
|
||||
android:text="@string/load_an_swf"
|
||||
android:textColor="@color/black"
|
||||
ruffle:layout_constraintBottom_toTopOf="@+id/linearLayout"
|
||||
ruffle:layout_constraintEnd_toEndOf="parent"
|
||||
ruffle:layout_constraintHorizontal_bias="0.498"
|
||||
ruffle:layout_constraintStart_toStartOf="parent"
|
||||
ruffle:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
ruffle:rippleColor="#FFAD33" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="308dp"
|
||||
android:layout_height="108dp"
|
||||
android:contentDescription="@string/ruffle_logo_description"
|
||||
ruffle:layout_constraintBottom_toTopOf="@+id/button"
|
||||
ruffle:layout_constraintEnd_toEndOf="parent"
|
||||
ruffle:layout_constraintHorizontal_bias="0.5"
|
||||
ruffle:layout_constraintStart_toStartOf="parent"
|
||||
ruffle:layout_constraintTop_toTopOf="parent"
|
||||
ruffle:layout_constraintVertical_chainStyle="spread"
|
||||
ruffle:srcCompat="@drawable/ic_logo" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="143dp"
|
||||
android:orientation="horizontal"
|
||||
ruffle:layout_constraintBottom_toBottomOf="parent"
|
||||
ruffle:layout_constraintEnd_toEndOf="parent"
|
||||
ruffle:layout_constraintHorizontal_bias="0.492"
|
||||
ruffle:layout_constraintStart_toStartOf="parent"
|
||||
ruffle:layout_constraintTop_toBottomOf="@+id/button">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextSwfUrl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textUri"
|
||||
android:minHeight="48dp"
|
||||
android:text="SWF URL" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button3"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="136dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="#FFAD33"
|
||||
android:backgroundTint="#FFAD33"
|
||||
android:foregroundTint="#FFAD33"
|
||||
android:text="@string/open_url"
|
||||
android:textColor="@color/black"
|
||||
ruffle:rippleColor="#FFAD33" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,16 +0,0 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Ruffle" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
@ -1,7 +0,0 @@
|
||||
<resources>
|
||||
<string name="app_name">Ruffle</string>
|
||||
<string name="load_an_swf">Load an SWF</string>
|
||||
<string name="open_url">Open URL</string>
|
||||
<string name="ruffle">Ruffle</string>
|
||||
<string name="ruffle_logo_description">Ruffle logo</string>
|
||||
</resources>
|
@ -1,16 +0,0 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Ruffle" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
@ -0,0 +1,24 @@
|
||||
package rs.ruffle
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("rs.ruffle", appContext.packageName)
|
||||
}
|
||||
}
|
@ -1,74 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET">
|
||||
|
||||
</uses-permission>
|
||||
|
||||
<!-- android:allowNativeHeapPointerTagging="false" -->
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Ruffle"
|
||||
>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
|
||||
<data android:scheme="file"/>
|
||||
<data android:scheme="content"/>
|
||||
|
||||
<data android:mimeType="application/x-shockwave-flash"/>
|
||||
|
||||
<data android:pathSuffix="swf" />
|
||||
<data android:pathPattern="*.swf" />
|
||||
</intent-filter>
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.swf" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<!-- no workyyy -->
|
||||
<!-- android:windowSoftInputMode="stateAlwaysVisible|adjustResize" -->
|
||||
|
||||
<activity
|
||||
android:name=".FullscreenNativeActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
|
||||
android:screenOrientation="user"
|
||||
>
|
||||
<meta-data android:name="android.app.lib_name" android:value="ruffle_android" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MainActivity" />
|
||||
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<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:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Ruffle"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
|
||||
<data android:scheme="file"/>
|
||||
<data android:scheme="content"/>
|
||||
|
||||
<data android:mimeType="application/x-shockwave-flash"/>
|
||||
|
||||
<data android:pathSuffix="swf" />
|
||||
<data android:pathPattern="*.swf" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.swf" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".FullscreenNativeActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
|
||||
android:screenOrientation="user"
|
||||
>
|
||||
<meta-data android:name="android.app.lib_name" android:value="ruffle_android" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MainActivity" />
|
||||
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
@ -1,20 +1,23 @@
|
||||
package rs.ruffle;
|
||||
|
||||
|
||||
import com.google.androidgamesdk.GameActivity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import android.widget.Button;
|
||||
import android.widget.PopupMenu;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.view.ViewCompat;
|
||||
@ -22,12 +25,8 @@ import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.PopupMenu;
|
||||
|
||||
import com.google.androidgamesdk.GameActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
44
app/src/main/java/rs/ruffle/MainActivity.kt
Normal file
@ -0,0 +1,44 @@
|
||||
package rs.ruffle
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import rs.ruffle.ui.theme.RuffleTheme
|
||||
import java.io.IOException
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
RuffleTheme {
|
||||
RuffleNavHost(openSwf = { openSwf(it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openSwf(uri: Uri) {
|
||||
val resolver = contentResolver
|
||||
try {
|
||||
val inputStream = resolver.openInputStream(uri)
|
||||
inputStream.use {
|
||||
val available = inputStream!!.available()
|
||||
val bytes = ByteArray(available)
|
||||
// assuming the whole contents will be available at once
|
||||
inputStream.read(bytes)
|
||||
FullscreenNativeActivity.SWF_BYTES = bytes
|
||||
}
|
||||
} catch (_: IOException) {
|
||||
}
|
||||
|
||||
val intent = Intent(
|
||||
this@MainActivity,
|
||||
FullscreenNativeActivity::class.java
|
||||
)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
29
app/src/main/java/rs/ruffle/Navigation.kt
Normal file
@ -0,0 +1,29 @@
|
||||
package rs.ruffle
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
|
||||
object Destinations {
|
||||
const val SELECT_SWF_ROUTE = "select"
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RuffleNavHost(
|
||||
navController: NavHostController = rememberNavController(),
|
||||
openSwf: (uri: Uri) -> Unit
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Destinations.SELECT_SWF_ROUTE,
|
||||
) {
|
||||
composable(Destinations.SELECT_SWF_ROUTE) {
|
||||
SelectSwfRoute(
|
||||
openSwf = openSwf
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
200
app/src/main/java/rs/ruffle/SelectSwfScreen.kt
Normal file
@ -0,0 +1,200 @@
|
||||
package rs.ruffle
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.paddingFromBaseline
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import rs.ruffle.ui.theme.RuffleTheme
|
||||
import rs.ruffle.ui.theme.slightlyDeemphasizedAlpha
|
||||
|
||||
@Composable
|
||||
fun BrandBar() {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_logo_dark),
|
||||
contentDescription = stringResource(id = R.string.logo_description),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 76.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectSwfRoute(
|
||||
openSwf: (uri: Uri) -> Unit
|
||||
) {
|
||||
SelectSwfScreen(
|
||||
openSwf = openSwf
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectSwfScreen(openSwf: (uri: Uri) -> Unit) {
|
||||
Scaffold { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.wrapContentHeight(align = Alignment.CenterVertically)
|
||||
) {
|
||||
BrandBar()
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 75.dp),
|
||||
text = stringResource(id = R.string.work_in_progress_warning)
|
||||
)
|
||||
SelectSwfUrlOrFile(openSwf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SelectSwfUrlOrFile(
|
||||
openSwf: (uri: Uri) -> Unit
|
||||
) {
|
||||
val urlState by rememberSaveable(stateSaver = UrlStateSaver) {
|
||||
mutableStateOf(UrlState())
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 20.dp)
|
||||
) {
|
||||
val submitUrl = {
|
||||
if (urlState.isValid) {
|
||||
openSwf(Uri.parse(urlState.text))
|
||||
} else {
|
||||
urlState.enableShowErrors()
|
||||
}
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = urlState.text,
|
||||
onValueChange = { urlState.text = it },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onFocusChanged { focusState ->
|
||||
urlState.onFocusChange(focusState.isFocused)
|
||||
if (!focusState.isFocused) {
|
||||
urlState.enableShowErrors()
|
||||
}
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.url),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
},
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
isError = urlState.showErrors(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
imeAction = ImeAction.Go,
|
||||
keyboardType = KeyboardType.Uri
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = { submitUrl() }
|
||||
),
|
||||
singleLine = true
|
||||
)
|
||||
urlState.getError()?.let { error -> TextFieldError(textError = error) }
|
||||
|
||||
Button(
|
||||
onClick = submitUrl,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 28.dp, bottom = 3.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.open_url),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.or),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = slightlyDeemphasizedAlpha),
|
||||
modifier = Modifier.paddingFromBaseline(top = 25.dp)
|
||||
)
|
||||
PickSwfButton(openSwf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PickSwfButton(onSelect: (uri: Uri) -> Unit) {
|
||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
if (it != null) {
|
||||
onSelect(it)
|
||||
}
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
launcher.launch(
|
||||
"application/x-shockwave-flash"
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 20.dp, bottom = 24.dp),
|
||||
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.select_a_swf))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextFieldError(textError: String) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(
|
||||
text = textError,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Select SWF - Light", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Select SWF - Dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun SelectSwfScreenPreview() {
|
||||
RuffleTheme {
|
||||
Surface {
|
||||
SelectSwfScreen(
|
||||
openSwf = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
53
app/src/main/java/rs/ruffle/TextFieldState.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package rs.ruffle
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.listSaver
|
||||
import androidx.compose.runtime.setValue
|
||||
|
||||
open class TextFieldState(
|
||||
private val validator: (String) -> Boolean = { true },
|
||||
private val errorFor: (String) -> String = { "" }
|
||||
) {
|
||||
var text: String by mutableStateOf("")
|
||||
|
||||
// was the TextField ever focused
|
||||
var isFocusedDirty: Boolean by mutableStateOf(false)
|
||||
var isFocused: Boolean by mutableStateOf(false)
|
||||
private var displayErrors: Boolean by mutableStateOf(false)
|
||||
|
||||
open val isValid: Boolean
|
||||
get() = validator(text)
|
||||
|
||||
fun onFocusChange(focused: Boolean) {
|
||||
isFocused = focused
|
||||
if (focused) isFocusedDirty = true
|
||||
}
|
||||
|
||||
fun enableShowErrors() {
|
||||
// only show errors if the text was at least once focused
|
||||
if (isFocusedDirty) {
|
||||
displayErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
fun showErrors() = !isValid && displayErrors
|
||||
|
||||
open fun getError(): String? {
|
||||
return if (showErrors()) {
|
||||
errorFor(text)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun textFieldStateSaver(state: TextFieldState) = listSaver<TextFieldState, Any>(
|
||||
save = { listOf(it.text, it.isFocusedDirty) },
|
||||
restore = {
|
||||
state.apply {
|
||||
text = it[0] as String
|
||||
isFocusedDirty = it[1] as Boolean
|
||||
}
|
||||
}
|
||||
)
|
22
app/src/main/java/rs/ruffle/UrlState.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package rs.ruffle
|
||||
|
||||
import android.util.Patterns
|
||||
|
||||
class UrlState(val url: String? = null) :
|
||||
TextFieldState(validator = ::isUrlValid, errorFor = ::urlValidationError) {
|
||||
init {
|
||||
url?.let {
|
||||
text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun urlValidationError(url: String): String {
|
||||
return "Invalid url: $url"
|
||||
}
|
||||
|
||||
private fun isUrlValid(url: String): Boolean {
|
||||
return Patterns.WEB_URL.matcher(url).matches()
|
||||
}
|
||||
|
||||
val UrlStateSaver = textFieldStateSaver(UrlState())
|
228
app/src/main/java/rs/ruffle/ui/theme/Color.kt
Normal file
@ -0,0 +1,228 @@
|
||||
package rs.ruffle.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val ruffleBlue = Color(0xFF37528c)
|
||||
|
||||
val primaryLight = Color(0xFF845400)
|
||||
val onPrimaryLight = Color(0xFFFFFFFF)
|
||||
val primaryContainerLight = Color(0xFFFFB858)
|
||||
val onPrimaryContainerLight = Color(0xFF4D2F00)
|
||||
val secondaryLight = Color(0xFF7A582A)
|
||||
val onSecondaryLight = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLight = Color(0xFFFFD5A4)
|
||||
val onSecondaryContainerLight = Color(0xFF5D3E12)
|
||||
val tertiaryLight = Color(0xFF213D76)
|
||||
val onTertiaryLight = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLight = Color(0xFF47619C)
|
||||
val onTertiaryContainerLight = Color(0xFFFFFFFF)
|
||||
val errorLight = Color(0xFFBA1A1A)
|
||||
val onErrorLight = Color(0xFFFFFFFF)
|
||||
val errorContainerLight = Color(0xFFFFDAD6)
|
||||
val onErrorContainerLight = Color(0xFF410002)
|
||||
val backgroundLight = Color(0xFFFFF8F4)
|
||||
val onBackgroundLight = Color(0xFF211A12)
|
||||
val surfaceLight = Color(0xFFFFF8F4)
|
||||
val onSurfaceLight = Color(0xFF211A12)
|
||||
val surfaceVariantLight = Color(0xFFF4DFC9)
|
||||
val onSurfaceVariantLight = Color(0xFF524434)
|
||||
val outlineLight = Color(0xFF857462)
|
||||
val outlineVariantLight = Color(0xFFD7C3AE)
|
||||
val scrimLight = Color(0xFF000000)
|
||||
val inverseSurfaceLight = Color(0xFF372F26)
|
||||
val inverseOnSurfaceLight = Color(0xFFFDEEE1)
|
||||
val inversePrimaryLight = Color(0xFFFFB95B)
|
||||
val surfaceDimLight = Color(0xFFE6D8CA)
|
||||
val surfaceBrightLight = Color(0xFFFFF8F4)
|
||||
val surfaceContainerLowestLight = Color(0xFFFFFFFF)
|
||||
val surfaceContainerLowLight = Color(0xFFFFF1E5)
|
||||
val surfaceContainerLight = Color(0xFFFAEBDE)
|
||||
val surfaceContainerHighLight = Color(0xFFF4E6D8)
|
||||
val surfaceContainerHighestLight = Color(0xFFEFE0D3)
|
||||
|
||||
val primaryLightMediumContrast = Color(0xFF5F3B00)
|
||||
val onPrimaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightMediumContrast = Color(0xFFA26800)
|
||||
val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightMediumContrast = Color(0xFF5B3D11)
|
||||
val onSecondaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightMediumContrast = Color(0xFF936E3E)
|
||||
val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightMediumContrast = Color(0xFF213D76)
|
||||
val onTertiaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightMediumContrast = Color(0xFF47619C)
|
||||
val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorLightMediumContrast = Color(0xFF8C0009)
|
||||
val onErrorLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightMediumContrast = Color(0xFFDA342E)
|
||||
val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightMediumContrast = Color(0xFFFFF8F4)
|
||||
val onBackgroundLightMediumContrast = Color(0xFF211A12)
|
||||
val surfaceLightMediumContrast = Color(0xFFFFF8F4)
|
||||
val onSurfaceLightMediumContrast = Color(0xFF211A12)
|
||||
val surfaceVariantLightMediumContrast = Color(0xFFF4DFC9)
|
||||
val onSurfaceVariantLightMediumContrast = Color(0xFF4E4031)
|
||||
val outlineLightMediumContrast = Color(0xFF6C5C4B)
|
||||
val outlineVariantLightMediumContrast = Color(0xFF897866)
|
||||
val scrimLightMediumContrast = Color(0xFF000000)
|
||||
val inverseSurfaceLightMediumContrast = Color(0xFF372F26)
|
||||
val inverseOnSurfaceLightMediumContrast = Color(0xFFFDEEE1)
|
||||
val inversePrimaryLightMediumContrast = Color(0xFFFFB95B)
|
||||
val surfaceDimLightMediumContrast = Color(0xFFE6D8CA)
|
||||
val surfaceBrightLightMediumContrast = Color(0xFFFFF8F4)
|
||||
val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val surfaceContainerLowLightMediumContrast = Color(0xFFFFF1E5)
|
||||
val surfaceContainerLightMediumContrast = Color(0xFFFAEBDE)
|
||||
val surfaceContainerHighLightMediumContrast = Color(0xFFF4E6D8)
|
||||
val surfaceContainerHighestLightMediumContrast = Color(0xFFEFE0D3)
|
||||
|
||||
val primaryLightHighContrast = Color(0xFF331E00)
|
||||
val onPrimaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightHighContrast = Color(0xFF5F3B00)
|
||||
val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightHighContrast = Color(0xFF331E00)
|
||||
val onSecondaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightHighContrast = Color(0xFF5B3D11)
|
||||
val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightHighContrast = Color(0xFF002051)
|
||||
val onTertiaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightHighContrast = Color(0xFF25417A)
|
||||
val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorLightHighContrast = Color(0xFF4E0002)
|
||||
val onErrorLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightHighContrast = Color(0xFF8C0009)
|
||||
val onErrorContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightHighContrast = Color(0xFFFFF8F4)
|
||||
val onBackgroundLightHighContrast = Color(0xFF211A12)
|
||||
val surfaceLightHighContrast = Color(0xFFFFF8F4)
|
||||
val onSurfaceLightHighContrast = Color(0xFF000000)
|
||||
val surfaceVariantLightHighContrast = Color(0xFFF4DFC9)
|
||||
val onSurfaceVariantLightHighContrast = Color(0xFF2D2214)
|
||||
val outlineLightHighContrast = Color(0xFF4E4031)
|
||||
val outlineVariantLightHighContrast = Color(0xFF4E4031)
|
||||
val scrimLightHighContrast = Color(0xFF000000)
|
||||
val inverseSurfaceLightHighContrast = Color(0xFF372F26)
|
||||
val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF)
|
||||
val inversePrimaryLightHighContrast = Color(0xFFFFE8D1)
|
||||
val surfaceDimLightHighContrast = Color(0xFFE6D8CA)
|
||||
val surfaceBrightLightHighContrast = Color(0xFFFFF8F4)
|
||||
val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF)
|
||||
val surfaceContainerLowLightHighContrast = Color(0xFFFFF1E5)
|
||||
val surfaceContainerLightHighContrast = Color(0xFFFAEBDE)
|
||||
val surfaceContainerHighLightHighContrast = Color(0xFFF4E6D8)
|
||||
val surfaceContainerHighestLightHighContrast = Color(0xFFEFE0D3)
|
||||
|
||||
val primaryDark = Color(0xFFFFDCB5)
|
||||
val onPrimaryDark = Color(0xFF462A00)
|
||||
val primaryContainerDark = Color(0xFFF8A72D)
|
||||
val onPrimaryContainerDark = Color(0xFF402600)
|
||||
val secondaryDark = Color(0xFFECBF87)
|
||||
val onSecondaryDark = Color(0xFF462B01)
|
||||
val secondaryContainerDark = Color(0xFF55370C)
|
||||
val onSecondaryContainerDark = Color(0xFFF8C991)
|
||||
val tertiaryDark = Color(0xFFB0C6FF)
|
||||
val onTertiaryDark = Color(0xFF0D2E66)
|
||||
val tertiaryContainerDark = Color(0xFF2D4882)
|
||||
val onTertiaryContainerDark = Color(0xFFDDE5FF)
|
||||
val errorDark = Color(0xFFFFB4AB)
|
||||
val onErrorDark = Color(0xFF690005)
|
||||
val errorContainerDark = Color(0xFF93000A)
|
||||
val onErrorContainerDark = Color(0xFFFFDAD6)
|
||||
val backgroundDark = Color(0xFF19120B)
|
||||
val onBackgroundDark = Color(0xFFEFE0D3)
|
||||
val surfaceDark = Color(0xFF19120B)
|
||||
val onSurfaceDark = Color(0xFFEFE0D3)
|
||||
val surfaceVariantDark = Color(0xFF524434)
|
||||
val onSurfaceVariantDark = Color(0xFFD7C3AE)
|
||||
val outlineDark = Color(0xFF9F8E7B)
|
||||
val outlineVariantDark = Color(0xFF524434)
|
||||
val scrimDark = Color(0xFF000000)
|
||||
val inverseSurfaceDark = Color(0xFFEFE0D3)
|
||||
val inverseOnSurfaceDark = Color(0xFF372F26)
|
||||
val inversePrimaryDark = Color(0xFF845400)
|
||||
val surfaceDimDark = Color(0xFF19120B)
|
||||
val surfaceBrightDark = Color(0xFF40382E)
|
||||
val surfaceContainerLowestDark = Color(0xFF130D06)
|
||||
val surfaceContainerLowDark = Color(0xFF211A12)
|
||||
val surfaceContainerDark = Color(0xFF261E16)
|
||||
val surfaceContainerHighDark = Color(0xFF302920)
|
||||
val surfaceContainerHighestDark = Color(0xFF3C332A)
|
||||
|
||||
val primaryDarkMediumContrast = Color(0xFFFFDCB5)
|
||||
val onPrimaryDarkMediumContrast = Color(0xFF3D2400)
|
||||
val primaryContainerDarkMediumContrast = Color(0xFFF8A72D)
|
||||
val onPrimaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val secondaryDarkMediumContrast = Color(0xFFF1C38B)
|
||||
val onSecondaryDarkMediumContrast = Color(0xFF231300)
|
||||
val secondaryContainerDarkMediumContrast = Color(0xFFB28957)
|
||||
val onSecondaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val tertiaryDarkMediumContrast = Color(0xFFB6CAFF)
|
||||
val onTertiaryDarkMediumContrast = Color(0xFF00143A)
|
||||
val tertiaryContainerDarkMediumContrast = Color(0xFF7690CE)
|
||||
val onTertiaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val errorDarkMediumContrast = Color(0xFFFFBAB1)
|
||||
val onErrorDarkMediumContrast = Color(0xFF370001)
|
||||
val errorContainerDarkMediumContrast = Color(0xFFFF5449)
|
||||
val onErrorContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val backgroundDarkMediumContrast = Color(0xFF19120B)
|
||||
val onBackgroundDarkMediumContrast = Color(0xFFEFE0D3)
|
||||
val surfaceDarkMediumContrast = Color(0xFF19120B)
|
||||
val onSurfaceDarkMediumContrast = Color(0xFFFFFAF7)
|
||||
val surfaceVariantDarkMediumContrast = Color(0xFF524434)
|
||||
val onSurfaceVariantDarkMediumContrast = Color(0xFFDCC7B2)
|
||||
val outlineDarkMediumContrast = Color(0xFFB2A08C)
|
||||
val outlineVariantDarkMediumContrast = Color(0xFF91806E)
|
||||
val scrimDarkMediumContrast = Color(0xFF000000)
|
||||
val inverseSurfaceDarkMediumContrast = Color(0xFFEFE0D3)
|
||||
val inverseOnSurfaceDarkMediumContrast = Color(0xFF302920)
|
||||
val inversePrimaryDarkMediumContrast = Color(0xFF664000)
|
||||
val surfaceDimDarkMediumContrast = Color(0xFF19120B)
|
||||
val surfaceBrightDarkMediumContrast = Color(0xFF40382E)
|
||||
val surfaceContainerLowestDarkMediumContrast = Color(0xFF130D06)
|
||||
val surfaceContainerLowDarkMediumContrast = Color(0xFF211A12)
|
||||
val surfaceContainerDarkMediumContrast = Color(0xFF261E16)
|
||||
val surfaceContainerHighDarkMediumContrast = Color(0xFF302920)
|
||||
val surfaceContainerHighestDarkMediumContrast = Color(0xFF3C332A)
|
||||
|
||||
val primaryDarkHighContrast = Color(0xFFFFFAF7)
|
||||
val onPrimaryDarkHighContrast = Color(0xFF000000)
|
||||
val primaryContainerDarkHighContrast = Color(0xFFFFBF6B)
|
||||
val onPrimaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryDarkHighContrast = Color(0xFFFFFAF7)
|
||||
val onSecondaryDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryContainerDarkHighContrast = Color(0xFFF1C38B)
|
||||
val onSecondaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryDarkHighContrast = Color(0xFFFCFAFF)
|
||||
val onTertiaryDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryContainerDarkHighContrast = Color(0xFFB6CAFF)
|
||||
val onTertiaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val errorDarkHighContrast = Color(0xFFFFF9F9)
|
||||
val onErrorDarkHighContrast = Color(0xFF000000)
|
||||
val errorContainerDarkHighContrast = Color(0xFFFFBAB1)
|
||||
val onErrorContainerDarkHighContrast = Color(0xFF000000)
|
||||
val backgroundDarkHighContrast = Color(0xFF19120B)
|
||||
val onBackgroundDarkHighContrast = Color(0xFFEFE0D3)
|
||||
val surfaceDarkHighContrast = Color(0xFF19120B)
|
||||
val onSurfaceDarkHighContrast = Color(0xFFFFFFFF)
|
||||
val surfaceVariantDarkHighContrast = Color(0xFF524434)
|
||||
val onSurfaceVariantDarkHighContrast = Color(0xFFFFFAF7)
|
||||
val outlineDarkHighContrast = Color(0xFFDCC7B2)
|
||||
val outlineVariantDarkHighContrast = Color(0xFFDCC7B2)
|
||||
val scrimDarkHighContrast = Color(0xFF000000)
|
||||
val inverseSurfaceDarkHighContrast = Color(0xFFEFE0D3)
|
||||
val inverseOnSurfaceDarkHighContrast = Color(0xFF000000)
|
||||
val inversePrimaryDarkHighContrast = Color(0xFF3E2500)
|
||||
val surfaceDimDarkHighContrast = Color(0xFF19120B)
|
||||
val surfaceBrightDarkHighContrast = Color(0xFF40382E)
|
||||
val surfaceContainerLowestDarkHighContrast = Color(0xFF130D06)
|
||||
val surfaceContainerLowDarkHighContrast = Color(0xFF211A12)
|
||||
val surfaceContainerDarkHighContrast = Color(0xFF261E16)
|
||||
val surfaceContainerHighDarkHighContrast = Color(0xFF302920)
|
||||
val surfaceContainerHighestDarkHighContrast = Color(0xFF3C332A)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
281
app/src/main/java/rs/ruffle/ui/theme/Theme.kt
Normal file
@ -0,0 +1,281 @@
|
||||
package rs.ruffle.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
const val stronglyDeemphasizedAlpha = 0.6f
|
||||
const val slightlyDeemphasizedAlpha = 0.87f
|
||||
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = primaryLight,
|
||||
onPrimary = onPrimaryLight,
|
||||
primaryContainer = primaryContainerLight,
|
||||
onPrimaryContainer = onPrimaryContainerLight,
|
||||
secondary = secondaryLight,
|
||||
onSecondary = onSecondaryLight,
|
||||
secondaryContainer = secondaryContainerLight,
|
||||
onSecondaryContainer = onSecondaryContainerLight,
|
||||
tertiary = tertiaryLight,
|
||||
onTertiary = onTertiaryLight,
|
||||
tertiaryContainer = tertiaryContainerLight,
|
||||
onTertiaryContainer = onTertiaryContainerLight,
|
||||
error = errorLight,
|
||||
onError = onErrorLight,
|
||||
errorContainer = errorContainerLight,
|
||||
onErrorContainer = onErrorContainerLight,
|
||||
background = backgroundLight,
|
||||
onBackground = onBackgroundLight,
|
||||
surface = surfaceLight,
|
||||
onSurface = onSurfaceLight,
|
||||
surfaceVariant = surfaceVariantLight,
|
||||
onSurfaceVariant = onSurfaceVariantLight,
|
||||
outline = outlineLight,
|
||||
outlineVariant = outlineVariantLight,
|
||||
scrim = scrimLight,
|
||||
inverseSurface = inverseSurfaceLight,
|
||||
inverseOnSurface = inverseOnSurfaceLight,
|
||||
inversePrimary = inversePrimaryLight,
|
||||
surfaceDim = surfaceDimLight,
|
||||
surfaceBright = surfaceBrightLight,
|
||||
surfaceContainerLowest = surfaceContainerLowestLight,
|
||||
surfaceContainerLow = surfaceContainerLowLight,
|
||||
surfaceContainer = surfaceContainerLight,
|
||||
surfaceContainerHigh = surfaceContainerHighLight,
|
||||
surfaceContainerHighest = surfaceContainerHighestLight,
|
||||
)
|
||||
|
||||
private val darkScheme = darkColorScheme(
|
||||
primary = primaryDark,
|
||||
onPrimary = onPrimaryDark,
|
||||
primaryContainer = primaryContainerDark,
|
||||
onPrimaryContainer = onPrimaryContainerDark,
|
||||
secondary = secondaryDark,
|
||||
onSecondary = onSecondaryDark,
|
||||
secondaryContainer = secondaryContainerDark,
|
||||
onSecondaryContainer = onSecondaryContainerDark,
|
||||
tertiary = tertiaryDark,
|
||||
onTertiary = onTertiaryDark,
|
||||
tertiaryContainer = tertiaryContainerDark,
|
||||
onTertiaryContainer = onTertiaryContainerDark,
|
||||
error = errorDark,
|
||||
onError = onErrorDark,
|
||||
errorContainer = errorContainerDark,
|
||||
onErrorContainer = onErrorContainerDark,
|
||||
background = backgroundDark,
|
||||
onBackground = onBackgroundDark,
|
||||
surface = surfaceDark,
|
||||
onSurface = onSurfaceDark,
|
||||
surfaceVariant = surfaceVariantDark,
|
||||
onSurfaceVariant = onSurfaceVariantDark,
|
||||
outline = outlineDark,
|
||||
outlineVariant = outlineVariantDark,
|
||||
scrim = scrimDark,
|
||||
inverseSurface = inverseSurfaceDark,
|
||||
inverseOnSurface = inverseOnSurfaceDark,
|
||||
inversePrimary = inversePrimaryDark,
|
||||
surfaceDim = surfaceDimDark,
|
||||
surfaceBright = surfaceBrightDark,
|
||||
surfaceContainerLowest = surfaceContainerLowestDark,
|
||||
surfaceContainerLow = surfaceContainerLowDark,
|
||||
surfaceContainer = surfaceContainerDark,
|
||||
surfaceContainerHigh = surfaceContainerHighDark,
|
||||
surfaceContainerHighest = surfaceContainerHighestDark,
|
||||
)
|
||||
|
||||
private val mediumContrastLightColorScheme = lightColorScheme(
|
||||
primary = primaryLightMediumContrast,
|
||||
onPrimary = onPrimaryLightMediumContrast,
|
||||
primaryContainer = primaryContainerLightMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightMediumContrast,
|
||||
secondary = secondaryLightMediumContrast,
|
||||
onSecondary = onSecondaryLightMediumContrast,
|
||||
secondaryContainer = secondaryContainerLightMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightMediumContrast,
|
||||
tertiary = tertiaryLightMediumContrast,
|
||||
onTertiary = onTertiaryLightMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerLightMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightMediumContrast,
|
||||
error = errorLightMediumContrast,
|
||||
onError = onErrorLightMediumContrast,
|
||||
errorContainer = errorContainerLightMediumContrast,
|
||||
onErrorContainer = onErrorContainerLightMediumContrast,
|
||||
background = backgroundLightMediumContrast,
|
||||
onBackground = onBackgroundLightMediumContrast,
|
||||
surface = surfaceLightMediumContrast,
|
||||
onSurface = onSurfaceLightMediumContrast,
|
||||
surfaceVariant = surfaceVariantLightMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightMediumContrast,
|
||||
outline = outlineLightMediumContrast,
|
||||
outlineVariant = outlineVariantLightMediumContrast,
|
||||
scrim = scrimLightMediumContrast,
|
||||
inverseSurface = inverseSurfaceLightMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightMediumContrast,
|
||||
inversePrimary = inversePrimaryLightMediumContrast,
|
||||
surfaceDim = surfaceDimLightMediumContrast,
|
||||
surfaceBright = surfaceBrightLightMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightMediumContrast,
|
||||
surfaceContainer = surfaceContainerLightMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
|
||||
)
|
||||
|
||||
private val highContrastLightColorScheme = lightColorScheme(
|
||||
primary = primaryLightHighContrast,
|
||||
onPrimary = onPrimaryLightHighContrast,
|
||||
primaryContainer = primaryContainerLightHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightHighContrast,
|
||||
secondary = secondaryLightHighContrast,
|
||||
onSecondary = onSecondaryLightHighContrast,
|
||||
secondaryContainer = secondaryContainerLightHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightHighContrast,
|
||||
tertiary = tertiaryLightHighContrast,
|
||||
onTertiary = onTertiaryLightHighContrast,
|
||||
tertiaryContainer = tertiaryContainerLightHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightHighContrast,
|
||||
error = errorLightHighContrast,
|
||||
onError = onErrorLightHighContrast,
|
||||
errorContainer = errorContainerLightHighContrast,
|
||||
onErrorContainer = onErrorContainerLightHighContrast,
|
||||
background = backgroundLightHighContrast,
|
||||
onBackground = onBackgroundLightHighContrast,
|
||||
surface = surfaceLightHighContrast,
|
||||
onSurface = onSurfaceLightHighContrast,
|
||||
surfaceVariant = surfaceVariantLightHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightHighContrast,
|
||||
outline = outlineLightHighContrast,
|
||||
outlineVariant = outlineVariantLightHighContrast,
|
||||
scrim = scrimLightHighContrast,
|
||||
inverseSurface = inverseSurfaceLightHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightHighContrast,
|
||||
inversePrimary = inversePrimaryLightHighContrast,
|
||||
surfaceDim = surfaceDimLightHighContrast,
|
||||
surfaceBright = surfaceBrightLightHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightHighContrast,
|
||||
surfaceContainer = surfaceContainerLightHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
|
||||
)
|
||||
|
||||
private val mediumContrastDarkColorScheme = darkColorScheme(
|
||||
primary = primaryDarkMediumContrast,
|
||||
onPrimary = onPrimaryDarkMediumContrast,
|
||||
primaryContainer = primaryContainerDarkMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
|
||||
secondary = secondaryDarkMediumContrast,
|
||||
onSecondary = onSecondaryDarkMediumContrast,
|
||||
secondaryContainer = secondaryContainerDarkMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
|
||||
tertiary = tertiaryDarkMediumContrast,
|
||||
onTertiary = onTertiaryDarkMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
|
||||
error = errorDarkMediumContrast,
|
||||
onError = onErrorDarkMediumContrast,
|
||||
errorContainer = errorContainerDarkMediumContrast,
|
||||
onErrorContainer = onErrorContainerDarkMediumContrast,
|
||||
background = backgroundDarkMediumContrast,
|
||||
onBackground = onBackgroundDarkMediumContrast,
|
||||
surface = surfaceDarkMediumContrast,
|
||||
onSurface = onSurfaceDarkMediumContrast,
|
||||
surfaceVariant = surfaceVariantDarkMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
|
||||
outline = outlineDarkMediumContrast,
|
||||
outlineVariant = outlineVariantDarkMediumContrast,
|
||||
scrim = scrimDarkMediumContrast,
|
||||
inverseSurface = inverseSurfaceDarkMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
|
||||
inversePrimary = inversePrimaryDarkMediumContrast,
|
||||
surfaceDim = surfaceDimDarkMediumContrast,
|
||||
surfaceBright = surfaceBrightDarkMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
|
||||
surfaceContainer = surfaceContainerDarkMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
|
||||
)
|
||||
|
||||
private val highContrastDarkColorScheme = darkColorScheme(
|
||||
primary = primaryDarkHighContrast,
|
||||
onPrimary = onPrimaryDarkHighContrast,
|
||||
primaryContainer = primaryContainerDarkHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkHighContrast,
|
||||
secondary = secondaryDarkHighContrast,
|
||||
onSecondary = onSecondaryDarkHighContrast,
|
||||
secondaryContainer = secondaryContainerDarkHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkHighContrast,
|
||||
tertiary = tertiaryDarkHighContrast,
|
||||
onTertiary = onTertiaryDarkHighContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkHighContrast,
|
||||
error = errorDarkHighContrast,
|
||||
onError = onErrorDarkHighContrast,
|
||||
errorContainer = errorContainerDarkHighContrast,
|
||||
onErrorContainer = onErrorContainerDarkHighContrast,
|
||||
background = backgroundDarkHighContrast,
|
||||
onBackground = onBackgroundDarkHighContrast,
|
||||
surface = surfaceDarkHighContrast,
|
||||
onSurface = onSurfaceDarkHighContrast,
|
||||
surfaceVariant = surfaceVariantDarkHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkHighContrast,
|
||||
outline = outlineDarkHighContrast,
|
||||
outlineVariant = outlineVariantDarkHighContrast,
|
||||
scrim = scrimDarkHighContrast,
|
||||
inverseSurface = inverseSurfaceDarkHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkHighContrast,
|
||||
inversePrimary = inversePrimaryDarkHighContrast,
|
||||
surfaceDim = surfaceDimDarkHighContrast,
|
||||
surfaceBright = surfaceBrightDarkHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkHighContrast,
|
||||
surfaceContainer = surfaceContainerDarkHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
data class ColorFamily(
|
||||
val color: Color,
|
||||
val onColor: Color,
|
||||
val colorContainer: Color,
|
||||
val onColorContainer: Color
|
||||
)
|
||||
|
||||
val unspecified_scheme = ColorFamily(
|
||||
Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun RuffleTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = false,
|
||||
content: @Composable() () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
34
app/src/main/java/rs/ruffle/ui/theme/Type.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package rs.ruffle.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
12
app/src/main/res/drawable/ic_logo_dark.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="328dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="328"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M37.85,61.25q-0.4,0 -0.8,0.05 -0.5,-0.25 -0.9,-0.5 -0.3,-0.1 -0.55,-0.3l-0.6,-0.6 -4.25,-6.45 -1.5,11.25h3.45q0.75,-0.1 1.5,-0.35 0.85,-0.25 1.65,-0.75 0.55,-0.35 1.05,-0.8 0.5,-0.45 0.95,-1 0.5,-0.5 0.75,-1.2 -0.05,0.05 -0.15,0.1 -0.1,0.15 -0.25,0.25l-0.1,0.2q-0.15,0.05 -0.25,0.1m58.3,0q-0.9,0 -1.45,-0.7 -0.3,-0.3 -0.55,-0.6l-0.05,-0.05v-0.05l-4.25,-6.4 -1.5,11.25h3.45q0.75,-0.1 1.5,-0.35 0.85,-0.25 1.6,-0.75 0.75,-0.5 1.4,-1.1 0.45,-0.35 0.75,-0.85 0.35,-0.5 0.65,-1.05l-0.45,0.55q-0.5,0.15 -1.1,0.1m19.7,3.25h3.45q0.75,-0.1 1.5,-0.35 0.25,-0.05 0.45,-0.15 0.35,-0.15 0.65,-0.3l0.5,-0.3q0.25,-0.15 0.5,-0.35 0.45,-0.35 0.9,-0.75 0.45,-0.35 0.75,-0.85l0.1,-0.1q0.1,-0.2 0.2,-0.35 0.2,-0.3 0.35,-0.6l-0.3,0.4 -0.15,0.15q-0.5,0.15 -1.1,0.1 -0.25,0 -0.4,-0.05 -0.5,-0.15 -0.8,-0.4 -0.15,-0.1 -0.25,-0.25 -0.3,-0.3 -0.55,-0.6l-0.05,-0.05v-0.05l-4.25,-6.4 -1.5,11.25m52.6,0.2q-3.1,-0.25 -5.7,-0.75 -5.6,-1.05 -8.9,-3.1 -0.75,-0.5 -1.4,-1 -3.15,-2.55 -3.5,-6.4l-1.5,11.25h21m33.4,-5.65q-2.3,-2.35 -2.6,-5.6l-1.5,11.25h21q-11.25,-0.95 -16,-4.85 -0.5,-0.4 -0.9,-0.8m55.15,2.5q-2.55,-0.25 -3.25,-1.8l-4.2,-6.3 -1.5,11.25h3.45q0.6,-0.1 1.2,-0.3 0.4,-0.1 0.75,-0.2 0.35,-0.15 0.65,-0.3 0.7,-0.35 1.35,-0.8 0.75,-0.55 1.3,-1.25 0.1,-0.15 0.25,-0.3m23.95,-8.05l-1.5,11.2h21q-0.3,-0.05 -0.6,-0.05 -10.8,-1 -15.4,-4.8 -3.15,-2.55 -3.5,-6.35z"
|
||||
android:fillColor="#966214"/>
|
||||
<path
|
||||
android:pathData="M40.65,47.25H51.8L49.75,61.7h16.5l2.3,-16.25h-0.05l0.8,-5.7q0.4,-2.45 -1,-4.2 -0.35,-0.4 -0.75,-0.8 -0.25,-0.25 -0.55,-0.5 -0.2,-0.2 -0.45,-0.35 -1.95,-1.4 -4.5,-1.4H34.3q-1.35,0 -2.6,0.45 -1.65,0.55 -3.15,1.8Q25.8,37 25.3,40l-1.65,12h0.05v0.3l5.85,1.15h-9.5q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.5,0.2 -0.95,0.45 -0.5,0.3 -0.95,0.7 -0.45,0.35 -0.85,0.8 -0.35,0.4 -0.65,0.85 -0.3,0.45 -0.5,0.9 -0.15,0.45 -0.3,0.95L8,100.2h16.25l5,-35.5 1.5,-11.25L35,59.9l0.6,0.6q0.25,0.2 0.55,0.3 0.4,0.25 0.9,0.5 0.4,-0.05 0.8,-0.05 0.1,-0.05 0.25,-0.1l0.1,-0.2q0.15,-0.1 0.25,-0.25 0.1,-0.05 0.15,-0.1l0.3,-1.05 1.75,-12.3m86.8,-2h-0.05l0.15,-1.25h-0.05l1.65,-11.7H112.9l-2.65,19.5h0.05v0.2l-0.05,0.1h0.05l5.8,1.15h-9.45q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.15,0.05 -0.3,0.15 -0.3,0.1 -0.55,0.25 -0.05,0 -0.1,0.05 -0.5,0.3 -1,0.65 -0.4,0.35 -0.7,0.7 -0.55,0.7 -0.95,1.45 -0.35,0.65 -0.55,1.4 -0.15,0.7 -0.25,1.4v0.05q-0.15,1.05 -0.35,2.05l-1.2,8.75v0.1l-2.1,14.7H85.35L87.6,69.9h0.05l0.7,-5.2 1.5,-11.25 4.25,6.4v0.05l0.05,0.05q0.25,0.3 0.55,0.6 0.55,0.7 1.45,0.7 0.6,0.05 1.1,-0.1l0.45,-0.55 0.3,-1.05 1.3,-9.05h-0.05l0.7,-5.05h-0.05l0.15,-1.25H100l1.65,-11.7H85.4L82.75,52h0.05l-0.05,0.3 5.85,1.15h-9.45q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.5,0.2 -0.95,0.45 -0.5,0.3 -1,0.65 -0.4,0.4 -0.8,0.85 -0.25,0.3 -0.55,0.65 -0.05,0.1 -0.15,0.2 -0.25,0.45 -0.4,0.9 -0.2,0.45 -0.3,0.95 -0.1,0.65 -0.2,1.25 -0.2,1.15 -0.4,2.25l-4.3,30.6q-0.25,3 1.75,5.25 1.6,1.8 4,2.15 0.6,0.1 1.25,0.1h27.35q3.25,0 6,-2.25 0.35,-0.35 0.7,-0.55l0.3,-0.2q2,-2 2.25,-4.5l1.65,-11.6q0.05,-0.05 0.1,-0.05l1.65,-11.35h0.05l0.7,-5.2 1.5,-11.25 4.25,6.4v0.05l0.05,0.05q0.25,0.3 0.55,0.6 0.1,0.15 0.25,0.25 0.3,0.25 0.8,0.4 0.15,0.05 0.4,0.05 0.6,0.05 1.1,-0.1l0.15,-0.15 0.3,-0.4 0.3,-1.05 1.3,-9.05h-0.05l0.7,-5.05m61.8,-35Q187,8 183.75,8H156q-3,0 -5.75,2.25 -1.3,0.95 -2.05,2.1 -0.45,0.6 -0.7,1.2 -0.2,0.5 -0.35,1 -0.1,0.45 -0.15,0.95l-4.15,29.95h-0.05l-0.7,5.2h-0.05l-0.2,1.35h0.05l-0.05,0.3 5.85,1.15h-9.45q-2.1,0.05 -3.95,1.6 -1.9,1.55 -2.25,3.55l-0.5,3.5h-0.05l-5.3,38.1h16.25l5,-35.5 1.5,-11.25q0.35,3.85 3.5,6.4 0.65,0.5 1.4,1 3.3,2.05 8.9,3.1 2.6,0.5 5.7,0.75l1.75,-11.25H158l0.4,-2.95h-0.05l0.7,-5.05H159q0.1,-0.9 0.3,-1.9 0.1,-0.75 0.2,-1.6 0.85,-5.9 2.15,-14.9 0,-0.15 0.05,-0.25l0.1,-0.9q0.2,-1.55 0.45,-3.15h11.25l-3.1,20.8h16.5L191,15.5q0.15,-1.7 -0.4,-3.15 -0.5,-1.1 -1.35,-2.1m50.3,0Q237.3,8 234.05,8H206.3q-2.3,0 -4.45,1.35 -0.65,0.35 -1.3,0.9 -1.3,0.95 -2.05,2.1 -0.45,0.6 -0.7,1.2 -0.4,0.9 -0.5,1.95l-4.15,29.95h-0.05l-0.7,5.2h-0.05l-0.2,1.35h0.05l-0.05,0.3 5.85,1.15h-9.45q-2.1,0.05 -3.95,1.6 -1.9,1.55 -2.25,3.55l-0.5,3.5h-0.05l-1.2,8.75v0.1l-4.1,29.25h16.25l5,-35.5 1.5,-11.25q0.3,3.25 2.6,5.6 0.4,0.4 0.9,0.8 4.75,3.9 16,4.85l1.75,-11.25h-12.2l0.4,-2.95h-0.05l0.7,-5.05h-0.05q0.15,-0.9 0.3,-1.9 0.1,-0.75 0.25,-1.6 0.15,-1.25 0.35,-2.65v-0.05q0.95,-6.7 2.35,-16.5h11.25l-3.1,20.8h16.5l4.1,-28.05q0.15,-1.7 -0.4,-3.15 -0.5,-1.1 -1.35,-2.1M259.5,45.5v-0.05L264.85,8H248.6l-6.15,44.3 5.85,1.15h-9.45q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.5,0.2 -0.95,0.45 -0.5,0.3 -1,0.65 -0.4,0.4 -0.8,0.85 -0.35,0.4 -0.7,0.85 -0.25,0.45 -0.45,0.9 -0.15,0.45 -0.3,0.95l-5.85,41.6h16.25l5,-35.5 1.5,-11.25 4.2,6.3q0.7,1.55 3.25,1.8l0.05,-0.1q0.25,-0.4 0.35,-0.85l0.3,-1.05 1.8,-14.05m25.6,-15.75q-0.65,0 -1.3,0.1 -2.5,0.35 -4.7,2.15 -2.75,2.25 -3.25,5.25l-1.95,14.7V52l-0.05,0.3 5.85,1.15h-9.45q-1.9,0.05 -3.6,1.35 -0.2,0.1 -0.35,0.25 -1.9,1.55 -2.25,3.55l-4.85,34.1q-0.25,3 1.75,5.25 1.25,1.4 3,1.95 1.05,0.3 2.25,0.3h27.75q3.25,0 6,-2.25 2.75,-2 3.25,-5l2.75,-18.5h-16.5l-1.75,11h-11.25l2.1,-14.75h0.05l0.85,-6 1.5,-11.2q0.35,3.8 3.5,6.35 4.6,3.8 15.4,4.8 0.3,0 0.6,0.05h15.75l2.75,-19.25h-0.05l1.15,-8.2q0.5,-3 -1.75,-5.25 -1.25,-1.25 -3,-1.75 -1,-0.5 -2.25,-0.5H285.1m5.95,15.7h-0.1l0.15,-0.95h11.45l-1.25,8.95H290l0.4,-2.95h-0.05l0.7,-5.05z"
|
||||
android:fillColor="#FFAD33"/>
|
||||
</vector>
|
@ -5,8 +5,8 @@
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M37.85,61.25q-0.4,0 -0.8,0.05 -0.5,-0.25 -0.9,-0.5 -0.3,-0.1 -0.55,-0.3l-0.6,-0.6 -4.25,-6.45 -1.5,11.25h3.45q0.75,-0.1 1.5,-0.35 0.85,-0.25 1.65,-0.75 0.55,-0.35 1.05,-0.8 0.5,-0.45 0.95,-1 0.5,-0.5 0.75,-1.2 -0.05,0.05 -0.15,0.1 -0.1,0.15 -0.25,0.25l-0.1,0.2q-0.15,0.05 -0.25,0.1m58.3,0q-0.9,0 -1.45,-0.7 -0.3,-0.3 -0.55,-0.6l-0.05,-0.05v-0.05l-4.25,-6.4 -1.5,11.25h3.45q0.75,-0.1 1.5,-0.35 0.85,-0.25 1.6,-0.75 0.75,-0.5 1.4,-1.1 0.45,-0.35 0.75,-0.85 0.35,-0.5 0.65,-1.05l-0.45,0.55q-0.5,0.15 -1.1,0.1m19.7,3.25h3.45q0.75,-0.1 1.5,-0.35 0.25,-0.05 0.45,-0.15 0.35,-0.15 0.65,-0.3l0.5,-0.3q0.25,-0.15 0.5,-0.35 0.45,-0.35 0.9,-0.75 0.45,-0.35 0.75,-0.85l0.1,-0.1q0.1,-0.2 0.2,-0.35 0.2,-0.3 0.35,-0.6l-0.3,0.4 -0.15,0.15q-0.5,0.15 -1.1,0.1 -0.25,0 -0.4,-0.05 -0.5,-0.15 -0.8,-0.4 -0.15,-0.1 -0.25,-0.25 -0.3,-0.3 -0.55,-0.6l-0.05,-0.05v-0.05l-4.25,-6.4 -1.5,11.25m52.6,0.2q-3.1,-0.25 -5.7,-0.75 -5.6,-1.05 -8.9,-3.1 -0.75,-0.5 -1.4,-1 -3.15,-2.55 -3.5,-6.4l-1.5,11.25h21m33.4,-5.65q-2.3,-2.35 -2.6,-5.6l-1.5,11.25h21q-11.25,-0.95 -16,-4.85 -0.5,-0.4 -0.9,-0.8m55.15,2.5q-2.55,-0.25 -3.25,-1.8l-4.2,-6.3 -1.5,11.25h3.45q0.6,-0.1 1.2,-0.3 0.4,-0.1 0.75,-0.2 0.35,-0.15 0.65,-0.3 0.7,-0.35 1.35,-0.8 0.75,-0.55 1.3,-1.25 0.1,-0.15 0.25,-0.3m23.95,-8.05l-1.5,11.2h21q-0.3,-0.05 -0.6,-0.05 -10.8,-1 -15.4,-4.8 -3.15,-2.55 -3.5,-6.35z"
|
||||
android:fillColor="#966214"/>
|
||||
android:fillColor="#2c4980"/>
|
||||
<path
|
||||
android:pathData="M40.65,47.25H51.8L49.75,61.7h16.5l2.3,-16.25h-0.05l0.8,-5.7q0.4,-2.45 -1,-4.2 -0.35,-0.4 -0.75,-0.8 -0.25,-0.25 -0.55,-0.5 -0.2,-0.2 -0.45,-0.35 -1.95,-1.4 -4.5,-1.4H34.3q-1.35,0 -2.6,0.45 -1.65,0.55 -3.15,1.8Q25.8,37 25.3,40l-1.65,12h0.05v0.3l5.85,1.15h-9.5q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.5,0.2 -0.95,0.45 -0.5,0.3 -0.95,0.7 -0.45,0.35 -0.85,0.8 -0.35,0.4 -0.65,0.85 -0.3,0.45 -0.5,0.9 -0.15,0.45 -0.3,0.95L8,100.2h16.25l5,-35.5 1.5,-11.25L35,59.9l0.6,0.6q0.25,0.2 0.55,0.3 0.4,0.25 0.9,0.5 0.4,-0.05 0.8,-0.05 0.1,-0.05 0.25,-0.1l0.1,-0.2q0.15,-0.1 0.25,-0.25 0.1,-0.05 0.15,-0.1l0.3,-1.05 1.75,-12.3m86.8,-2h-0.05l0.15,-1.25h-0.05l1.65,-11.7H112.9l-2.65,19.5h0.05v0.2l-0.05,0.1h0.05l5.8,1.15h-9.45q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.15,0.05 -0.3,0.15 -0.3,0.1 -0.55,0.25 -0.05,0 -0.1,0.05 -0.5,0.3 -1,0.65 -0.4,0.35 -0.7,0.7 -0.55,0.7 -0.95,1.45 -0.35,0.65 -0.55,1.4 -0.15,0.7 -0.25,1.4v0.05q-0.15,1.05 -0.35,2.05l-1.2,8.75v0.1l-2.1,14.7H85.35L87.6,69.9h0.05l0.7,-5.2 1.5,-11.25 4.25,6.4v0.05l0.05,0.05q0.25,0.3 0.55,0.6 0.55,0.7 1.45,0.7 0.6,0.05 1.1,-0.1l0.45,-0.55 0.3,-1.05 1.3,-9.05h-0.05l0.7,-5.05h-0.05l0.15,-1.25H100l1.65,-11.7H85.4L82.75,52h0.05l-0.05,0.3 5.85,1.15h-9.45q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.5,0.2 -0.95,0.45 -0.5,0.3 -1,0.65 -0.4,0.4 -0.8,0.85 -0.25,0.3 -0.55,0.65 -0.05,0.1 -0.15,0.2 -0.25,0.45 -0.4,0.9 -0.2,0.45 -0.3,0.95 -0.1,0.65 -0.2,1.25 -0.2,1.15 -0.4,2.25l-4.3,30.6q-0.25,3 1.75,5.25 1.6,1.8 4,2.15 0.6,0.1 1.25,0.1h27.35q3.25,0 6,-2.25 0.35,-0.35 0.7,-0.55l0.3,-0.2q2,-2 2.25,-4.5l1.65,-11.6q0.05,-0.05 0.1,-0.05l1.65,-11.35h0.05l0.7,-5.2 1.5,-11.25 4.25,6.4v0.05l0.05,0.05q0.25,0.3 0.55,0.6 0.1,0.15 0.25,0.25 0.3,0.25 0.8,0.4 0.15,0.05 0.4,0.05 0.6,0.05 1.1,-0.1l0.15,-0.15 0.3,-0.4 0.3,-1.05 1.3,-9.05h-0.05l0.7,-5.05m61.8,-35Q187,8 183.75,8H156q-3,0 -5.75,2.25 -1.3,0.95 -2.05,2.1 -0.45,0.6 -0.7,1.2 -0.2,0.5 -0.35,1 -0.1,0.45 -0.15,0.95l-4.15,29.95h-0.05l-0.7,5.2h-0.05l-0.2,1.35h0.05l-0.05,0.3 5.85,1.15h-9.45q-2.1,0.05 -3.95,1.6 -1.9,1.55 -2.25,3.55l-0.5,3.5h-0.05l-5.3,38.1h16.25l5,-35.5 1.5,-11.25q0.35,3.85 3.5,6.4 0.65,0.5 1.4,1 3.3,2.05 8.9,3.1 2.6,0.5 5.7,0.75l1.75,-11.25H158l0.4,-2.95h-0.05l0.7,-5.05H159q0.1,-0.9 0.3,-1.9 0.1,-0.75 0.2,-1.6 0.85,-5.9 2.15,-14.9 0,-0.15 0.05,-0.25l0.1,-0.9q0.2,-1.55 0.45,-3.15h11.25l-3.1,20.8h16.5L191,15.5q0.15,-1.7 -0.4,-3.15 -0.5,-1.1 -1.35,-2.1m50.3,0Q237.3,8 234.05,8H206.3q-2.3,0 -4.45,1.35 -0.65,0.35 -1.3,0.9 -1.3,0.95 -2.05,2.1 -0.45,0.6 -0.7,1.2 -0.4,0.9 -0.5,1.95l-4.15,29.95h-0.05l-0.7,5.2h-0.05l-0.2,1.35h0.05l-0.05,0.3 5.85,1.15h-9.45q-2.1,0.05 -3.95,1.6 -1.9,1.55 -2.25,3.55l-0.5,3.5h-0.05l-1.2,8.75v0.1l-4.1,29.25h16.25l5,-35.5 1.5,-11.25q0.3,3.25 2.6,5.6 0.4,0.4 0.9,0.8 4.75,3.9 16,4.85l1.75,-11.25h-12.2l0.4,-2.95h-0.05l0.7,-5.05h-0.05q0.15,-0.9 0.3,-1.9 0.1,-0.75 0.25,-1.6 0.15,-1.25 0.35,-2.65v-0.05q0.95,-6.7 2.35,-16.5h11.25l-3.1,20.8h16.5l4.1,-28.05q0.15,-1.7 -0.4,-3.15 -0.5,-1.1 -1.35,-2.1M259.5,45.5v-0.05L264.85,8H248.6l-6.15,44.3 5.85,1.15h-9.45q-0.5,0.05 -1,0.15 -0.5,0.15 -1,0.35 -0.5,0.2 -0.95,0.45 -0.5,0.3 -1,0.65 -0.4,0.4 -0.8,0.85 -0.35,0.4 -0.7,0.85 -0.25,0.45 -0.45,0.9 -0.15,0.45 -0.3,0.95l-5.85,41.6h16.25l5,-35.5 1.5,-11.25 4.2,6.3q0.7,1.55 3.25,1.8l0.05,-0.1q0.25,-0.4 0.35,-0.85l0.3,-1.05 1.8,-14.05m25.6,-15.75q-0.65,0 -1.3,0.1 -2.5,0.35 -4.7,2.15 -2.75,2.25 -3.25,5.25l-1.95,14.7V52l-0.05,0.3 5.85,1.15h-9.45q-1.9,0.05 -3.6,1.35 -0.2,0.1 -0.35,0.25 -1.9,1.55 -2.25,3.55l-4.85,34.1q-0.25,3 1.75,5.25 1.25,1.4 3,1.95 1.05,0.3 2.25,0.3h27.75q3.25,0 6,-2.25 2.75,-2 3.25,-5l2.75,-18.5h-16.5l-1.75,11h-11.25l2.1,-14.75h0.05l0.85,-6 1.5,-11.2q0.35,3.8 3.5,6.35 4.6,3.8 15.4,4.8 0.3,0 0.6,0.05h15.75l2.75,-19.25h-0.05l1.15,-8.2q0.5,-3 -1.75,-5.25 -1.25,-1.25 -3,-1.75 -1,-0.5 -2.25,-0.5H285.1m5.95,15.7h-0.1l0.15,-0.95h11.45l-1.25,8.95H290l0.4,-2.95h-0.05l0.7,-5.05z"
|
||||
android:fillColor="#FFAD33"/>
|
||||
android:fillColor="#37528c"/>
|
||||
</vector>
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFAD33</color>
|
||||
<color name="purple_500">#FFAD33</color>
|
||||
<color name="purple_700">#FFAD33</color>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
9
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<resources>
|
||||
<string name="app_name">Ruffle</string>
|
||||
<string name="logo_description">Ruffle Logo</string>
|
||||
<string name="or">or</string>
|
||||
<string name="open_url">Open URL</string>
|
||||
<string name="select_a_swf">Select a SWF</string>
|
||||
<string name="url">URL</string>
|
||||
<string name="work_in_progress_warning">This app is a work in progress. Some functionality may not be implemented yet!</string>
|
||||
</resources>
|
5
app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Ruffle" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
</style>
|
||||
</resources>
|
13
app/src/main/res/xml/backup_rules.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
19
app/src/main/res/xml/data_extraction_rules.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
17
app/src/test/java/rs/ruffle/ExampleUnitTest.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package rs.ruffle
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
6
build.gradle.kts
Normal file
@ -0,0 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication) apply false
|
||||
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
|
||||
alias(libs.plugins.cargoNdkAndroid) apply false
|
||||
}
|
@ -8,15 +8,16 @@
|
||||
# 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
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app"s APK
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
android.nonFinalResIds=false
|
||||
android.nonTransitiveRClass=true
|
45
gradle/libs.versions.toml
Normal file
@ -0,0 +1,45 @@
|
||||
[versions]
|
||||
agp = "8.3.0"
|
||||
kotlin = "1.9.0"
|
||||
coreKtx = "1.12.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
espressoCore = "3.5.1"
|
||||
lifecycleRuntimeKtx = "2.7.0"
|
||||
activityCompose = "1.8.2"
|
||||
composeBom = "2024.02.02"
|
||||
lifecycleViewmodelCompose = "2.7.0"
|
||||
navigationRuntimeKtx = "2.7.7"
|
||||
navigationCompose = "2.7.7"
|
||||
gamesActivity = "2.0.2" # Needs to be in sync with android-activity crate
|
||||
constraintlayout = "2.1.4"
|
||||
appcompat = "1.6.1"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
|
||||
androidx-navigation-runtime-ktx = { group = "androidx.navigation", name = "navigation-runtime-ktx", version.ref = "navigationRuntimeKtx" }
|
||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
||||
androidx-games-activity = { group = "androidx.games", name = "games-activity", version.ref = "gamesActivity" }
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
|
||||
[plugins]
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
|
||||
cargoNdkAndroid = { id = "com.github.willir.rust.cargo-ndk-android", version = "0.3.4" }
|
||||
|
@ -1,6 +1,6 @@
|
||||
#Tue Jan 16 22:20:46 CET 2024
|
||||
#Tue Mar 12 12:21:42 CET 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
0
app/gradlew → gradlew
vendored
0
app/gradlew.bat → gradlew.bat
vendored
1
native/.gitignore
vendored
@ -1 +0,0 @@
|
||||
target
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -1,8 +1,14 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
@ -12,5 +18,7 @@ dependencyResolutionManagement {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "Ruffle"
|
||||
include ':ruffle'
|
||||
include(":app")
|
||||
|