From 56a420bb7a15a600b00eb482c9da8a7e17bb2e87 Mon Sep 17 00:00:00 2001
From: 2xlink <2xlink@web.de>
Date: Tue, 1 Sep 2020 13:02:35 +0200
Subject: [PATCH 1/9] Adds intent listener for nxtb.it links
---
app/src/main/AndroidManifest.xml | 10 ++++++++++
.../example/hochi/nextcompanion/RentActivity.java | 14 ++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5637d70..f35d609 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -30,6 +30,16 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java b/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
index 79c7fb4..a3bc50b 100644
--- a/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
+++ b/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
@@ -1,12 +1,16 @@
package com.example.hochi.nextcompanion;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
+import org.w3c.dom.Text;
+
public class RentActivity extends AppCompatActivity implements AsyncTaskCallbacks {
private RequestHandler rentRequestTask = null;
@Override
@@ -21,6 +25,16 @@ public class RentActivity extends AppCompatActivity implements AsyncTaskCallback
rentRequest();
}
});
+
+ Intent intent = getIntent();
+// String action = intent.getAction();
+ Uri data = intent.getData();
+
+ if (data != null) {
+ String bikeID = data.toString().substring(15);
+ ((TextView) findViewById(R.id.bike_id)).setText(bikeID);
+ rentRequest();
+ }
}
void rentRequest() {
From 5ab4301aca618f2a929549612a22ad5656d520ef Mon Sep 17 00:00:00 2001
From: 2xlink <2xlink@web.de>
Date: Wed, 2 Sep 2020 07:17:32 +0200
Subject: [PATCH 2/9] Adds app name to open with dialog
---
app/src/main/AndroidManifest.xml | 4 +---
.../java/com/example/hochi/nextcompanion/RentActivity.java | 2 +-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f35d609..f15290c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -30,15 +30,13 @@
-
+
-
-
Date: Wed, 21 Apr 2021 23:16:22 +0200
Subject: [PATCH 3/9] Removes comments
---
.../main/java/com/example/hochi/nextcompanion/RentActivity.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java b/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
index 801860f..4b1eb87 100644
--- a/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
+++ b/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
@@ -27,13 +27,11 @@ public class RentActivity extends AppCompatActivity implements AsyncTaskCallback
});
Intent intent = getIntent();
-// String action = intent.getAction();
Uri data = intent.getData();
if (data != null) {
String bikeID = data.toString().substring(15);
((TextView) findViewById(R.id.bike_id)).setText(bikeID);
-// rentRequest();
}
}
From 4ed7f5d54ac18b5d4be356dbf5442154ece72eef Mon Sep 17 00:00:00 2001
From: lgehr
Date: Fri, 1 Oct 2021 23:36:48 +0200
Subject: [PATCH 4/9] Bumps to version 0.1.7.2
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index c8c10d8..7cde814 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,8 +6,8 @@ android {
applicationId "com.example.hochi.nextcompanion"
minSdkVersion 15
targetSdkVersion 28
- versionCode 8
- versionName "0.1.7.1"
+ versionCode 9
+ versionName "0.1.7.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
From 0df2b02ad5544425248175bdfcfe25858958e094 Mon Sep 17 00:00:00 2001
From: loewenzahm
Date: Mon, 7 Feb 2022 22:24:29 +0100
Subject: [PATCH 5/9] [Bugfix] Fix logout bug
On a logout the login-key has not been removed from the shared preferences due to a typo ("loginkey" instead of "loginKey"). Now that the typo is fixed the login key is no longer kept stored after logouts which resolves the bug mentioned in issue #16
---
.../main/java/com/example/hochi/nextcompanion/MainActivity.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java b/app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java
index 5ec0d77..ee51d6e 100644
--- a/app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java
+++ b/app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java
@@ -81,7 +81,7 @@ public class MainActivity extends AppCompatActivity implements AsyncTaskCallback
if (id == R.id.action_logout) {
SharedPreferences sharedPref = getSharedPreferences("persistence", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
- editor.remove("loginkey");
+ editor.remove("loginKey");
editor.apply();
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
From f810a0f6454154d9e978dab80c33ded7c5a5ad60 Mon Sep 17 00:00:00 2001
From: lgehr
Date: Sun, 20 Mar 2022 23:33:18 +0100
Subject: [PATCH 6/9] Recommiting strings.xml files without old apikey
---
app/src/main/res/values-de/strings.xml | 30 ++++++++++++++++++++++++++
app/src/main/res/values/strings.xml | 29 +++++++++++++++++++++++++
2 files changed, 59 insertions(+)
create mode 100644 app/src/main/res/values-de/strings.xml
create mode 100644 app/src/main/res/values/strings.xml
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..4c558ea
--- /dev/null
+++ b/app/src/main/res/values-de/strings.xml
@@ -0,0 +1,30 @@
+
+
+
+ NextCompanion
+
+ Ausloggen
+ Anmelden
+ Mobiltelefonnummer
+ Pin
+ Anmelden
+ Anmelden
+ Telefonnummer ist inkorrekt
+ Pin oder Telefonnummer ist inkorrekt
+ Feld ist erforderlich
+ Rad ausleihen
+ Fahrrad-ID eingeben
+ Ausleihen
+ Nummer der Rückgabestation
+ Rückgabe
+ Fahrrad zurückgeben
+ Karte
+ Keine Räder geliehen…
+ Dieses Rad ein elektronisches Schloss. Zur Rückgabe bitte das Schloss schließen.
+
+
+ 3IaBlP9OZw14dvES
+
+ persistence
+ Ausleihen mit NextCompanion
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..736f6f6
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,29 @@
+
+
+ NextCompanion
+
+ Logout
+ Sign in
+ Phone number
+ Pin
+ Sign in
+ Sign in
+ This phone number is invalid
+ This pin or phone number is incorrect
+ This field is required
+ Rent Bike
+ Enter Bike UID
+ Rent
+ Enter the station UID where you leave the bike
+ Return
+ Return Bike
+ Map
+ No bikes here…
+ This is a bike with electric lock. Please just close the lock to return.
+
+
+ 3IaBlP9OZw14dvES
+
+ persistence
+ Rent with NextCompanion
+
From 3490cd6ed9d6a92ef8a8220b43f956f51ba4a309 Mon Sep 17 00:00:00 2001
From: Jonas Heinrich
Date: Tue, 10 Mar 2026 23:25:26 +0100
Subject: [PATCH 7/9] init
---
.gitignore | 5 +
Cargo.lock | 2068 +++++++++++++++++++++++++++++++++++++++++++++++++++
Cargo.toml | 11 +
flake.lock | 62 ++
flake.nix | 47 ++
result | 1 +
src/main.rs | 605 +++++++++++++++
7 files changed, 2799 insertions(+)
create mode 100644 Cargo.lock
create mode 100644 Cargo.toml
create mode 100644 flake.lock
create mode 100644 flake.nix
create mode 120000 result
create mode 100644 src/main.rs
diff --git a/.gitignore b/.gitignore
index 69cb356..5acaefe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,8 @@
/captures
.externalNativeBuild
.idea/*
+
+
+# Added by cargo
+
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..a76249b
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,2068 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bitflags"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
+
+[[package]]
+name = "bumpalo"
+version = "3.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
+
+[[package]]
+name = "bytes"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+
+[[package]]
+name = "cairo-rs"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b01fe135c0bd16afe262b6dea349bd5ea30e6de50708cec639aae7c5c14cc7e4"
+dependencies = [
+ "bitflags",
+ "cairo-sys-rs",
+ "glib",
+ "libc",
+]
+
+[[package]]
+name = "cairo-sys-rs"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06c28280c6b12055b5e39e4554271ae4e6630b27c0da9148c4cf6485fc6d245c"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-expr"
+version = "0.20.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "dirs"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "field-offset"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
+dependencies = [
+ "memoffset",
+ "rustc_version",
+]
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+
+[[package]]
+name = "futures-task"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+
+[[package]]
+name = "futures-util"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+dependencies = [
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "gdk-pixbuf"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "debb0d39e3cdd84626edfd54d6e4a6ba2da9a0ef2e796e691c4e9f8646fda00c"
+dependencies = [
+ "gdk-pixbuf-sys",
+ "gio",
+ "glib",
+ "libc",
+]
+
+[[package]]
+name = "gdk-pixbuf-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd95ad50b9a3d2551e25dd4f6892aff0b772fe5372d84514e9d0583af60a0ce7"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gdk4"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "756564212bbe4a4ce05d88ffbd2582581ac6003832d0d32822d0825cca84bfbf"
+dependencies = [
+ "cairo-rs",
+ "gdk-pixbuf",
+ "gdk4-sys",
+ "gio",
+ "glib",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gdk4-sys"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6d4e5b3ccf591826a4adcc83f5f57b4e59d1925cb4bf620b0d645f79498b034"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "system-deps",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gio"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5ff48bf600c68b476e61dc6b7c762f2f4eb91deef66583ba8bb815c30b5811a"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "gio-sys",
+ "glib",
+ "libc",
+ "pin-project-lite",
+ "smallvec",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "glib"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "gio-sys",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "memchr",
+ "smallvec",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17"
+dependencies = [
+ "heck",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "graphene-rs"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2730030ac9db663fd8bfe1e7093742c1cafb92db9c315c9417c29032341fe2f9"
+dependencies = [
+ "glib",
+ "graphene-sys",
+ "libc",
+]
+
+[[package]]
+name = "graphene-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915e32091ea9ad241e4b044af62b7351c2d68aeb24f489a0d7f37a0fc484fd93"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "pkg-config",
+ "system-deps",
+]
+
+[[package]]
+name = "gsk4"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e755de9d8c5896c5beaa028b89e1969d067f1b9bf1511384ede971f5983aa153"
+dependencies = [
+ "cairo-rs",
+ "gdk4",
+ "glib",
+ "graphene-rs",
+ "gsk4-sys",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gsk4-sys"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ce91472391146f482065f1041876d8f869057b195b95399414caa163d72f4f7"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk4-sys",
+ "glib-sys",
+ "gobject-sys",
+ "graphene-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "gtk4"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acb21d53cfc6f7bfaf43549731c43b67ca47d87348d81c8cfc4dcdd44828e1a4"
+dependencies = [
+ "cairo-rs",
+ "field-offset",
+ "futures-channel",
+ "gdk-pixbuf",
+ "gdk4",
+ "gio",
+ "glib",
+ "graphene-rs",
+ "gsk4",
+ "gtk4-macros",
+ "gtk4-sys",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gtk4-macros"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ccfb5a14a3d941244815d5f8101fa12d4577b59cc47245778d8d907b0003e42"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "gtk4-sys"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "842577fe5a1ee15d166cd3afe804ce0cab6173bc789ca32e21308834f20088dd"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk4-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "graphene-sys",
+ "gsk4-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "http"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "hyper"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "pin-utils",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "webpki-roots",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
+
+[[package]]
+name = "icu_properties"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
+
+[[package]]
+name = "icu_provider"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+
+[[package]]
+name = "iri-string"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
+
+[[package]]
+name = "js-sys"
+version = "0.3.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libadwaita"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb09e12bf8f73342b3315c839d0a7668cc0ccebd78490c49fec48bab15d5484b"
+dependencies = [
+ "gdk4",
+ "gio",
+ "glib",
+ "gtk4",
+ "libadwaita-sys",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "libadwaita-sys"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7f94227ba87eb596fecada2491f04e357d507324142f77bf76d9e6be4a3e31"
+dependencies = [
+ "gdk4-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "gtk4-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.183"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
+
+[[package]]
+name = "libredox"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "litemap"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[package]]
+name = "memchr"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mio"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "next-companion"
+version = "0.1.0"
+dependencies = [
+ "dirs",
+ "gtk4",
+ "libadwaita",
+ "reqwest",
+ "serde_json",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "pango"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52d1d85e2078077a065bb7fc072783d5bcd4e51b379f22d67107d0a16937eb69"
+dependencies = [
+ "gio",
+ "glib",
+ "libc",
+ "pango-sys",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f06627d36ed5ff303d2df65211fc2e52ba5b17bf18dd80ff3d9628d6e06cfd"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "potential_utf"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
+dependencies = [
+ "bytes",
+ "getrandom 0.3.4",
+ "lru-slab",
+ "rand",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.18",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
+dependencies = [
+ "getrandom 0.2.17",
+ "libredox",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.12.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
+dependencies = [
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "ryu"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+
+[[package]]
+name = "semver"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "socket2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "system-deps"
+version = "7.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c"
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl 2.0.18",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.9.12+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
+dependencies = [
+ "indexmap",
+ "serde_core",
+ "serde_spanned",
+ "toml_datetime 0.7.5+spec-1.1.0",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "1.0.0+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.25.4+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2"
+dependencies = [
+ "indexmap",
+ "toml_datetime 1.0.0+spec-1.1.0",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.9+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "toml_writer"
+version = "1.0.6+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
+
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "version-compare"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.2+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "winnow"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+
+[[package]]
+name = "writeable"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
+
+[[package]]
+name = "yoke"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+
+[[package]]
+name = "zerotrie"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..dbc3fc0
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "next-companion"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+gtk = { package = "gtk4", version = "0.10" }
+adw = { package = "libadwaita", version = "0.8", features = ["v1_6"] }
+reqwest = { version = "0.12", features = ["blocking", "rustls-tls"], default-features = false }
+serde_json = "1"
+dirs = "5"
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..09412e5
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,62 @@
+{
+ "nodes": {
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1772963539,
+ "narHash": "sha256-9jVDGZnvCckTGdYT53d/EfznygLskyLQXYwJLKMPsZs=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "9dcb002ca1690658be4a04645215baea8b95f31d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1744536153,
+ "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs",
+ "rust-overlay": "rust-overlay"
+ }
+ },
+ "rust-overlay": {
+ "inputs": {
+ "nixpkgs": "nixpkgs_2"
+ },
+ "locked": {
+ "lastModified": 1773115373,
+ "narHash": "sha256-bfK9FJFcQth6f3ydYggS5m0z2NRGF/PY6Y2XgZDJ6pg=",
+ "owner": "oxalica",
+ "repo": "rust-overlay",
+ "rev": "1924b4672a2b8e4aee6e6652ec2e59a8d3c5648e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "oxalica",
+ "repo": "rust-overlay",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..97f288f
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,47 @@
+{
+ description = "NextCompanion — a minimal GTK4 Nextbike client for Linux";
+
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ rust-overlay.url = "github:oxalica/rust-overlay";
+ };
+
+ outputs = { self, nixpkgs, rust-overlay, ... }:
+ let
+ system = "x86_64-linux";
+ overlays = [ (import rust-overlay) ];
+ pkgs = import nixpkgs { inherit system overlays; };
+
+ runtimeDeps = with pkgs; [
+ gtk4
+ libadwaita
+ glib
+ ];
+ buildDeps = with pkgs; [
+ pkg-config
+ rust-bin.stable.latest.default
+ ];
+ in
+ {
+ devShells.${system}.default = pkgs.mkShell {
+ buildInputs = buildDeps ++ runtimeDeps;
+ shellHook = ''
+ export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath runtimeDeps}:$LD_LIBRARY_PATH"
+ '';
+ };
+
+ packages.${system}.default = pkgs.rustPlatform.buildRustPackage {
+ pname = "next-companion";
+ version = "0.1.0";
+ src = ./.;
+ cargoLock.lockFile = ./Cargo.lock;
+ nativeBuildInputs = buildDeps;
+ buildInputs = runtimeDeps;
+ };
+
+ apps.${system}.default = {
+ type = "app";
+ program = "${self.packages.${system}.default}/bin/next-companion";
+ };
+ };
+}
diff --git a/result b/result
new file mode 120000
index 0000000..91521cf
--- /dev/null
+++ b/result
@@ -0,0 +1 @@
+/nix/store/gan72x7dji7c6lwngwfx88my14azg9wn-next-companion-0.1.0
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..31f6de8
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,605 @@
+use adw::prelude::*;
+use adw::{Application, ApplicationWindow, BottomSheet, Clamp, HeaderBar, NavigationPage, NavigationView};
+use gtk::{
+ Box, Button, Entry, Label, ListBox, ListBoxRow, Orientation, ScrolledWindow, Spinner, Stack,
+};
+use gtk::gio;
+use gtk::glib;
+use std::cell::RefCell;
+use std::fs;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+const API_KEY: &str = "3IaBlP9OZw14dvES";
+const BASE_URL: &str = "https://api.nextbike.net";
+
+// ── Bike model ────────────────────────────────────────────────────────────────
+
+#[derive(Clone)]
+struct Bike {
+ id: String,
+ code: String,
+ electric_lock: bool,
+}
+
+// ── Persistent login key ──────────────────────────────────────────────────────
+
+fn config_path() -> PathBuf {
+ let mut p = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
+ p.push("next-companion");
+ p.push("loginkey");
+ p
+}
+
+fn load_loginkey() -> Option {
+ fs::read_to_string(config_path())
+ .ok()
+ .map(|s| s.trim().to_string())
+ .filter(|s| !s.is_empty())
+}
+
+fn save_loginkey(key: &str) {
+ let path = config_path();
+ if let Some(parent) = path.parent() {
+ let _ = fs::create_dir_all(parent);
+ }
+ let _ = fs::write(&path, key);
+}
+
+fn clear_loginkey() {
+ let _ = fs::remove_file(config_path());
+}
+
+// ── API calls (blocking — run via gio::spawn_blocking) ────────────────────────
+
+fn api_login(phone: &str, pin: &str) -> Result {
+ let resp = reqwest::blocking::Client::new()
+ .post(format!("{BASE_URL}/api/login.json"))
+ .form(&[("apikey", API_KEY), ("mobile", phone), ("pin", pin)])
+ .send()
+ .map_err(|e| e.to_string())?;
+ let json: serde_json::Value =
+ serde_json::from_str(&resp.text().map_err(|e| e.to_string())?)
+ .map_err(|e| e.to_string())?;
+ json["user"]["loginkey"]
+ .as_str()
+ .map(|s| s.to_string())
+ .ok_or_else(|| "Invalid credentials".to_string())
+}
+
+fn api_get_rentals(loginkey: &str) -> Result, String> {
+ let resp = reqwest::blocking::Client::new()
+ .post(format!("{BASE_URL}/api/getOpenRentals.json"))
+ .form(&[("apikey", API_KEY), ("loginkey", loginkey)])
+ .send()
+ .map_err(|e| e.to_string())?;
+ let json: serde_json::Value =
+ serde_json::from_str(&resp.text().map_err(|e| e.to_string())?)
+ .map_err(|e| e.to_string())?;
+ let arr = json["rentalCollection"]
+ .as_array()
+ .ok_or_else(|| "No rental data".to_string())?;
+ Ok(arr
+ .iter()
+ .map(|b| Bike {
+ id: b["bike"].as_str().unwrap_or("").to_string(),
+ code: b["code"].as_str().unwrap_or("").to_string(),
+ electric_lock: b["electric_lock"].as_str().map_or(false, |s| s == "true"),
+ })
+ .collect())
+}
+
+fn api_rent(loginkey: &str, bike_id: &str) -> Result<(), String> {
+ reqwest::blocking::Client::new()
+ .post(format!("{BASE_URL}/api/rent.json"))
+ .form(&[("apikey", API_KEY), ("loginkey", loginkey), ("bike", bike_id)])
+ .send()
+ .map_err(|e| e.to_string())?;
+ Ok(())
+}
+
+fn api_return(loginkey: &str, bike_id: &str, station_id: &str) -> Result<(), String> {
+ reqwest::blocking::Client::new()
+ .post(format!("{BASE_URL}/api/return.json"))
+ .form(&[
+ ("apikey", API_KEY),
+ ("bike", bike_id),
+ ("loginkey", loginkey),
+ ("station", station_id),
+ ("comment", ""),
+ ])
+ .send()
+ .map_err(|e| e.to_string())?;
+ Ok(())
+}
+
+// ── Async helpers (run on GLib main context) ──────────────────────────────────
+
+async fn load_rentals(key: String, bikes: Rc>>, bikes_list: ListBox, list_stack: Stack) {
+ let result = match gio::spawn_blocking(move || api_get_rentals(&key)).await {
+ Ok(r) => r,
+ Err(_) => return,
+ };
+ if let Ok(new_bikes) = result {
+ while let Some(child) = bikes_list.first_child() {
+ bikes_list.remove(&child);
+ }
+ for bike in &new_bikes {
+ let text = format!(
+ "Bike {} · code: {}{}",
+ bike.id,
+ bike.code,
+ if bike.electric_lock { " ⚡" } else { "" }
+ );
+ let lbl = Label::builder()
+ .label(&text)
+ .xalign(0.0)
+ .margin_top(14)
+ .margin_bottom(14)
+ .margin_start(12)
+ .margin_end(12)
+ .build();
+ let row = ListBoxRow::new();
+ row.set_child(Some(&lbl));
+ bikes_list.append(&row);
+ }
+ list_stack.set_visible_child_name(if new_bikes.is_empty() { "empty" } else { "list" });
+ *bikes.borrow_mut() = new_bikes;
+ }
+}
+
+// ── Entry point ───────────────────────────────────────────────────────────────
+
+fn main() -> glib::ExitCode {
+ let app = Application::builder()
+ .application_id("org.nextbike.NextCompanion")
+ .build();
+ app.connect_activate(build_ui);
+ app.run()
+}
+
+// ── UI ────────────────────────────────────────────────────────────────────────
+
+fn build_ui(app: &Application) {
+ let loginkey: Rc>> = Rc::new(RefCell::new(load_loginkey()));
+ let bikes: Rc>> = Rc::new(RefCell::new(vec![]));
+ let return_bike: Rc>> = Rc::new(RefCell::new(None));
+
+ // ── Login page ────────────────────────────────────────────────────────────
+ let phone_entry = Entry::builder()
+ .placeholder_text("Phone number")
+ .build();
+ let pin_entry = Entry::builder()
+ .placeholder_text("PIN")
+ .visibility(false)
+ .build();
+ let login_err = Label::builder()
+ .css_classes(["error"])
+ .wrap(true)
+ .visible(false)
+ .build();
+ let login_btn = Button::builder()
+ .label("Sign In")
+ .css_classes(["suggested-action", "pill"])
+ .build();
+ let login_spinner = Spinner::new();
+
+ let login_form = Box::builder()
+ .orientation(Orientation::Vertical)
+ .spacing(12)
+ .margin_top(24).margin_bottom(24).margin_start(12).margin_end(12)
+ .build();
+ login_form.append(&phone_entry);
+ login_form.append(&pin_entry);
+ login_form.append(&login_err);
+ login_form.append(&login_btn);
+ login_form.append(&login_spinner);
+
+ let login_clamp = Clamp::builder().maximum_size(400).build();
+ login_clamp.set_child(Some(&login_form));
+
+ let login_body = Box::builder().orientation(Orientation::Vertical).build();
+ login_body.append(&HeaderBar::new());
+ login_body.append(&login_clamp);
+
+ let login_page = NavigationPage::builder()
+ .title("Sign In")
+ .child(&login_body)
+ .build();
+
+ // ── Main page ─────────────────────────────────────────────────────────────
+ let bikes_list = ListBox::builder()
+ .css_classes(["boxed-list"])
+ .selection_mode(gtk::SelectionMode::None)
+ .margin_top(8).margin_bottom(8).margin_start(12).margin_end(12)
+ .build();
+ let empty_label = Label::builder()
+ .label("No active rentals")
+ .margin_top(48)
+ .css_classes(["dim-label"])
+ .build();
+ let list_stack = Stack::new();
+ list_stack.add_named(&empty_label, Some("empty"));
+ list_stack.add_named(&bikes_list, Some("list"));
+ list_stack.set_visible_child_name("empty");
+
+ let scroll = ScrolledWindow::builder().vexpand(true).child(&list_stack).build();
+
+ let rent_btn = Button::builder()
+ .label("Rent a Bike")
+ .css_classes(["suggested-action", "pill"])
+ .margin_top(8).margin_bottom(12).margin_start(12).margin_end(12)
+ .build();
+
+ let main_hdr = HeaderBar::new();
+ let logout_btn = Button::builder()
+ .icon_name("system-log-out-symbolic")
+ .tooltip_text("Logout")
+ .build();
+ let refresh_btn = Button::builder()
+ .icon_name("view-refresh-symbolic")
+ .tooltip_text("Refresh")
+ .build();
+ main_hdr.pack_end(&logout_btn);
+ main_hdr.pack_start(&refresh_btn);
+
+ // ── Return sheet (bottom sheet) ───────────────────────────────────────────
+ let station_entry = Entry::builder()
+ .placeholder_text("Station number")
+ .input_purpose(gtk::InputPurpose::Digits)
+ .build();
+ let ret_err = Label::builder()
+ .css_classes(["error"])
+ .wrap(true)
+ .visible(false)
+ .build();
+ let ret_submit = Button::builder()
+ .label("Return Bike")
+ .css_classes(["destructive-action", "pill"])
+ .build();
+ let ret_spinner = Spinner::new();
+ let electric_msg = Label::builder()
+ .label("This bike has an electric lock.\nJust close the lock to return it.")
+ .wrap(true)
+ .justify(gtk::Justification::Center)
+ .margin_top(24)
+ .build();
+
+ let manual_form = Box::builder()
+ .orientation(Orientation::Vertical)
+ .spacing(12)
+ .build();
+ manual_form.append(&station_entry);
+ manual_form.append(&ret_err);
+ manual_form.append(&ret_submit);
+ manual_form.append(&ret_spinner);
+
+ let ret_inner = Stack::new();
+ ret_inner.add_named(&manual_form, Some("manual"));
+ ret_inner.add_named(&electric_msg, Some("electric"));
+
+ let sheet_title = Label::builder()
+ .css_classes(["title-4"])
+ .label("Return Bike")
+ .xalign(0.0)
+ .build();
+ let sheet_box = Box::builder()
+ .orientation(Orientation::Vertical)
+ .spacing(16)
+ .margin_top(8)
+ .margin_bottom(24)
+ .margin_start(16)
+ .margin_end(16)
+ .build();
+ sheet_box.append(&sheet_title);
+ sheet_box.append(&ret_inner);
+
+ let bottom_sheet = BottomSheet::builder()
+ .show_drag_handle(true)
+ .sheet(&sheet_box)
+ .build();
+
+ let main_content = Box::builder().orientation(Orientation::Vertical).build();
+ main_content.append(&scroll);
+ main_content.append(&rent_btn);
+ bottom_sheet.set_content(Some(&main_content));
+
+ let main_body = Box::builder().orientation(Orientation::Vertical).build();
+ main_body.append(&main_hdr);
+ main_body.append(&bottom_sheet);
+
+ let main_page = NavigationPage::builder()
+ .title("NextCompanion")
+ .child(&main_body)
+ .build();
+
+ // ── Rent page ─────────────────────────────────────────────────────────────
+ let bike_entry = Entry::builder()
+ .placeholder_text("Bike number")
+ .input_purpose(gtk::InputPurpose::Digits)
+ .build();
+ let rent_err = Label::builder()
+ .css_classes(["error"])
+ .wrap(true)
+ .visible(false)
+ .build();
+ let rent_submit = Button::builder()
+ .label("Rent")
+ .css_classes(["suggested-action", "pill"])
+ .build();
+ let rent_spinner = Spinner::new();
+
+ let rent_form = Box::builder()
+ .orientation(Orientation::Vertical)
+ .spacing(12)
+ .margin_top(24).margin_bottom(24).margin_start(12).margin_end(12)
+ .build();
+ rent_form.append(&bike_entry);
+ rent_form.append(&rent_err);
+ rent_form.append(&rent_submit);
+ rent_form.append(&rent_spinner);
+
+ let rent_clamp = Clamp::builder().maximum_size(400).build();
+ rent_clamp.set_child(Some(&rent_form));
+
+ let rent_body = Box::builder().orientation(Orientation::Vertical).build();
+ rent_body.append(&HeaderBar::new());
+ rent_body.append(&rent_clamp);
+
+ let rent_page = NavigationPage::builder()
+ .title("Rent Bike")
+ .child(&rent_body)
+ .build();
+
+ // ── Navigation view ───────────────────────────────────────────────────────
+ let nav = NavigationView::new();
+ nav.push(&main_page);
+ if loginkey.borrow().is_none() {
+ nav.push(&login_page);
+ }
+
+ // ── Window ────────────────────────────────────────────────────────────────
+ let window = ApplicationWindow::builder()
+ .application(app)
+ .title("NextCompanion")
+ .default_width(390)
+ .default_height(700)
+ .content(&nav)
+ .build();
+
+ // ── Login button ──────────────────────────────────────────────────────────
+ {
+ let phone = phone_entry.clone();
+ let pin = pin_entry.clone();
+ let err = login_err.clone();
+ let spinner = login_spinner.clone();
+ let btn = login_btn.clone();
+ let nav = nav.clone();
+ let loginkey = loginkey.clone();
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ login_btn.connect_clicked(move |_| {
+ let p = phone.text().to_string();
+ let n = pin.text().to_string();
+ if p.is_empty() || n.is_empty() {
+ err.set_label("Phone and PIN are required");
+ err.set_visible(true);
+ return;
+ }
+ err.set_visible(false);
+ spinner.set_spinning(true);
+ btn.set_sensitive(false);
+
+ let spinner = spinner.clone();
+ let btn = btn.clone();
+ let err = err.clone();
+ let nav = nav.clone();
+ let loginkey = loginkey.clone();
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ glib::MainContext::default().spawn_local(async move {
+ let result = match gio::spawn_blocking(move || api_login(&p, &n)).await {
+ Ok(r) => r,
+ Err(_) => Err("Internal error".to_string()),
+ };
+ spinner.set_spinning(false);
+ btn.set_sensitive(true);
+ match result {
+ Ok(key) => {
+ save_loginkey(&key);
+ *loginkey.borrow_mut() = Some(key.clone());
+ nav.pop();
+ load_rentals(key, bikes, bikes_list, list_stack).await;
+ }
+ Err(e) => {
+ err.set_label(&e);
+ err.set_visible(true);
+ }
+ }
+ });
+ });
+ }
+
+ // ── Logout button ─────────────────────────────────────────────────────────
+ {
+ let nav = nav.clone();
+ let login_page = login_page.clone();
+ let loginkey = loginkey.clone();
+ logout_btn.connect_clicked(move |_| {
+ clear_loginkey();
+ *loginkey.borrow_mut() = None;
+ nav.push(&login_page);
+ });
+ }
+
+ // ── Refresh button ────────────────────────────────────────────────────────
+ {
+ let loginkey = loginkey.clone();
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ refresh_btn.connect_clicked(move |_| {
+ if let Some(key) = loginkey.borrow().clone() {
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ glib::MainContext::default().spawn_local(async move {
+ load_rentals(key, bikes, bikes_list, list_stack).await;
+ });
+ }
+ });
+ }
+
+ // ── Open rent page ────────────────────────────────────────────────────────
+ {
+ let nav = nav.clone();
+ let rent_page = rent_page.clone();
+ let bike_entry = bike_entry.clone();
+ rent_btn.connect_clicked(move |_| {
+ bike_entry.set_text("");
+ nav.push(&rent_page);
+ });
+ }
+
+ // ── Rent submit ───────────────────────────────────────────────────────────
+ {
+ let loginkey = loginkey.clone();
+ let entry = bike_entry.clone();
+ let err = rent_err.clone();
+ let spinner = rent_spinner.clone();
+ let btn = rent_submit.clone();
+ let nav = nav.clone();
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ rent_submit.connect_clicked(move |_| {
+ let id = entry.text().to_string();
+ if id.is_empty() {
+ err.set_label("Enter a bike number");
+ err.set_visible(true);
+ return;
+ }
+ if let Some(key) = loginkey.borrow().clone() {
+ err.set_visible(false);
+ spinner.set_spinning(true);
+ btn.set_sensitive(false);
+
+ let spinner = spinner.clone();
+ let btn = btn.clone();
+ let err = err.clone();
+ let nav = nav.clone();
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ let key_reload = key.clone();
+ glib::MainContext::default().spawn_local(async move {
+ let result = match gio::spawn_blocking(move || api_rent(&key, &id)).await {
+ Ok(r) => r,
+ Err(_) => Err("Internal error".to_string()),
+ };
+ spinner.set_spinning(false);
+ btn.set_sensitive(true);
+ if let Err(e) = result {
+ err.set_label(&e);
+ err.set_visible(true);
+ } else {
+ nav.pop();
+ load_rentals(key_reload, bikes, bikes_list, list_stack).await;
+ }
+ });
+ }
+ });
+ }
+
+ // ── Click rental row → open return bottom sheet ───────────────────────────
+ {
+ let bottom_sheet = bottom_sheet.clone();
+ let bikes = bikes.clone();
+ let return_bike = return_bike.clone();
+ let ret_inner = ret_inner.clone();
+ let station_entry = station_entry.clone();
+ let ret_err = ret_err.clone();
+ bikes_list.connect_row_activated(move |_, row| {
+ let idx = row.index() as usize;
+ let bike = bikes.borrow().get(idx).cloned();
+ if let Some(bike) = bike {
+ station_entry.set_text("");
+ ret_err.set_visible(false);
+ ret_inner.set_visible_child_name(if bike.electric_lock { "electric" } else { "manual" });
+ *return_bike.borrow_mut() = Some(bike);
+ bottom_sheet.set_open(true);
+ }
+ });
+ }
+
+ // ── Return submit ─────────────────────────────────────────────────────────
+ {
+ let loginkey = loginkey.clone();
+ let return_bike = return_bike.clone();
+ let entry = station_entry.clone();
+ let err = ret_err.clone();
+ let spinner = ret_spinner.clone();
+ let btn = ret_submit.clone();
+ let bottom_sheet = bottom_sheet.clone();
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ ret_submit.connect_clicked(move |_| {
+ let station = entry.text().to_string();
+ if station.is_empty() {
+ err.set_label("Enter a station number");
+ err.set_visible(true);
+ return;
+ }
+ if let (Some(key), Some(bike)) =
+ (loginkey.borrow().clone(), return_bike.borrow().clone())
+ {
+ err.set_visible(false);
+ spinner.set_spinning(true);
+ btn.set_sensitive(false);
+
+ let spinner = spinner.clone();
+ let btn = btn.clone();
+ let err = err.clone();
+ let bottom_sheet = bottom_sheet.clone();
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ let key_reload = key.clone();
+ glib::MainContext::default().spawn_local(async move {
+ let result = match gio::spawn_blocking(move || {
+ api_return(&key, &bike.id, &station)
+ })
+ .await
+ {
+ Ok(r) => r,
+ Err(_) => Err("Internal error".to_string()),
+ };
+ spinner.set_spinning(false);
+ btn.set_sensitive(true);
+ if let Err(e) = result {
+ err.set_label(&e);
+ err.set_visible(true);
+ } else {
+ bottom_sheet.set_open(false);
+ load_rentals(key_reload, bikes, bikes_list, list_stack).await;
+ }
+ });
+ }
+ });
+ }
+
+ // ── Initial rentals load ──────────────────────────────────────────────────
+ if let Some(key) = loginkey.borrow().clone() {
+ let bikes = bikes.clone();
+ let bikes_list = bikes_list.clone();
+ let list_stack = list_stack.clone();
+ glib::MainContext::default().spawn_local(async move {
+ load_rentals(key, bikes, bikes_list, list_stack).await;
+ });
+ }
+
+ window.present();
+}
From 5e4560e89e0b6c48e9be2d2729db0ac3bc897963 Mon Sep 17 00:00:00 2001
From: Jonas Heinrich
Date: Tue, 10 Mar 2026 23:31:50 +0100
Subject: [PATCH 8/9] add bottomsheet navigation
---
app/.gitignore | 1 -
app/build.gradle | 29 ---
app/proguard-rules.pro | 21 --
.../ExampleInstrumentedTest.java | 26 ---
app/src/main/AndroidManifest.xml | 50 -----
app/src/main/ic_launcher-web.png | Bin 18362 -> 0 bytes
.../nextcompanion/AsyncTaskCallbacks.java | 5 -
.../hochi/nextcompanion/LoginActivity.java | 184 ------------------
.../hochi/nextcompanion/MainActivity.java | 175 -----------------
.../hochi/nextcompanion/RentActivity.java | 65 -------
.../hochi/nextcompanion/RequestHandler.java | 98 ----------
.../hochi/nextcompanion/ReturnActivity.java | 70 -------
.../main/res/drawable/ic_add_white_24dp.xml | 9 -
app/src/main/res/layout/activity_login.xml | 78 --------
app/src/main/res/layout/activity_main.xml | 33 ----
app/src/main/res/layout/activity_rent.xml | 50 -----
app/src/main/res/layout/activity_return.xml | 62 ------
app/src/main/res/layout/content_main.xml | 24 ---
app/src/main/res/menu/menu_main.xml | 15 --
.../res/mipmap-anydpi-v26/ic_launcher.xml | 5 -
.../mipmap-anydpi-v26/ic_launcher_round.xml | 5 -
app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 1903 -> 0 bytes
.../mipmap-hdpi/ic_launcher_foreground.png | Bin 1935 -> 0 bytes
.../res/mipmap-hdpi/ic_launcher_round.png | Bin 3275 -> 0 bytes
app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1147 -> 0 bytes
.../mipmap-mdpi/ic_launcher_foreground.png | Bin 1187 -> 0 bytes
.../res/mipmap-mdpi/ic_launcher_round.png | Bin 1996 -> 0 bytes
app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 2429 -> 0 bytes
.../mipmap-xhdpi/ic_launcher_foreground.png | Bin 2639 -> 0 bytes
.../res/mipmap-xhdpi/ic_launcher_round.png | Bin 4399 -> 0 bytes
.../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 3834 -> 0 bytes
.../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 4311 -> 0 bytes
.../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 6908 -> 0 bytes
.../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 5234 -> 0 bytes
.../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 6044 -> 0 bytes
.../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 9776 -> 0 bytes
app/src/main/res/values-de/strings.xml | 30 ---
app/src/main/res/values/colors.xml | 6 -
app/src/main/res/values/dimens.xml | 6 -
.../res/values/ic_launcher_background.xml | 4 -
app/src/main/res/values/strings.xml | 29 ---
app/src/main/res/values/styles.xml | 20 --
.../hochi/nextcompanion/ExampleUnitTest.java | 17 --
build.gradle | 27 ---
gradle.properties | 13 --
gradle/wrapper/gradle-wrapper.jar | Bin 54708 -> 0 bytes
gradle/wrapper/gradle-wrapper.properties | 6 -
gradlew | 172 ----------------
gradlew.bat | 84 --------
settings.gradle | 1 -
src/main.rs | 115 ++++++-----
51 files changed, 65 insertions(+), 1470 deletions(-)
delete mode 100644 app/.gitignore
delete mode 100644 app/build.gradle
delete mode 100644 app/proguard-rules.pro
delete mode 100644 app/src/androidTest/java/com/example/hochi/nextcompanion/ExampleInstrumentedTest.java
delete mode 100644 app/src/main/AndroidManifest.xml
delete mode 100644 app/src/main/ic_launcher-web.png
delete mode 100644 app/src/main/java/com/example/hochi/nextcompanion/AsyncTaskCallbacks.java
delete mode 100644 app/src/main/java/com/example/hochi/nextcompanion/LoginActivity.java
delete mode 100644 app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java
delete mode 100644 app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
delete mode 100644 app/src/main/java/com/example/hochi/nextcompanion/RequestHandler.java
delete mode 100644 app/src/main/java/com/example/hochi/nextcompanion/ReturnActivity.java
delete mode 100644 app/src/main/res/drawable/ic_add_white_24dp.xml
delete mode 100644 app/src/main/res/layout/activity_login.xml
delete mode 100644 app/src/main/res/layout/activity_main.xml
delete mode 100644 app/src/main/res/layout/activity_rent.xml
delete mode 100644 app/src/main/res/layout/activity_return.xml
delete mode 100644 app/src/main/res/layout/content_main.xml
delete mode 100644 app/src/main/res/menu/menu_main.xml
delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png
delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png
delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png
delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png
delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/values-de/strings.xml
delete mode 100644 app/src/main/res/values/colors.xml
delete mode 100644 app/src/main/res/values/dimens.xml
delete mode 100644 app/src/main/res/values/ic_launcher_background.xml
delete mode 100644 app/src/main/res/values/strings.xml
delete mode 100644 app/src/main/res/values/styles.xml
delete mode 100644 app/src/test/java/com/example/hochi/nextcompanion/ExampleUnitTest.java
delete mode 100644 build.gradle
delete mode 100644 gradle.properties
delete mode 100644 gradle/wrapper/gradle-wrapper.jar
delete mode 100644 gradle/wrapper/gradle-wrapper.properties
delete mode 100755 gradlew
delete mode 100644 gradlew.bat
delete mode 100644 settings.gradle
diff --git a/app/.gitignore b/app/.gitignore
deleted file mode 100644
index 796b96d..0000000
--- a/app/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index 7cde814..0000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,29 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion 28
- defaultConfig {
- applicationId "com.example.hochi.nextcompanion"
- minSdkVersion 15
- targetSdkVersion 28
- versionCode 9
- versionName "0.1.7.2"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- implementation 'com.android.support:design:28.0.0'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
-}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
deleted file mode 100644
index f1b4245..0000000
--- a/app/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/app/src/androidTest/java/com/example/hochi/nextcompanion/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/hochi/nextcompanion/ExampleInstrumentedTest.java
deleted file mode 100644
index 2759dd6..0000000
--- a/app/src/androidTest/java/com/example/hochi/nextcompanion/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.example.hochi.nextcompanion;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
-
- assertEquals("com.example.hochi.nextcompanion", appContext.getPackageName());
- }
-}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
deleted file mode 100644
index f15290c..0000000
--- a/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
deleted file mode 100644
index e96c6aa8fd71eb03c0aa721a8f85038a399f93ea..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 18362
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
Bb6NlZ
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/AsyncTaskCallbacks.java b/app/src/main/java/com/example/hochi/nextcompanion/AsyncTaskCallbacks.java
deleted file mode 100644
index 2099b15..0000000
--- a/app/src/main/java/com/example/hochi/nextcompanion/AsyncTaskCallbacks.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.example.hochi.nextcompanion;
-
-interface AsyncTaskCallbacks {
- void onTaskComplete(T response);
-}
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/LoginActivity.java b/app/src/main/java/com/example/hochi/nextcompanion/LoginActivity.java
deleted file mode 100644
index 1ff8e6b..0000000
--- a/app/src/main/java/com/example/hochi/nextcompanion/LoginActivity.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package com.example.hochi.nextcompanion;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.TargetApi;
-
-import android.content.SharedPreferences;
-import android.support.v7.app.AppCompatActivity;
-
-import android.os.Build;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.inputmethod.EditorInfo;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import org.json.JSONObject;
-
-
-/**
- * A login screen that offers login via phone number/pin.
- */
-public class LoginActivity extends AppCompatActivity implements AsyncTaskCallbacks {
-
- /**
- * Keep track of the login task to ensure we can cancel it if requested.
- */
- private RequestHandler mAuthTask = null;
-
- // UI references.
- private TextView mPhoneView;
- private EditText mPinView;
- private View mProgressView;
- private View mLoginFormView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_login);
- // Set up the login form.
- mPhoneView = findViewById(R.id.phone);
-
- mPinView = findViewById(R.id.pin);
- mPinView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
- if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) {
- attemptLogin();
- return true;
- }
- return false;
- }
- });
-
- Button mPhoneSignInButton = findViewById(R.id.phone_sign_in_button);
- mPhoneSignInButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- attemptLogin();
- }
- });
-
- mLoginFormView = findViewById(R.id.login_form);
- mProgressView = findViewById(R.id.login_progress);
- }
-
- /**
- * Attempts to sign in or register the account specified by the login form.
- * If there are form errors (invalid phone number, missing fields, etc.), the
- * errors are presented and no actual login attempt is made.
- */
- private void attemptLogin() {
- if (mAuthTask != null) {
- return;
- }
-
- // Reset errors.
- mPhoneView.setError(null);
- mPinView.setError(null);
-
- // Store values at the time of the login attempt.
- String phone = mPhoneView.getText().toString();
- String pin = mPinView.getText().toString();
- String[] credentials = {
- "apikey=", getString(R.string.apikey),
- "mobile=", mPhoneView.getText().toString(),
- "pin=", mPinView.getText().toString()
- };
-
- boolean cancel = false;
- View focusView = null;
-
- // Check for a valid pin, if the user entered one.
- if (TextUtils.isEmpty(pin)) {
- mPinView.setError(getString(R.string.error_field_required));
- focusView = mPinView;
- cancel = true;
- }
-
- // Check for a valid phone address.
- if (TextUtils.isEmpty(phone)) {
- mPhoneView.setError(getString(R.string.error_field_required));
- focusView = mPhoneView;
- cancel = true;
- }
-
- if (cancel) {
- // There was an error; don't attempt login and focus the first
- // form field with an error.
- focusView.requestFocus();
- } else {
- // Show a progress spinner, and kick off a background task to
- // perform the user login attempt.
- showProgress(true);
- mAuthTask = new RequestHandler(this, "POST",
- "api/login.json", credentials);
- mAuthTask.execute((Void) null);
- }
- }
-
- /**
- * Shows the progress UI and hides the login form.
- */
- @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
- private void showProgress(final boolean show) {
- // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
- // for very easy animations. If available, use these APIs to fade-in
- // the progress spinner.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
- int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
-
- mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
- mLoginFormView.animate().setDuration(shortAnimTime).alpha(
- show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
- }
- });
-
- mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
- mProgressView.animate().setDuration(shortAnimTime).alpha(
- show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
- }
- });
- } else {
- // The ViewPropertyAnimator APIs are not available, so simply show
- // and hide the relevant UI components.
- mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
- mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
- }
- }
-
- @Override
- public void onTaskComplete(String response) {
- //Callback called when RequestHandler finished request
- if (!response.isEmpty()) {
- try {
- JSONObject jObject = new JSONObject(response);
- JSONObject userObject = jObject.getJSONObject("user");
- String loginkey = userObject.getString("loginkey");
- SharedPreferences sharedPref = getSharedPreferences("persistence", MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.putString("loginKey", loginkey);
- editor.apply();
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- finish();
- } else {
- mPinView.setError(getString(R.string.error_incorrect_pin));
- mPinView.requestFocus();
- }
- }
-}
-
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java b/app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java
deleted file mode 100644
index ee51d6e..0000000
--- a/app/src/main/java/com/example/hochi/nextcompanion/MainActivity.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package com.example.hochi.nextcompanion;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.design.widget.FloatingActionButton;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.View;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-
-public class MainActivity extends AppCompatActivity implements AsyncTaskCallbacks {
- private RequestHandler getBikesTask = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- //now this "every android activity" stuff
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Toolbar toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
- final Context context = this;
-
- //Floating Button
- FloatingActionButton fab = findViewById(R.id.fab);
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(context, RentActivity.class);
- startActivity(intent);
- }
- });
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- //pre-condition: Is there a login key?
- SharedPreferences sharedPref = getSharedPreferences("persistence", MODE_PRIVATE);
- String defaultValue = "nokey";
- String loginKey = sharedPref.getString("loginKey", defaultValue);
- //if not, go to LoginActivity
- if (loginKey.equals("nokey")) {
- Intent intent = new Intent(this, LoginActivity.class);
- startActivity(intent);
- }
- else {
- reloadBikeList();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_main, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- int id = item.getItemId();
-
-
- //noinspection SimplifiableIfStatement
- if (id == R.id.action_logout) {
- SharedPreferences sharedPref = getSharedPreferences("persistence", MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.remove("loginKey");
- editor.apply();
- Intent intent = new Intent(this, LoginActivity.class);
- startActivity(intent);
- }
-
- if (id == R.id.action_map) {
- Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.map_url)));
- startActivity(browserIntent);
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- protected void reloadBikeList() {
- //get loginkey
- SharedPreferences sharedPref = getSharedPreferences("persistence", MODE_PRIVATE);
- String defaultValue = "nokey";
- String loginKey = sharedPref.getString("loginKey", defaultValue);
-
- String[] params = {
- "apikey=", getString(R.string.apikey),
- "loginkey=", loginKey
- };
-
- getBikesTask = new RequestHandler(this, "POST",
- "api/getOpenRentals.json", params);
- getBikesTask.execute((Void) null);
- }
-
- @Override
- public void onTaskComplete(String response) {
- //Callback called when RequestHandler finished request
- final Context context = this;
- if (!response.isEmpty()) {
- final ArrayList list = new ArrayList<>();
- try {
- JSONObject jObject = new JSONObject(response);
- JSONArray bikesArray = jObject.getJSONArray("rentalCollection");
- for (int i = 0; i < bikesArray.length(); i++) {
- String entry;
- JSONObject bike = bikesArray.getJSONObject(i);
- entry = "Bike " + bike.getString("bike")
- + " with lock code " + bike.getString("code");
- list.add(entry);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- //Create and fill list
- final ListView listview = findViewById(R.id.listview);
- final ArrayAdapter adapter = new ArrayAdapter<>(this,
- android.R.layout.simple_list_item_1, list);
- listview.setAdapter(adapter);
-
- //Print indicator if empty
- TextView tv = findViewById(R.id.noBikes);
- if(list.isEmpty()) tv.setVisibility(View.VISIBLE);
- else tv.setVisibility(View.INVISIBLE);
-
- try {
- final JSONObject jObject = new JSONObject(response);
- final JSONArray bikesArray = jObject.getJSONArray("rentalCollection");
- listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, final View view, int position, long id) {
- Intent intent = new Intent(context, ReturnActivity.class);
- try {
- JSONObject bike = bikesArray.getJSONObject(position);
- String bID = bike.getString("bike");
- String stID = bike.getString("start_place");
- String lockE = bike.getString("electric_lock");
- String[] bikeArray = {bID, stID, lockE};
- intent.putExtra("bike", bikeArray);
- startActivity(intent);
- }
- catch (JSONException e) {
- e.printStackTrace();
- }
- }
-
- });
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- else {
- //TODO: implement error handling
- }
- }
-}
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java b/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
deleted file mode 100644
index 4b1eb87..0000000
--- a/app/src/main/java/com/example/hochi/nextcompanion/RentActivity.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.example.hochi.nextcompanion;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.support.v7.app.AppCompatActivity;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import org.w3c.dom.Text;
-
-public class RentActivity extends AppCompatActivity implements AsyncTaskCallbacks {
- private RequestHandler rentRequestTask = null;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_rent);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- Button mRentSubmitButton = findViewById(R.id.rent_submit_button);
- mRentSubmitButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- rentRequest();
- }
- });
-
- Intent intent = getIntent();
- Uri data = intent.getData();
-
- if (data != null) {
- String bikeID = data.toString().substring(15);
- ((TextView) findViewById(R.id.bike_id)).setText(bikeID);
- }
- }
-
- void rentRequest() {
- //Prepare request to rent bike
- TextView mBikeInput;
- mBikeInput = findViewById(R.id.bike_id);
- String bikeID = mBikeInput.getText().toString();
- //get loginkey
- SharedPreferences sharedPref = getSharedPreferences("persistence", MODE_PRIVATE);
- String defaultValue = "nokey";
- String loginKey = sharedPref.getString("loginKey", defaultValue);
-
- String[] params = {
- "apikey=", getString(R.string.apikey),
- "loginkey=", loginKey,
- "bike=", bikeID
- };
-
- rentRequestTask = new RequestHandler(this, "POST",
- "api/rent.json", params);
- rentRequestTask.execute((Void) null);
- }
-
- @Override
- public void onTaskComplete(String response) {
- //get back to main activity
- //TODO: *any* response handling
- finish();
- }
-}
diff --git a/app/src/main/java/com/example/hochi/nextcompanion/RequestHandler.java b/app/src/main/java/com/example/hochi/nextcompanion/RequestHandler.java
deleted file mode 100644
index 2476e63..0000000
--- a/app/src/main/java/com/example/hochi/nextcompanion/RequestHandler.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.example.hochi.nextcompanion;
-
-import android.os.AsyncTask;
-
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLEncoder;
-
-public class RequestHandler extends AsyncTask {
-
- private String mHTTPmethod;
- private String mEndpoint;
- private AsyncTaskCallbacks callback;
- private String[] mCredentials;
-
- RequestHandler(AsyncTaskCallbacks act, String HTTPmethod,
- String endpoint, String[] credentials) {
- mHTTPmethod = HTTPmethod;
- mEndpoint = endpoint;
- mCredentials = credentials;
- callback = act;
- }
-
- @Override
- protected String doInBackground(Void... params) {
- StringBuilder response = new StringBuilder();
- StringBuilder urlParameters = new StringBuilder();
- int i=0;
- while (i {
- private String[] bikeArray;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_return);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
-
- Intent intent = getIntent();
- bikeArray = intent.getStringArrayExtra("bike");
-
- //if GPS and electric lock, show the instruction
- TextView tv = findViewById(R.id.gps_info);
- LinearLayout la = findViewById(R.id.return_form_container);
- if(bikeArray[2].equals("true")) {
- tv.setVisibility(View.VISIBLE);
- la.setVisibility(View.INVISIBLE);
- }
- else {
- la.setVisibility(View.VISIBLE);
- tv.setVisibility(View.INVISIBLE);
- Button mReturnSubmitButton = findViewById(R.id.return_submit_button);
- mReturnSubmitButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- returnRequest();
- }
- });
- }
- }
- void returnRequest() {
- TextView mStationInput;
- mStationInput = findViewById(R.id.return_station_id);
- String stationID = mStationInput.getText().toString();
- //get loginkey
- SharedPreferences sharedPref = getSharedPreferences("persistence", MODE_PRIVATE);
- String defaultValue = "nokey";
- String loginKey = sharedPref.getString("loginKey", defaultValue);
-
- String[] params = {
- "apikey=", getString(R.string.apikey),
- "bike=", bikeArray[0],
- "loginkey=", loginKey,
- "station=", stationID,
- "comment=", ""
- };
- RequestHandler returnRequestTask = new RequestHandler(this, "POST",
- "api/return.json", params);
- returnRequestTask.execute((Void) null);
- }
-
- @Override
- public void onTaskComplete(String response) {
- //get back to main activity
- //TODO: *any* response handling
- finish();
- }
-}
diff --git a/app/src/main/res/drawable/ic_add_white_24dp.xml b/app/src/main/res/drawable/ic_add_white_24dp.xml
deleted file mode 100644
index b5b5ba4..0000000
--- a/app/src/main/res/drawable/ic_add_white_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
deleted file mode 100644
index de480af..0000000
--- a/app/src/main/res/layout/activity_login.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index d48b1b1..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_rent.xml b/app/src/main/res/layout/activity_rent.xml
deleted file mode 100644
index fb3084d..0000000
--- a/app/src/main/res/layout/activity_rent.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_return.xml b/app/src/main/res/layout/activity_return.xml
deleted file mode 100644
index 5728d58..0000000
--- a/app/src/main/res/layout/activity_return.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
deleted file mode 100644
index ef9edf3..0000000
--- a/app/src/main/res/layout/content_main.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
deleted file mode 100644
index e2970cd..0000000
--- a/app/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 036d09b..0000000
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 036d09b..0000000
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 287a510c56d974017da6f7115d270bf9b1300df5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1903
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
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ded5dc9d91c85098778665dfe762ec75c0589254..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1147
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?FOrZ@OKs&)|G=P;rm>_jx|0c1xWk!ey2$DLwvhP|qSO6;Z85l^s=l2;S^xNhgdb0YSFdX5=b7Hb
zb%HCTVMV@zwbFxn0eN|UO-)S^adGiFCN3_njMCDn=gytWC@PvXY4YU5-Ul8%QrdYp
zukZMA_gAl8ap^NkOH0c!9yoBI;Rn+MwWUj!+PsgBjz0Oa{zvGm5}>^KY#Y9|7dS#pEYAfKu>{_6Vumk
z-!9#|Cl?zZUwq~I^={<_yLQc56Sg{iV^mbsqucE9){8HCq^71`xpT)SZL_5blaR3R
z%8eT*{`&oU`k2r%s=)Ton=$a^d2|iO+p?baW=1ObPmNi0|8nX8Vc*-_@8dRHmn^
z&z(R2v&lsF+fR!wo;t-PDk>V7mZp}QmsjP&!^c;I^|)^%NY!rZxMi)*s8yTe8An>S1KPc1Jin|5EhAd=zTN13--*^EC9aMyhi
z7GbQdtu3jpe!acu$gyLW&YbB{=g4Q=RI%l--&y%R58Uk?1r2O%&wl^Ba^=dp)o$+Y
z?04_n3Gnspbpo67ujU}g6u|{*466L=cdT3IXUecQZoS9Cix&g;wH{ku&UFxG&g>%^
z<+pF&uJL{oy)Y^&s^UO#aq*vfNgD%FQ&s;Tv7NVAwrt<-pbw4We;j8Yc_Grx|LXh0
zhl=|3m6eq?Cq8~G+_h`hqQ{SuPn|p&D4S^GZVd{oAIl+;{J$K$b
zEwj9co@vvk|7Clx%T)GBNrSoOLFmo7`~HP7t=8Ihh?kdl&y&*f^3VE)Y|V-bY=vRQ
z)a_g9T6crXn=$e48@=PD-6uL(LJ9;~$(4C@+g-b5=-C
ziPF4Y=T&@AbFbZVY3FyshJPkqa+iMLsXC=D;m4T|rbm7#9Zl+}dKS5H-oYaR@<}t6
rM8uVR=sPJSob*EJ{||wmzqs4vPFB5p;(UvNfq}u()z4*}Q$iB}yp0vg
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index 03ca6747a6dd2111c598536c0f04d29442fd1956..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1187
zcmeAS@N?(olHy`uVBq!ia0y~yV8{Vs4mJh`hW@nhvkVL@S)MMAAr-gY&i3_)2^Tqj
zbyCPF5zkfJxnhFOii!#xVcBAOw_0~h@KC&YWLJTrkWq`vuA+-`InEw(IC@nl_Gs^v
zX*y2kyekyDBPC8OVA;fZRI77a_cF!FpI=P8H)l^}{Ev@M{?&bOpHcaJj`{QV)!+9O
zr@wl|fEyI~Y>l$j+@%{O7V6Ty$WCi={ARfj`zO6Gq(o26Pu#wG>rGM9;H5Q^+hTg3
zS+AO+e^|?~?d}qt5NwC_GR&_RWFij-_O!B
zSQ9xvc;{ln)qy4UPq=Og)UG^k!p_~3aE>dlIVRFQ``UWJ;w#!0!$V$G%xKL#enH2_
zx-D(zve-pUKTTiXpK$#jSJ;G*PX%YJ6o08MjO=%P{v}`HyUX*HuMSF?O+B>cvemOK
zF9LfX-|Ld=suPG6{2K7|uj!jesm*f==Xcfl8I(=cdOtsschU9Cvqq2eU&^#4wx`^3
zbJ=)$>lq`%a~2cqZdWpov25SGKYPZuc`2_|I#zBB
zH@Pl-HuhRE=ed<~?f3Mf%A4NDzOgmd?YuR2qwOO5ESdi*FS@RrxVdiQJ+%|kbE3`t
z{)X@9WbrON?R~L9c(L=BEyp(MDq6CexJfk?&pEJ$|7Wa;-m4(xD%Z)2OpEW>FUp@2
zG5_TakHbxdR@YK}i@#)@)3a^gP$6u0pY5)8wzrAQW-FapN%>t-bCaVM^IZOJ-uv?C
zin-4p%=vQXj`rHy=9^!gowIf7m!F<}(k5=x=X{S!;?bUYh+pkY<;5A>-duHZ36Ik|
zta;pj=ArFJCBtfh-|WvP
zH*ETR@VN1@8Kp@&Dh4?RD|5|~&lPUZe>pkP`W*ZFQxeHX<^~H%JUcg2S+cmy(y*+?
z=uEC=X-bb_%KzKRJjLr@E$p$hzB6CzXUdIjpAS|WACuVpIiV-<-5=v=hUx2tlW(gZ
z4x7VoH`CDVq~6x`m5dw#N}PTm(M@HZvo!uMHc`W2rAKhv3|RCWb5vp1EiORV@T
zdH3B7pPlBRCzGZbuAOA`eA(`~k9TLT{UtBAed5m5p92!*i^NQ>x^
z-`QKZZ+U6GX3qDV{?wO!!e-Cp=_cWk#riLHZh6RqJ6kuT?_r*}a@j5!L9bmPRi3VX
JF6*2UngF&FHJtze
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 92b9d0c1ff7c128232618075bad9f432e0ff1be4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1996
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
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index d0b186612d85302f8ea2ef08506f2d60e13728b2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2639
zcmeAS@N?(olHy`uVBq!ia0y~yV7LLo9Bd2>44n$PZy6Z4#5`RbLn>~)y&InqmMU}L
zAHjjblWCychvT7I9|K5@P2n8Ny&N}$*EmkM
zSN%Bo$ki`O5BxTj#r_j%-&y|6U~hYs|Gd|#QKsw4Yu33>w{KOuzWIyZE7O(hE=!+?
z_xxk6Wq9WPeUCj_h1pa8?~a~s|5K0KvUp+MA1R5w`f%$sa6slcpY{p;_N2WDz_
z)p=CfcdIZLd9AzXdF-ju1m~hcHRfY~o}IGVX)&3x>Y0_$({5wQ9Xcu}8#-skOs~~4
zoO_Rbdh1U^?{#_4qZ!iI6+Aa}`m46FA)i?#{D##Lxn2v;GS8&FH&;x_XG`^1H(O=j
z>4#^&URYuDNbTm+wVT7e&pe7g*K*EIxm7`alT?iVO}wG^vBkyszJyv(k?n-z
zsS6kH(d)b)EOS4`PVQ>%gL_jVls|IZS2>p5w0H9L)P;-BRIB)1Y}Fj74n
za`*9FovOQvg%OW>=Cz*jeGsq8=aF+s-1Lm>ZvQ<`cYOE!Gs)m&+?j=QeWSkH<(_8D
zy>cNtV9>1zj&Pm-?wQPN^pT`>0_pd)qxx6Uf({t0Kzb|`k-C9>_
zJ8^-W_?CV#1J33PjN2m~CAf8`oXu`Cn_XUId1`my^23jfUUSd)xOgsX?d|=NTd#h(
zndByr!~9OHlfU>@&as%ZuebGNgMA*)_TGM5D2D5sdX0f`%ge*j5`hx7FLr(X?sl*2
zrrddd!`}OXi|1|@ny35NsP^XB6HSsO{$77#A6&~!+n76hv0dTvkJe1LZ-#w{>sjVM
zKc!`2%)<1ZV4=f8+N#rDEdH6s-;*lDEM%>!$0m3BjNG}hE86Ew>f9Zy(~r;CxMxMK
zVQy8-?HfyT<5t!dXdO%8@eDgYy;U)N-QBd%>Jy8if1cs#6j$kEmASs>T=
zpYo@%pG%GP{hdCJU~#i!;zo+L%Cjd?QbMxx3z&noAaxyYSsiMMlfl{eLJ
zmfiT2x_*sg?6=5QpQ6|}zs-7D{5$KyxvA53$)veV(?57FV&5B8(-qxsZ>~ScBb0Mt
z+x{fpTRV>gO*aM`|5k9qI;ks1r?E&Id2HZSJQ*Eey*r1$%Sh?;^n`8d71db*;`EYaz=pYLOP8yDo}@$cA4cVmwY?y|cB&g$$~
zYpq*)^9V@AS4+8x^8*uX!z{!eZcCexu(r?js5V#5bDra3T(uyV--tP`P`hw})eQBX
z?@_M`O5R*P%PG9Zu|M?ey%h_@JzjicHY)}Pu-b7s@#B;1jN~`-#r8RVcbj@%J@(D-
zw#T1iS+^8#N{*i-TxnC>{&8YTwk&hL!t+hq^V)5#jeR!wop_TDml@h!aiQh4~W@`|LOssFRDB;Q(dZsXj~GPD2A*q5KKGR^v=
zS+!YX=E;R;p7zaje!NlURLz;dc`HwUk~x+0G3V3fnSv4jShByqIbEl6`mdJB&Y0WV
z-=0jK&t=>F_G8ZVZ8nELJ*zPLZYggW*VI^~8d-lpZ{O$OX@M6{9}+yTx2yc=jdQzg
zcJuaU$ZBhzo+-cKcD}vx`Z}RCnh#VtrkC*dKAUko@50SP6T%g2;~k>s^RGDWxVG5&
zOtN{l{LgLj_m7@Q{HuC%&4KBPF&B>i*&WNT^NOoiwC{J!|L7ahN5s;qR$7QeEZ=*p
zZD!wN-;4Vqml?zdYwjWNy~~6?)s=
z&f(Cvy5REqij{otq5D57!+xh_Zms>=Xwh6SDeuM2)wdV%zItZl{Hx=iR_%q2rAqN~
zFH?(FZ%duJE!X3=)r#ErSNF|0a)R0|DFy}xk>~ciu6*6Ngky6p$aGIvKbLh*2~7Yk
C)ZOm@
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 5c57a4722d9522b87d5a7e38967d981bae9c29a4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 4399
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
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index e83664a5e05063ed17e97546a080f272db50b5f3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3834
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 |