First commit
This commit is contained in:
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@@ -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/
|
||||||
126
AGENTS.md
Normal file
126
AGENTS.md
Normal file
@@ -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.
|
||||||
5
LICENSE
Normal file
5
LICENSE
Normal file
@@ -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.
|
||||||
38
README.md
Normal file
38
README.md
Normal file
@@ -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`.
|
||||||
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
54
app/build.gradle.kts
Normal file
54
app/build.gradle.kts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
1
app/proguard-rules.pro
vendored
Normal file
1
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Project-specific ProGuard rules.
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/src/main/AndroidManifest.xml
Normal file
22
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.Kanbn4Droid">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app/src/main/res/layout/activity_main.xml
Normal file
18
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/titleText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
5
app/src/main/res/values-night/themes.xml
Normal file
5
app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="Base.Theme.Kanbn4Droid" parent="Theme.MaterialComponents.DayNight.NoActionBar" />
|
||||||
|
</resources>
|
||||||
4
app/src/main/res/values/strings.xml
Normal file
4
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Kanbn4Droid</string>
|
||||||
|
</resources>
|
||||||
7
app/src/main/res/values/themes.xml
Normal file
7
app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="Base.Theme.Kanbn4Droid" parent="Theme.MaterialComponents.DayNight.NoActionBar" />
|
||||||
|
|
||||||
|
<style name="Theme.Kanbn4Droid" parent="Base.Theme.Kanbn4Droid" />
|
||||||
|
</resources>
|
||||||
2
app/src/main/res/xml/backup_rules.xml
Normal file
2
app/src/main/res/xml/backup_rules.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<full-backup-content />
|
||||||
5
app/src/main/res/xml/data_extraction_rules.xml
Normal file
5
app/src/main/res/xml/data_extraction_rules.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup />
|
||||||
|
<device-transfer />
|
||||||
|
</data-extraction-rules>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package space.hackenslacker.kanbn4droid.app
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun additionIsCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
build.gradle.kts
Normal file
4
build.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application) apply false
|
||||||
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
|
}
|
||||||
4
gradle.properties
Normal file
4
gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
|
||||||
|
org.gradle.daemon=true
|
||||||
|
android.useAndroidX=true
|
||||||
|
kotlin.code.style=official
|
||||||
23
gradle/libs.versions.toml
Normal file
23
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[versions]
|
||||||
|
agp = "8.9.2"
|
||||||
|
kotlin = "2.1.20"
|
||||||
|
coreKtx = "1.16.0"
|
||||||
|
appcompat = "1.7.0"
|
||||||
|
material = "1.12.0"
|
||||||
|
constraintlayout = "2.2.1"
|
||||||
|
junit = "4.13.2"
|
||||||
|
androidxJunit = "1.2.1"
|
||||||
|
espressoCore = "3.6.1"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
|
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||||
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" }
|
||||||
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
248
gradlew
vendored
Executable file
248
gradlew
vendored
Executable file
@@ -0,0 +1,248 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
93
gradlew.bat
vendored
Normal file
93
gradlew.bat
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
3
local.properties.example
Normal file
3
local.properties.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Copy this file to local.properties and set your local Android SDK path.
|
||||||
|
# The SDK is also discoverable if configured in your environment.
|
||||||
|
sdk.dir=/path/to/android/sdk
|
||||||
18
settings.gradle.kts
Normal file
18
settings.gradle.kts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "Kanbn4Droid"
|
||||||
|
include(":app")
|
||||||
Reference in New Issue
Block a user