From 3605f4e081283b2f32451c7075fe07648e76e99d Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 22 Oct 2026 11:08:09 +0200 Subject: Add Android project skeleton and Docker build Co-Authored-By: Claude Opus 5.5 --- .gitignore | 3 ++ app/Dockerfile | 22 ++++++++ app/build.gradle | 37 +++++++++++++ app/gradle.properties | 2 + app/settings.gradle | 16 ++++++ app/src/main/AndroidManifest.xml | 33 ++++++++++++ app/src/main/java/mygram/droid/App.java | 32 ++++++++++++ app/src/main/java/mygram/droid/FolderActivity.java | 20 ++++++++ app/src/main/java/mygram/droid/MainActivity.java | 20 ++++++++ app/src/main/java/mygram/droid/Prefs.java | 60 ++++++++++++++++++++++ .../main/java/mygram/droid/SettingsActivity.java | 20 ++++++++ app/src/main/java/mygram/droid/SetupActivity.java | 20 ++++++++ app/src/main/java/mygram/droid/ViewerActivity.java | 20 ++++++++ app/src/main/res/drawable/banner.xml | 13 +++++ app/src/main/res/drawable/focus_ring.xml | 17 ++++++ app/src/main/res/drawable/ic_arrow_forward.xml | 6 +++ app/src/main/res/drawable/ic_check.xml | 6 +++ app/src/main/res/drawable/ic_edit.xml | 6 +++ app/src/main/res/drawable/ic_favorite.xml | 6 +++ app/src/main/res/drawable/ic_folder.xml | 6 +++ app/src/main/res/drawable/ic_home.xml | 6 +++ app/src/main/res/drawable/ic_pause.xml | 6 +++ app/src/main/res/drawable/ic_reels.xml | 6 +++ app/src/main/res/drawable/ic_refresh.xml | 6 +++ app/src/main/res/drawable/ic_settings.xml | 6 +++ app/src/main/res/drawable/ic_swap.xml | 6 +++ build.sh | 33 ++++++++++++ deploy.sh | 34 ++++++++++++ 28 files changed, 468 insertions(+) create mode 100644 .gitignore create mode 100644 app/Dockerfile create mode 100644 app/build.gradle create mode 100644 app/gradle.properties create mode 100644 app/settings.gradle create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/mygram/droid/App.java create mode 100644 app/src/main/java/mygram/droid/FolderActivity.java create mode 100644 app/src/main/java/mygram/droid/MainActivity.java create mode 100644 app/src/main/java/mygram/droid/Prefs.java create mode 100644 app/src/main/java/mygram/droid/SettingsActivity.java create mode 100644 app/src/main/java/mygram/droid/SetupActivity.java create mode 100644 app/src/main/java/mygram/droid/ViewerActivity.java create mode 100644 app/src/main/res/drawable/banner.xml create mode 100644 app/src/main/res/drawable/focus_ring.xml create mode 100644 app/src/main/res/drawable/ic_arrow_forward.xml create mode 100644 app/src/main/res/drawable/ic_check.xml create mode 100644 app/src/main/res/drawable/ic_edit.xml create mode 100644 app/src/main/res/drawable/ic_favorite.xml create mode 100644 app/src/main/res/drawable/ic_folder.xml create mode 100644 app/src/main/res/drawable/ic_home.xml create mode 100644 app/src/main/res/drawable/ic_pause.xml create mode 100644 app/src/main/res/drawable/ic_reels.xml create mode 100644 app/src/main/res/drawable/ic_refresh.xml create mode 100644 app/src/main/res/drawable/ic_settings.xml create mode 100644 app/src/main/res/drawable/ic_swap.xml create mode 100755 build.sh create mode 100755 deploy.sh 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. +# 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. +// 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. +// 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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. + * + * 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. + * + * 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. + * + * 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. + * + * 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. + * + * 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. + * + * 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. + * + * 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 @@ + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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 @@ + + + + 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. +# +# 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. +# +# 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)}" -- cgit v1.2.3