From 75e9ad1da23db4c1c236149fb9c0a0e4ba727977 Mon Sep 17 00:00:00 2001 From: Wally Hackenslacker Date: Sun, 15 Mar 2026 19:24:04 -0400 Subject: [PATCH] First commit --- .gitignore | 25 ++ AGENTS.md | 126 +++++++++ LICENSE | 5 + README.md | 38 +++ app/.gitignore | 1 + app/build.gradle.kts | 54 ++++ app/proguard-rules.pro | 1 + .../app/ExampleInstrumentedTest.kt | 16 ++ app/src/main/AndroidManifest.xml | 22 ++ .../kanbn4droid/app/MainActivity.kt | 11 + app/src/main/res/layout/activity_main.xml | 18 ++ app/src/main/res/values-night/themes.xml | 5 + app/src/main/res/values/strings.xml | 4 + app/src/main/res/values/themes.xml | 7 + app/src/main/res/xml/backup_rules.xml | 2 + .../main/res/xml/data_extraction_rules.xml | 5 + .../kanbn4droid/app/ExampleUnitTest.kt | 11 + build.gradle.kts | 4 + gradle.properties | 4 + gradle/libs.versions.toml | 23 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 46175 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 248 ++++++++++++++++++ gradlew.bat | 93 +++++++ local.properties.example | 3 + settings.gradle.kts | 18 ++ 26 files changed, 751 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/space/hackenslacker/kanbn4droid/app/MainActivity.kt create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/values-night/themes.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/test/java/space/hackenslacker/kanbn4droid/app/ExampleUnitTest.kt create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 local.properties.example create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..807bf56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +.gradle/ +build/ +local.properties +*.iml +.idea/ + +# Android +**/build/ +captures/ +.externalNativeBuild/ +.cxx/ + +# Linux +*~ +.directory +.Trash-* +.fuse_hidden* +.nfs* + +# Kate +*.kate-swp +*.swp +*.swo +.kateproject +.kateproject.d/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..5487318 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,126 @@ +# AGENTS.md + +This file provides guidance to AI coding agents when working with code in this repository. + +## Project Overview + +Kanbn4Droid is an unofficial app to connect to and manipulate data stored in self-hosted Kan.bn repositories. The app allows the user to authenticate with a Kan.bn server, display a list of available boards, open boards to edit lists and move cards around, and to create and edit cards. The app is written in Kotlin using mostly standard Android libraries and components and is designed to be compiled and edited through the command line, without Android Studio. + +## Dependencies + +- AndroidX Preferences library. + +## Current bootstrap status + +- Build system: Gradle Kotlin DSL with version catalog (`gradle/libs.versions.toml`). +- Wrapper: Gradle 8.11.1. +- Android plugin and language: AGP 8.9.2, Kotlin 2.1.20, Java 17 target. +- Module layout: single Android app module at `app/`. +- Namespace and application id: `space.hackenslacker.kanbn4droid.app`. +- Minimum SDK: API 29. +- Compile/target SDK: API 35. +- Baseline tests: + - JVM unit test in `app/src/test/`. + - Instrumentation smoke test in `app/src/androidTest/`. + +## Command-line workflow + +- List tasks: `./gradlew tasks` +- Run unit tests: `./gradlew test` +- Build debug APK: `./gradlew assembleDebug` +- Install debug APK: `./gradlew installDebug` +- Run instrumentation tests: `./gradlew connectedDebugAndroidTest` + +`installDebug` and `connectedDebugAndroidTest` require a connected device or emulator. + +## Architecture + +**Splash screen** +- The app displays a standard Android splash screen when open from a cold start. + +**Login view** +- It's the first screen the user sees when opening the app if no login has been successfully stored so far. +- The view has fields to request the user for an instance base URL (http or https, with or without port number) and an API key. +- The default base URL is that of the standard Kan.bn instance at https://kan.bn/ +- The view tries to check connection with the server at the given base URL using the Kan.bn API healthcheck endpoint. +- The API key is managed using Android's own Credential Manager. +- On success, the view stores the URL and API key pair in preferences and moves over to the boards view. +- If there is a URL and API Key pair stored, the view tries to authenticate the user through the API automatically and proceeds to the boards view instantly without showing the login screen if successful. + +**Boards list view** +- Displays a list of boards as rounded-square cards with the board's title centered in it. +- Clicking on a board card moves on to that board's detail view. +- The boards list is refreshed automatically when entering the view. +- The boards list can be refreshed manually with a pull-down gesture on the list. +- The view has a floating + button at the bottom right that shows a modal dialog for creating a new board. + - The modal board creation dialog requests the user for a board name. + - The modal board creation dialog has a toggleable pill button labeled "Use template". + - Enabling the "Use template" button shows a list of available templates to use when creating the board. + - The list of templates is obtained through the Kan.bn API. + - The modal board creation dialog has two buttons at the bottom right for "Cancel" and "Create" respectively. +- Board creation is done through the Kan.bn API. +- On success creating a board moves on to that new board's detal view immediately. +- On failure creating a board shows a modal dialog with the server's reported cause of failure and an OK button. +- Long-pressing an existing board shows a modal dialog asking the user if they want to delete the board, with buttons for "Cancel" and "Delete". + - Pressing "Delete" in the modal dialog MUST show a second confirmation modal asking if the user is sure, with buttons for "Cancel" and "I'm sure". + - Only on pressing "I'm sure" in the second confirmation modal dialog should a board delete request be sent to the API. + +**Board detail view** + +- The board detail view shows the lists in the board as vertical lists. +- Each list has it's title at the top. + - Clicking on the title of a list allows editing the title. +- Below the title each card is shown in a vertically scrolling list of rounded-square shaped cards with one entry for each card in the list. + - Each card contains it's title in bold font at the top, a list of the card's tags and it's due date if available, in that order. + - The tag list of a card is a series of pill-shaped labels with the tag's name on them and a colored border with the color of the tag. + - The due date of the card is shown in black (or light in dark mode) text if it's still valid, or in red text if it's expired. + - The due date MUST be formatted in the system's locale. +- Swiping right or left on a list allows the user to change to the next or previous list in the board respectively if any. + - On reaching the first or last list in the board it's no longer possible to keep swiping in that direction. +- Long-pressing a card in a list allows selecting that card. + - Tapping other cards in the same or other lists selects them as well. + - Tapping an already selected card deselects it. + - When one or more cards are selected, the top bar of the application MUST display the following buttons using the indicated icon for each one, without text: "Select all" (a 4x4 square grid), "Move cards" (a double-ended left-right arrow), "Delete cards" (a trash can). + - Tapping the "Select all" button selects all cards in the list that is being shown to the user, NOT all cards in all available lists. + - Tapping the "Move cards" button shows a modal dialog that asks the user to what list does he want to move the cards. + - This modal dialog shows a selector with all the lists available in the current board. + - This modal dialog has two buttons at the bottom for "Cancel" and "Move" + - Tapping the "Delete cards" button asks the user for confirmation to delete the cards. This confirmation dialog has two buttons at the bottom for "Cancel" and "Delete". + - Pressing "Delete" in the modal dialog MUST show a second confirmation modal asking if the user is sure, with buttons for "Cancel" and "I'm sure". + - Only on pressing "I'm sure" in the second confirmation modal dialog should a board delete request be sent to the API. + - Long-pressing any of the buttons must show a tooltip with the button name. + +**Card detail view** +- The view shows the card's title in bold letters. Tapping on the card's title allows editing it. +- Below the title a horizontally scrollable there is a list of the card's tags, shown as pills with the border color set to the tag's color obtained from the Kan.bn API. +- Below the tags there is a date field showing the card's due date, if any, or the option to set a due date. + - If the due date is set and is still valid then it is shown in black (or light in dark mode) text. + - If the due date is set and is expired, then it is shown in red text. +- Below the due date the view shows the card's description if any in a markdown-enabled, editable text field. +- Below the description the app shows the lastest 10 elements of the card's edit history (named card activities in the Kan.bn documentation) obtained from the Kan.bn API, in least to most recent order. +- The view has a floating + button that shows a modal dialog that allows adding a comment to the card's history using the Kan.bn API. + - The modal dialog has "Add comment" as a title. + - The modal dialog has an editable markdown-enabled text field for the comment. + - The modal dialog has two buttons at the bottom on the right side for "Cancel" and "Add" respectively. + +**Settings view** +- The view shows a list of settings that can be changed by the user. The following settings are available: + - Theme (selector with three choices: Light, Dark, Follow System) + - Base URL (editable text field) + - API Key (editable password text field with obfuscated characters) + - Logout (single button that shows a modal dialog asking the user if it's OK to sign out) +- All settings are managed using the AndroidX Preferences library. +- Signing out clears the stored API key and app cache and returns the user to the login screen. + +## Considerations + +- All views support light or dark theme following the system's indicated preference by default. +- The app's default accent color is obtained from the system's theme. +- The app's design follows Material You guidelines. +- Every new feature MUST include a full set of tests, using the standard Android SDK testing framework. +- The minimum supported Android version is Android 10 (API level 29) +- It is preferable to use standard Android libraries and tools whenever possible. +- If a task would be better served by or can only be done with external libraries then it is allowed to use them but you MUST ask the user for permission to add the library to the project first. +- The documentation for the Kan.bn API is available here https://docs.kan.bn/api-reference/introduction +- Never commit or push code unless explicitely prompted to do so. +- After every run, update this file as needed to reflect the changes made. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..984f15b --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Copyright (C) 2026 by WallyHackenslacker wallyhackenslacker@noreply.git.hackenslacker.space + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8adf4fd --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Kanbn4Droid + +An unofficial, totally vibe-coded app for interacting with self-hosted Kan.bn instances. + +## CLI setup + +This project is designed to be developed fully from the command line. + +### Requirements + +- Java 17 +- Android SDK (with command-line tools) +- Android SDK packages: + - `platforms;android-35` + - `build-tools;35.0.0` + - `platform-tools` + +If needed, install packages with: + +```bash +sdkmanager "platforms;android-35" "build-tools;35.0.0" "platform-tools" +``` + +If your SDK is not auto-detected, create `local.properties` from `local.properties.example` and set `sdk.dir`. + +### Common commands + +- List tasks: `./gradlew tasks` +- Clean: `./gradlew clean` +- Run JVM tests: `./gradlew test` +- Build debug APK: `./gradlew assembleDebug` +- Install on device/emulator: `./gradlew installDebug` +- Run instrumentation tests: `./gradlew connectedDebugAndroidTest` + +### Notes + +- `installDebug` and `connectedDebugAndroidTest` require a connected emulator/device. +- The app namespace and application id are `space.hackenslacker.kanbn4droid.app`. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..2354485 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,54 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "space.hackenslacker.kanbn4droid.app" + compileSdk = 35 + + defaultConfig { + applicationId = "space.hackenslacker.kanbn4droid.app" + minSdk = 29 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.constraintlayout) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..69dd36c --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1 @@ +# Project-specific ProGuard rules. diff --git a/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/ExampleInstrumentedTest.kt b/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..1b9d833 --- /dev/null +++ b/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/ExampleInstrumentedTest.kt @@ -0,0 +1,16 @@ +package space.hackenslacker.kanbn4droid.app + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("space.hackenslacker.kanbn4droid.app", appContext.packageName) + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4b32981 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/java/space/hackenslacker/kanbn4droid/app/MainActivity.kt b/app/src/main/java/space/hackenslacker/kanbn4droid/app/MainActivity.kt new file mode 100644 index 0000000..ed69202 --- /dev/null +++ b/app/src/main/java/space/hackenslacker/kanbn4droid/app/MainActivity.kt @@ -0,0 +1,11 @@ +package space.hackenslacker.kanbn4droid.app + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity + +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + } +} 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..0b9e209 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..024366a --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,5 @@ + + + +