diff options
| author | Lena <lena@omega> | 2026-01-01 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-01-01 00:00:00 +0000 |
| commit | 7e04941bccb2683f8a6e3ee38a99c50129234dd1 (patch) | |
| tree | 471227fa437291e7a6b499e3de6c106c54eaf311 /app/src/main/res | |
| download | rsend-7e04941bccb2683f8a6e3ee38a99c50129234dd1.tar.gz | |
rsend: push phone folders to a home SSH host over rsync
A small Android app for one-way folder backup, a KISS alternative to
Syncthing. It bundles rsync (built from pinned source via the NDK) and
a pure-Go SSH transport, both shipped in the APK as lib*.so and run from
the native library directory.
rsend pins the host key, stores the ed25519 identity Keystore-encrypted,
pushes each folder additively or as a mirror, and runs on demand or on a
WiFi-only schedule. The build is self-contained and reproducible: make
setup provisions the toolchain, make builds rsync, rsh, and the APK.
Diffstat (limited to 'app/src/main/res')
29 files changed, 1195 insertions, 0 deletions
diff --git a/app/src/main/res/drawable/bg_button_primary.xml b/app/src/main/res/drawable/bg_button_primary.xml new file mode 100644 index 0000000..c2a22f5 --- /dev/null +++ b/app/src/main/res/drawable/bg_button_primary.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="#33FFFFFF"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/button_primary_bg" /> + <corners android:radius="@dimen/radius" /> + </shape> + </item> +</ripple> diff --git a/app/src/main/res/drawable/bg_button_secondary.xml b/app/src/main/res/drawable/bg_button_secondary.xml new file mode 100644 index 0000000..93c7ccd --- /dev/null +++ b/app/src/main/res/drawable/bg_button_secondary.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/surface_variant"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/surface" /> + <corners android:radius="@dimen/radius" /> + <stroke + android:width="@dimen/stroke" + android:color="@color/outline" /> + </shape> + </item> +</ripple> diff --git a/app/src/main/res/drawable/bg_card.xml b/app/src/main/res/drawable/bg_card.xml new file mode 100644 index 0000000..0971d54 --- /dev/null +++ b/app/src/main/res/drawable/bg_card.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/surface" /> + <corners android:radius="@dimen/radius" /> + <stroke + android:width="@dimen/stroke" + android:color="@color/outline" /> +</shape> diff --git a/app/src/main/res/drawable/bg_chip.xml b/app/src/main/res/drawable/bg_chip.xml new file mode 100644 index 0000000..aecffef --- /dev/null +++ b/app/src/main/res/drawable/bg_chip.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/surface_variant" /> + <corners android:radius="@dimen/radius_pill" /> +</shape> diff --git a/app/src/main/res/drawable/bg_input.xml b/app/src/main/res/drawable/bg_input.xml new file mode 100644 index 0000000..b87f6b3 --- /dev/null +++ b/app/src/main/res/drawable/bg_input.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_focused="true"> + <shape android:shape="rectangle"> + <solid android:color="@color/surface" /> + <corners android:radius="@dimen/radius" /> + <stroke + android:width="2dp" + android:color="@color/accent" /> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/surface" /> + <corners android:radius="@dimen/radius" /> + <stroke + android:width="@dimen/stroke" + android:color="@color/outline" /> + </shape> + </item> +</selector> diff --git a/app/src/main/res/drawable/dot_off.xml b/app/src/main/res/drawable/dot_off.xml new file mode 100644 index 0000000..3115ccc --- /dev/null +++ b/app/src/main/res/drawable/dot_off.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@android:color/transparent" /> + <stroke + android:width="2dp" + android:color="@color/text_secondary" /> + <size + android:width="10dp" + android:height="10dp" /> +</shape> diff --git a/app/src/main/res/drawable/dot_on.xml b/app/src/main/res/drawable/dot_on.xml new file mode 100644 index 0000000..396b841 --- /dev/null +++ b/app/src/main/res/drawable/dot_on.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@color/accent" /> + <size + android:width="10dp" + android:height="10dp" /> +</shape> diff --git a/app/src/main/res/drawable/ic_chevron.xml b/app/src/main/res/drawable/ic_chevron.xml new file mode 100644 index 0000000..4c416de --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@color/text_secondary" + android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_folder.xml b/app/src/main/res/drawable/ic_folder.xml new file mode 100644 index 0000000..b5b85f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_folder.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@color/text_secondary" + android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_key.xml b/app/src/main/res/drawable/ic_key.xml new file mode 100644 index 0000000..1d905df --- /dev/null +++ b/app/src/main/res/drawable/ic_key.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@color/text_secondary" + android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..c468735 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:fillColor="#23252B" + android:pathData="M0,0h108v108h-108z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..ff1dc35 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,14 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <!-- Checkmark inside the 72dp adaptive-icon safe zone. --> + <path + android:strokeColor="#FFFFFF" + android:strokeWidth="9" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:fillColor="#00000000" + android:pathData="M38,55 L49,67 L72,40" /> +</vector> diff --git a/app/src/main/res/drawable/ic_notification.xml b/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..b4309dd --- /dev/null +++ b/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <!-- White silhouette; the system tints status-bar icons by alpha. --> + <path + android:fillColor="#FFFFFF" + android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_remote.xml b/app/src/main/res/drawable/ic_remote.xml new file mode 100644 index 0000000..c81a049 --- /dev/null +++ b/app/src/main/res/drawable/ic_remote.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@color/text_secondary" + android:pathData="M19,15H5c-1.1,0 -2,0.9 -2,2v2c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-2c0,-1.1 -0.9,-2 -2,-2zM7,19c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM19,3H5c-1.1,0 -2,0.9 -2,2v2c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM7,7C6.45,7 6,6.55 6,6s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_schedule.xml b/app/src/main/res/drawable/ic_schedule.xml new file mode 100644 index 0000000..e125104 --- /dev/null +++ b/app/src/main/res/drawable/ic_schedule.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@color/text_secondary" + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" /> + <path + android:fillColor="@color/text_secondary" + android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" /> +</vector> diff --git a/app/src/main/res/layout/activity_folder.xml b/app/src/main/res/layout/activity_folder.xml new file mode 100644 index 0000000..1a15f00 --- /dev/null +++ b/app/src/main/res/layout/activity_folder.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/appRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/app_toolbar" /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="@dimen/space_m"> + + <LinearLayout + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="Name" /> + + <EditText + android:id="@+id/name" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="text" + android:hint="DCIM" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="Local path" /> + + <EditText + android:id="@+id/local" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="textUri" + android:hint="/storage/emulated/0/DCIM" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="Remote path" /> + + <EditText + android:id="@+id/remote" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="textUri" + android:hint="/backup/phone/DCIM" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="Excludes (comma-separated)" /> + + <EditText + android:id="@+id/excludes" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="text" + android:hint=".thumbnails/, .trashed/" /> + + <Switch + android:id="@+id/delete" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Mirror (delete on server when deleted locally)" /> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/save" + style="@style/Widget.Rsend.Button.Primary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginEnd="@dimen/space_s" + android:text="Save" /> + + <Button + android:id="@+id/removeFolder" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Remove" /> + </LinearLayout> + </LinearLayout> + </ScrollView> +</LinearLayout> diff --git a/app/src/main/res/layout/activity_log.xml b/app/src/main/res/layout/activity_log.xml new file mode 100644 index 0000000..af15ec2 --- /dev/null +++ b/app/src/main/res/layout/activity_log.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/appRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/app_toolbar" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="vertical" + android:padding="@dimen/space_m"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginBottom="@dimen/space_m"> + + <Button + android:id="@+id/refresh" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginEnd="@dimen/space_s" + android:text="Refresh" /> + + <Button + android:id="@+id/clear" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Clear" /> + </LinearLayout> + + <ScrollView + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <TextView + android:id="@+id/log" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Mono" + android:textSize="11sp" + android:textIsSelectable="true" /> + </ScrollView> + </LinearLayout> +</LinearLayout> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d58f907 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,413 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/appRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/app_toolbar" /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="@dimen/space_m"> + + <!-- Status hero --> + <LinearLayout + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="@style/TextAppearance.Rsend.SectionHeader" + android:text="Status" /> + + <TextView + android:id="@+id/appVersion" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <TextView + android:id="@+id/lastSync" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_s" + android:textAppearance="@style/TextAppearance.Rsend.Title" + android:textIsSelectable="true" + android:text="Last sync: never" /> + </LinearLayout> + + <!-- Primary action --> + <Button + android:id="@+id/btnSync" + style="@style/Widget.Rsend.Button.Primary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/space_m" + android:text="Sync now" /> + + <ProgressBar + android:id="@+id/syncBar" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:indeterminate="true" + android:visibility="gone" /> + + <TextView + android:id="@+id/syncStatus" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:visibility="gone" /> + + <!-- Setup --> + <LinearLayout + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/space_s" + android:textAppearance="@style/TextAppearance.Rsend.SectionHeader" + android:text="Setup" /> + + <LinearLayout + android:id="@+id/rowRemote" + style="@style/Widget.Rsend.Row" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="@dimen/space_m" + android:src="@drawable/ic_remote" + android:contentDescription="@null" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Remote" /> + + <TextView + android:id="@+id/remoteValue" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:src="@drawable/ic_chevron" + android:contentDescription="@null" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="@dimen/stroke" + android:layout_marginStart="40dp" + android:background="@color/outline" /> + + <LinearLayout + android:id="@+id/rowKey" + style="@style/Widget.Rsend.Row" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="@dimen/space_m" + android:src="@drawable/ic_key" + android:contentDescription="@null" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Identity key" /> + + <TextView + android:id="@+id/keyValue" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:src="@drawable/ic_chevron" + android:contentDescription="@null" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="@dimen/stroke" + android:layout_marginStart="40dp" + android:background="@color/outline" /> + + <LinearLayout + android:id="@+id/rowSchedule" + style="@style/Widget.Rsend.Row" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="@dimen/space_m" + android:src="@drawable/ic_schedule" + android:contentDescription="@null" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Schedule" /> + + <TextView + android:id="@+id/scheduleValue" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:src="@drawable/ic_chevron" + android:contentDescription="@null" /> + </LinearLayout> + </LinearLayout> + + <!-- Permissions --> + <LinearLayout + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/space_s" + android:textAppearance="@style/TextAppearance.Rsend.SectionHeader" + android:text="Permissions" /> + + <LinearLayout + android:id="@+id/rowAllFiles" + style="@style/Widget.Rsend.Row" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <View + android:id="@+id/dotAllFiles" + android:layout_width="10dp" + android:layout_height="10dp" + android:layout_marginStart="7dp" + android:layout_marginEnd="@dimen/space_l" + android:background="@drawable/dot_off" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="All-files access" /> + + <TextView + android:id="@+id/valAllFiles" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:src="@drawable/ic_chevron" + android:contentDescription="@null" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="@dimen/stroke" + android:layout_marginStart="40dp" + android:background="@color/outline" /> + + <LinearLayout + android:id="@+id/rowNotif" + style="@style/Widget.Rsend.Row" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <View + android:id="@+id/dotNotif" + android:layout_width="10dp" + android:layout_height="10dp" + android:layout_marginStart="7dp" + android:layout_marginEnd="@dimen/space_l" + android:background="@drawable/dot_off" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Notifications" /> + + <TextView + android:id="@+id/valNotif" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:src="@drawable/ic_chevron" + android:contentDescription="@null" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="@dimen/stroke" + android:layout_marginStart="40dp" + android:background="@color/outline" /> + + <LinearLayout + android:id="@+id/rowBattery" + style="@style/Widget.Rsend.Row" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <View + android:id="@+id/dotBattery" + android:layout_width="10dp" + android:layout_height="10dp" + android:layout_marginStart="7dp" + android:layout_marginEnd="@dimen/space_l" + android:background="@drawable/dot_off" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Battery" /> + + <TextView + android:id="@+id/valBattery" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:src="@drawable/ic_chevron" + android:contentDescription="@null" /> + </LinearLayout> + </LinearLayout> + + <!-- Folders --> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/space_s" + android:layout_marginBottom="@dimen/space_s" + android:textAppearance="@style/TextAppearance.Rsend.SectionHeader" + android:text="Folders" /> + + <LinearLayout + android:id="@+id/folders" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/btnAddFolder" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginEnd="@dimen/space_s" + android:text="+ Add folder" /> + + <Button + android:id="@+id/btnLog" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="View log" /> + </LinearLayout> + </LinearLayout> + </ScrollView> +</LinearLayout> diff --git a/app/src/main/res/layout/activity_remote.xml b/app/src/main/res/layout/activity_remote.xml new file mode 100644 index 0000000..2c3ba4b --- /dev/null +++ b/app/src/main/res/layout/activity_remote.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/appRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/app_toolbar" /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="@dimen/space_m"> + + <LinearLayout + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="Host" /> + + <EditText + android:id="@+id/host" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="textUri" + android:hint="home.example.org" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="Port" /> + + <EditText + android:id="@+id/port" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="number" + android:hint="22" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="User" /> + + <EditText + android:id="@+id/user" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="text" + android:hint="backup" /> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/test" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginEnd="@dimen/space_s" + android:text="Test connection" /> + + <Button + android:id="@+id/save" + style="@style/Widget.Rsend.Button.Primary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Save" /> + </LinearLayout> + + <TextView + android:id="@+id/status" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Mono" + android:textIsSelectable="true" /> + </LinearLayout> + </ScrollView> +</LinearLayout> diff --git a/app/src/main/res/layout/activity_schedule.xml b/app/src/main/res/layout/activity_schedule.xml new file mode 100644 index 0000000..981d6a9 --- /dev/null +++ b/app/src/main/res/layout/activity_schedule.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/appRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/app_toolbar" /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="@dimen/space_m"> + + <LinearLayout + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <Switch + android:id="@+id/enabled" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Auto-sync on a schedule" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Caption" + android:text="Interval (minutes, minimum 1)" /> + + <EditText + android:id="@+id/interval" + style="@style/Widget.Rsend.EditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:inputType="number" + android:hint="120" /> + + <Switch + android:id="@+id/wifiOnly" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="WiFi only (unmetered networks)" /> + + <Switch + android:id="@+id/charging" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/space_m" + android:textAppearance="@style/TextAppearance.Rsend.Body" + android:text="Only while charging" /> + </LinearLayout> + + <Button + android:id="@+id/save" + style="@style/Widget.Rsend.Button.Primary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Save" /> + </LinearLayout> + </ScrollView> +</LinearLayout> diff --git a/app/src/main/res/layout/app_toolbar.xml b/app/src/main/res/layout/app_toolbar.xml new file mode 100644 index 0000000..12eeb47 --- /dev/null +++ b/app/src/main/res/layout/app_toolbar.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Shared title bar. The theme is NoActionBar so each screen hosts its own + Toolbar; the dark overlay gives the title light text on the dark bar. --> +<androidx.appcompat.widget.Toolbar + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="@color/bar" + android:elevation="4dp" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" /> diff --git a/app/src/main/res/layout/dialog_key.xml b/app/src/main/res/layout/dialog_key.xml new file mode 100644 index 0000000..c2a0ed6 --- /dev/null +++ b/app/src/main/res/layout/dialog_key.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="@dimen/space_m"> + + <TextView + android:id="@+id/keyText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/space_m" + android:background="@drawable/bg_input" + android:padding="12dp" + android:textAppearance="@style/TextAppearance.Rsend.Mono" + android:textIsSelectable="true" /> + + <Button + android:id="@+id/btnCopyKey" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/space_s" + android:text="Copy public key" /> + + <Button + android:id="@+id/btnGenKey" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/space_s" + android:text="Generate new key" /> + + <Button + android:id="@+id/btnImportKey" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Import private key" /> +</LinearLayout> diff --git a/app/src/main/res/layout/item_folder.xml b/app/src/main/res/layout/item_folder.xml new file mode 100644 index 0000000..bb5a2bf --- /dev/null +++ b/app/src/main/res/layout/item_folder.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical" + android:clickable="true" + android:focusable="true" + android:foreground="?attr/selectableItemBackground"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="@dimen/space_m" + android:src="@drawable/ic_folder" + android:contentDescription="@null" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:id="@+id/folderName" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxLines="1" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.Rsend.Title" /> + + <TextView + android:id="@+id/folderPath" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxLines="1" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.Rsend.Caption" /> + </LinearLayout> + + <TextView + android:id="@+id/folderChip" + style="@style/Widget.Rsend.Chip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/space_s" /> +</LinearLayout> diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..b3e26b4 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml new file mode 100644 index 0000000..e3d2e06 --- /dev/null +++ b/app/src/main/res/values-night/colors.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Neutral / monochrome palette, dark mode. --> + <color name="background">#121315</color> + <color name="surface">#1C1D20</color> + <color name="surface_variant">#2A2B2F</color> + <color name="outline">#34363B</color> + <color name="text_primary">#ECECEE</color> + <color name="text_secondary">#A0A2A8</color> + + <color name="accent">#AEB6C7</color> + + <!-- Light fill + near-black text reads well on a dark surface. --> + <color name="button_primary_bg">#E6E7EA</color> + <color name="button_primary_text">#16171A</color> + + <color name="bar">#1C1D20</color> +</resources> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..bfcdd4e --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Neutral / monochrome palette. Light mode; values-night overrides. --> + <color name="background">#F4F4F5</color> + <color name="surface">#FFFFFF</color> + <color name="surface_variant">#ECECEE</color> + <color name="outline">#DCDCE0</color> + <color name="text_primary">#17181A</color> + <color name="text_secondary">#6A6C70</color> + + <!-- Single restrained accent: a quiet slate. Drives switches and focus. --> + <color name="accent">#3B4252</color> + + <!-- Primary button flips so it always has contrast against the surface. --> + <color name="button_primary_bg">#23252B</color> + <color name="button_primary_text">#FFFFFF</color> + + <!-- Action bar / status bar. --> + <color name="bar">#23252B</color> +</resources> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..abcba37 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="space_s">8dp</dimen> + <dimen name="space_m">16dp</dimen> + <dimen name="space_l">24dp</dimen> + + <dimen name="radius">14dp</dimen> + <dimen name="radius_pill">999dp</dimen> + + <dimen name="card_padding">16dp</dimen> + <dimen name="row_min_height">56dp</dimen> + <dimen name="stroke">1dp</dimen> +</resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..1f1e4bb --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">rsend</string> +</resources> diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..c31d4d8 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,103 @@ +<resources> + + <style name="Theme.Rsend" parent="Theme.AppCompat.DayNight.NoActionBar"> + <item name="colorPrimary">@color/bar</item> + <item name="colorPrimaryDark">@color/bar</item> + <item name="colorAccent">@color/accent</item> + <item name="colorControlActivated">@color/accent</item> + <item name="android:windowBackground">@color/background</item> + <item name="android:colorBackground">@color/background</item> + <item name="android:textColorPrimary">@color/text_primary</item> + <item name="android:textColorSecondary">@color/text_secondary</item> + <item name="android:textColorHint">@color/text_secondary</item> + </style> + + <!-- Text --> + <style name="TextAppearance.Rsend.Title" parent="TextAppearance.AppCompat"> + <item name="android:textColor">@color/text_primary</item> + <item name="android:textSize">17sp</item> + <item name="android:textStyle">bold</item> + </style> + + <style name="TextAppearance.Rsend.SectionHeader" parent="TextAppearance.AppCompat"> + <item name="android:textColor">@color/text_secondary</item> + <item name="android:textSize">12sp</item> + <item name="android:textStyle">bold</item> + <item name="android:textAllCaps">true</item> + <item name="android:letterSpacing">0.08</item> + </style> + + <style name="TextAppearance.Rsend.Body" parent="TextAppearance.AppCompat"> + <item name="android:textColor">@color/text_primary</item> + <item name="android:textSize">15sp</item> + </style> + + <style name="TextAppearance.Rsend.Caption" parent="TextAppearance.AppCompat"> + <item name="android:textColor">@color/text_secondary</item> + <item name="android:textSize">13sp</item> + </style> + + <style name="TextAppearance.Rsend.Mono" parent="TextAppearance.AppCompat"> + <item name="android:textColor">@color/text_primary</item> + <item name="android:fontFamily">monospace</item> + <item name="android:textSize">12sp</item> + </style> + + <!-- Containers --> + <style name="Widget.Rsend.Card" parent=""> + <item name="android:background">@drawable/bg_card</item> + <item name="android:padding">@dimen/card_padding</item> + <item name="android:layout_marginBottom">@dimen/space_m</item> + </style> + + <style name="Widget.Rsend.Row" parent=""> + <item name="android:orientation">horizontal</item> + <item name="android:gravity">center_vertical</item> + <item name="android:minHeight">@dimen/row_min_height</item> + <item name="android:background">?attr/selectableItemBackground</item> + <item name="android:paddingTop">@dimen/space_s</item> + <item name="android:paddingBottom">@dimen/space_s</item> + </style> + + <!-- Buttons --> + <style name="Widget.Rsend.Button.Primary" parent="Widget.AppCompat.Button"> + <item name="android:background">@drawable/bg_button_primary</item> + <item name="android:textColor">@color/button_primary_text</item> + <item name="android:textAllCaps">false</item> + <item name="android:textStyle">bold</item> + <item name="android:textSize">15sp</item> + <item name="android:minHeight">52dp</item> + <item name="android:stateListAnimator">@null</item> + </style> + + <style name="Widget.Rsend.Button.Secondary" parent="Widget.AppCompat.Button"> + <item name="android:background">@drawable/bg_button_secondary</item> + <item name="android:textColor">@color/text_primary</item> + <item name="android:textAllCaps">false</item> + <item name="android:textSize">15sp</item> + <item name="android:minHeight">48dp</item> + <item name="android:stateListAnimator">@null</item> + </style> + + <!-- Inputs --> + <style name="Widget.Rsend.EditText" parent="Widget.AppCompat.EditText"> + <item name="android:background">@drawable/bg_input</item> + <item name="android:textColor">@color/text_primary</item> + <item name="android:textColorHint">@color/text_secondary</item> + <item name="android:padding">12dp</item> + <item name="android:textSize">15sp</item> + </style> + + <!-- Chip (per-folder add/mirror tag) --> + <style name="Widget.Rsend.Chip" parent=""> + <item name="android:background">@drawable/bg_chip</item> + <item name="android:textColor">@color/text_secondary</item> + <item name="android:textSize">11sp</item> + <item name="android:textAllCaps">true</item> + <item name="android:paddingLeft">10dp</item> + <item name="android:paddingRight">10dp</item> + <item name="android:paddingTop">3dp</item> + <item name="android:paddingBottom">3dp</item> + </style> + +</resources> |