diff options
28 files changed, 468 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1785407 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +app/build/ +app/.gradle/ +app/.home/ diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 0000000..d2174f7 --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,22 @@ +# Build image for mygram-droid: JDK 17, Android SDK 34, Gradle. +# Used by build.sh; nothing is installed on the host. +# +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License version 2 only. +FROM eclipse-temurin:17-jdk-jammy + +ENV ANDROID_HOME=/opt/android-sdk \ + GRADLE_VERSION=8.7 +ENV PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:/opt/gradle-$GRADLE_VERSION/bin + +RUN apt-get update && apt-get install -y --no-install-recommends unzip curl \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL -o /tmp/tools.zip https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip \ + && mkdir -p $ANDROID_HOME/cmdline-tools \ + && unzip -q /tmp/tools.zip -d $ANDROID_HOME/cmdline-tools \ + && mv $ANDROID_HOME/cmdline-tools/cmdline-tools $ANDROID_HOME/cmdline-tools/latest \ + && curl -fsSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip \ + && unzip -q /tmp/gradle.zip -d /opt \ + && rm /tmp/*.zip \ + && yes | sdkmanager --licenses >/dev/null \ + && sdkmanager "platforms;android-34" "build-tools;34.0.0" "platform-tools" >/dev/null diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..bad1efb --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,37 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// Licensed under the GNU General Public License version 2 only. +plugins { + id 'com.android.application' version '8.5.2' +} + +android { + namespace 'mygram.droid' + compileSdk 34 + + defaultConfig { + applicationId 'mygram.droid' + minSdk 34 + targetSdk 34 + versionCode 1 + versionName '0.1' + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + testOptions { + unitTests.returnDefaultValues = true + } +} + +dependencies { + implementation 'androidx.recyclerview:recyclerview:1.3.2' + implementation 'com.github.bumptech.glide:glide:4.16.0' + implementation 'androidx.media3:media3-exoplayer:1.4.1' + implementation 'androidx.media3:media3-ui:1.4.1' + testImplementation 'junit:junit:4.13.2' + // Android's org.json is a stub on the JVM; tests need the real one. + testImplementation 'org.json:json:20240303' +} diff --git a/app/gradle.properties b/app/gradle.properties new file mode 100644 index 0000000..c335529 --- /dev/null +++ b/app/gradle.properties @@ -0,0 +1,2 @@ +android.useAndroidX=true +org.gradle.jvmargs=-Xmx2g diff --git a/app/settings.gradle b/app/settings.gradle new file mode 100644 index 0000000..c70619b --- /dev/null +++ b/app/settings.gradle @@ -0,0 +1,16 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// Licensed under the GNU General Public License version 2 only. +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + } +} +rootProject.name = 'mygram-droid' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9918188 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2026 Danilo M. <danix@danix.xyz> + Licensed under the GNU General Public License version 2 only. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-feature android:name="android.software.leanback" android:required="true" /> + <uses-feature android:name="android.hardware.touchscreen" android:required="false" /> + + <!-- Cleartext: Foldergram runs on the LAN over plain HTTP. + No backup: the viewer password lives in app preferences. --> + <application + android:name=".App" + android:allowBackup="false" + android:banner="@drawable/banner" + android:icon="@drawable/ic_folder" + android:label="mygram" + android:theme="@android:style/Theme.DeviceDefault.NoActionBar" + android:usesCleartextTraffic="true"> + <activity android:name=".MainActivity" android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LEANBACK_LAUNCHER" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name=".SetupActivity" /> + <activity android:name=".SettingsActivity" /> + <activity android:name=".FolderActivity" /> + <activity android:name=".ViewerActivity" /> + </application> +</manifest> diff --git a/app/src/main/java/mygram/droid/App.java b/app/src/main/java/mygram/droid/App.java new file mode 100644 index 0000000..ffbdd8d --- /dev/null +++ b/app/src/main/java/mygram/droid/App.java @@ -0,0 +1,32 @@ +/* + * mygram-droid: Android TV client for Foldergram. + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package mygram.droid; + +import android.app.Application; + +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.CookiePolicy; + +public final class App extends Application { + @Override + public void onCreate() { + super.onCreate(); + Prefs.init(this); + // One cookie jar for the Foldergram session: Api, Glide and ExoPlayer + // all use HttpURLConnection, which consults the default handler. + CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL)); + } +} diff --git a/app/src/main/java/mygram/droid/FolderActivity.java b/app/src/main/java/mygram/droid/FolderActivity.java new file mode 100644 index 0000000..d53588a --- /dev/null +++ b/app/src/main/java/mygram/droid/FolderActivity.java @@ -0,0 +1,20 @@ +/* + * mygram-droid: Android TV client for Foldergram. + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package mygram.droid; + +import android.app.Activity; + +public final class FolderActivity extends Activity { +} diff --git a/app/src/main/java/mygram/droid/MainActivity.java b/app/src/main/java/mygram/droid/MainActivity.java new file mode 100644 index 0000000..767dbb1 --- /dev/null +++ b/app/src/main/java/mygram/droid/MainActivity.java @@ -0,0 +1,20 @@ +/* + * mygram-droid: Android TV client for Foldergram. + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package mygram.droid; + +import android.app.Activity; + +public final class MainActivity extends Activity { +} diff --git a/app/src/main/java/mygram/droid/Prefs.java b/app/src/main/java/mygram/droid/Prefs.java new file mode 100644 index 0000000..33ce878 --- /dev/null +++ b/app/src/main/java/mygram/droid/Prefs.java @@ -0,0 +1,60 @@ +/* + * mygram-droid: Android TV client for Foldergram. + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package mygram.droid; + +import android.content.Context; +import android.content.SharedPreferences; + +/** Settings stored on the device. */ +final class Prefs { + private static SharedPreferences prefs; + + private Prefs() { + } + + static void init(Context c) { + prefs = c.getSharedPreferences("mygram", Context.MODE_PRIVATE); + } + + /** Base URL like "http://192.0.2.10:4141", or null before setup. */ + static String server() { + return prefs.getString("server", null); + } + + /** Viewer password; empty for a public instance. */ + static String password() { + return prefs.getString("password", ""); + } + + static int photoSeconds() { + return prefs.getInt("photo", 8); + } + + static int backgroundSeconds() { + return prefs.getInt("background", 15); + } + + static void setServer(String base, String password) { + prefs.edit().putString("server", base).putString("password", password).apply(); + } + + static void setPassword(String password) { + prefs.edit().putString("password", password).apply(); + } + + static void setTimes(int photo, int background) { + prefs.edit().putInt("photo", photo).putInt("background", background).apply(); + } +} diff --git a/app/src/main/java/mygram/droid/SettingsActivity.java b/app/src/main/java/mygram/droid/SettingsActivity.java new file mode 100644 index 0000000..776e48b --- /dev/null +++ b/app/src/main/java/mygram/droid/SettingsActivity.java @@ -0,0 +1,20 @@ +/* + * mygram-droid: Android TV client for Foldergram. + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package mygram.droid; + +import android.app.Activity; + +public final class SettingsActivity extends Activity { +} diff --git a/app/src/main/java/mygram/droid/SetupActivity.java b/app/src/main/java/mygram/droid/SetupActivity.java new file mode 100644 index 0000000..ef1f68c --- /dev/null +++ b/app/src/main/java/mygram/droid/SetupActivity.java @@ -0,0 +1,20 @@ +/* + * mygram-droid: Android TV client for Foldergram. + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package mygram.droid; + +import android.app.Activity; + +public final class SetupActivity extends Activity { +} diff --git a/app/src/main/java/mygram/droid/ViewerActivity.java b/app/src/main/java/mygram/droid/ViewerActivity.java new file mode 100644 index 0000000..9a3be55 --- /dev/null +++ b/app/src/main/java/mygram/droid/ViewerActivity.java @@ -0,0 +1,20 @@ +/* + * mygram-droid: Android TV client for Foldergram. + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +package mygram.droid; + +import android.app.Activity; + +public final class ViewerActivity extends Activity { +} diff --git a/app/src/main/res/drawable/banner.xml b/app/src/main/res/drawable/banner.xml new file mode 100644 index 0000000..e1a075a --- /dev/null +++ b/app/src/main/res/drawable/banner.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2026 Danilo M. <danix@danix.xyz> + Licensed under the GNU General Public License version 2 only. --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape> + <solid android:color="#FF4F46E5" /> + <size android:width="320dp" android:height="180dp" /> + </shape> + </item> + <item android:drawable="@drawable/ic_folder" android:gravity="center" + android:width="96dp" android:height="96dp" /> +</layer-list> diff --git a/app/src/main/res/drawable/focus_ring.xml b/app/src/main/res/drawable/focus_ring.xml new file mode 100644 index 0000000..1cab3f8 --- /dev/null +++ b/app/src/main/res/drawable/focus_ring.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2026 Danilo M. <danix@danix.xyz> + Licensed under the GNU General Public License version 2 only. --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_focused="true"> + <shape> + <corners android:radius="8dp" /> + <stroke android:width="3dp" android:color="#FFFFFFFF" /> + </shape> + </item> + <item> + <shape> + <corners android:radius="8dp" /> + <solid android:color="#00000000" /> + </shape> + </item> +</selector> diff --git a/app/src/main/res/drawable/ic_arrow_forward.xml b/app/src/main/res/drawable/ic_arrow_forward.xml new file mode 100644 index 0000000..4b3cf6d --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_forward.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "arrow_forward", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 0000000..ef81024 --- /dev/null +++ b/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "check", Apache License 2.0 --> +<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="#FFFFFFFF" 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_edit.xml b/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 0000000..35b45bb --- /dev/null +++ b/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "edit", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_favorite.xml b/app/src/main/res/drawable/ic_favorite.xml new file mode 100644 index 0000000..ccd588f --- /dev/null +++ b/app/src/main/res/drawable/ic_favorite.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "favorite", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z" /> +</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..16c7032 --- /dev/null +++ b/app/src/main/res/drawable/ic_folder.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "folder", Apache License 2.0 --> +<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="#FFFFFFFF" 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_home.xml b/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 0000000..5503d0c --- /dev/null +++ b/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "home", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_pause.xml b/app/src/main/res/drawable/ic_pause.xml new file mode 100644 index 0000000..1a87d2a --- /dev/null +++ b/app/src/main/res/drawable/ic_pause.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "pause", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M6,19h4V5H6v14zM14,5v14h4V5h-4z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_reels.xml b/app/src/main/res/drawable/ic_reels.xml new file mode 100644 index 0000000..f4dda6d --- /dev/null +++ b/app/src/main/res/drawable/ic_reels.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "reels", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zM20,2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM12,14.5v-9l6,4.5 -6,4.5z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 0000000..41fa710 --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "refresh", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..2afd04d --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "settings", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_swap.xml b/app/src/main/res/drawable/ic_swap.xml new file mode 100644 index 0000000..ba3591d --- /dev/null +++ b/app/src/main/res/drawable/ic_swap.xml @@ -0,0 +1,6 @@ +<!-- Material Icons "swap", Apache License 2.0 --> +<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="#FFFFFFFF" android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z" /> +</vector> diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..154e32d --- /dev/null +++ b/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# build.sh: build the debug APK in a throwaway Docker container, or run the +# JVM unit tests with "./build.sh test". Nothing is installed on the host. +# Output: app/build/outputs/apk/debug/mygram-droid-debug.apk +# +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. + +set -euo pipefail +cd "$(dirname "$0")" + +task=assembleDebug +[ "${1:-}" = test ] && task=testDebugUnitTest + +docker image inspect mygram-droid-build >/dev/null 2>&1 || + docker build -t mygram-droid-build app + +# Gradle and Android caches live in app/.home so files stay user-owned. +mkdir -p app/.home +docker run --rm --user "$(id -u):$(id -g)" -e HOME=/src/.home -e JAVA_TOOL_OPTIONS=-Duser.home=/src/.home \ + -v "$PWD/app:/src" -w /src mygram-droid-build \ + gradle --no-daemon -q "$task" +if [ "$task" = assembleDebug ]; then + ls -l app/build/outputs/apk/debug/*.apk +fi diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..41d546d --- /dev/null +++ b/deploy.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# deploy.sh: build the APK, install it on the TV over wireless adb and +# launch it. Finds the TV through its _adb-tls-connect mDNS record when no +# device is connected; wireless debugging must be on (it turns off on reboot). +# +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. + +set -euo pipefail +cd "$(dirname "$0")" + +pkg=mygram.droid +apk=app/build/outputs/apk/debug/mygram-droid-debug.apk + +if ! adb get-state >/dev/null 2>&1; then + addr=$(avahi-browse -rtp _adb-tls-connect._tcp 2>/dev/null | + awk -F';' '/^=/ && $3 == "IPv4" {print $8 ":" $9; exit}') + [ -n "$addr" ] || { echo "no device: set ANDROID_SERIAL or enable wireless debugging" >&2; exit 1; } + adb connect "$addr" >/dev/null + export ANDROID_SERIAL=$addr +fi + +./build.sh +adb install -r "$apk" +adb shell -n am start -n "$pkg/.MainActivity" >/dev/null +echo "deployed to ${ANDROID_SERIAL:-$(adb get-serialno)}" |
