Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gradlew text eol=lf
gradlew.bat text eol=crlf
67 changes: 67 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Android

on:
pull_request:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: android-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build, unit tests, and lint
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0
with:
distribution: temurin
java-version: '21'

- uses: android-actions/setup-android@be39fa834029ff78f1a44aa3bb0819b8fc2bd8fd # v4.0.4
with:
cmdline-tools-version: '16111833'
packages: 'platform-tools platforms;android-37.0 build-tools;36.0.0'
log-accepted-android-sdk-licenses: false

- uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-provider: basic

- name: Check metadata, build, test, and lint
run: >-
./gradlew --no-daemon --console=plain
:app:checkDebugAarMetadata
:app:assembleDebug
:app:assembleDebugAndroidTest
:app:testDebugUnitTest
:app:lintDebug

- name: Upload reports
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: android-reports
path: |
app/build/reports/
app/build/test-results/
if-no-files-found: warn
retention-days: 14

- name: Upload debug APK
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: acetimetracker-debug
path: app/build/outputs/apk/debug/app-debug.apk
if-no-files-found: error
retention-days: 14
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.iml
.gradle
/.kotlin
/local.properties
/.idea/caches
/.idea/libraries
Expand Down
4 changes: 2 additions & 2 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ To get started with TimeTracker, follow these steps:

1. Clone the repository to your local machine.
2. Open the project in Android Studio.
3. Build and run the application on an Android device or emulator.
3. Follow the [Android development guide](docs/development.md) to configure JDK 21,
install the pinned SDK packages, and run the build, tests, and lint checks.
Debug builds use a separate demo Firebase configuration; see the guide before
testing account or cloud features.

## Usage

Expand Down
90 changes: 47 additions & 43 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,73 +1,77 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.gms.google-services")
alias(libs.plugins.android.application)
alias(libs.plugins.google.services)
}

android {
namespace = "com.example.acetimetracker"
compileSdk = 34
compileSdk = 37
buildToolsVersion = "36.0.0"

defaultConfig {
applicationId = "com.example.acetimetracker"
minSdk = 27
targetSdk = 34
targetSdk = 36
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
debug {
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
}
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

buildFeatures {
viewBinding = true
}
}

dependencies {

implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("pl.droidsonroids.gif:android-gif-drawable:1.2.28")
implementation("com.google.firebase:firebase-auth:23.0.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.1")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
implementation("androidx.annotation:annotation:1.7.1")
implementation("com.google.firebase:firebase-database:21.0.0")
implementation("com.google.firebase:firebase-firestore:25.0.0")
implementation("com.google.firebase:firebase-storage:21.0.0")
implementation ("com.github.bumptech.glide:glide:4.16.0")
implementation ("com.github.PhilJay:MPAndroidChart:v3.1.0")
implementation ("com.airbnb.android:lottie:5.2.0")
dependencies {
implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha13")
// To use constraintlayout in compose
lint {
abortOnError = true
}

implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13")
testOptions {
unitTests.isIncludeAndroidResources = true
unitTests.all {
// Robolectric's API 36 shared-memory setup accesses JDK FileDescriptor internals.
it.jvmArgs("--add-opens=java.base/jdk.internal.access=ALL-UNNAMED")
}
}
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.constraintlayout)
implementation(libs.gif.drawable)
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.auth)
implementation(libs.firebase.firestore)
implementation(libs.firebase.storage)
implementation(libs.glide)
implementation(libs.mpandroidchart)
implementation(libs.lottie)

}
testImplementation(libs.junit)
testImplementation(libs.robolectric)
androidTestImplementation(libs.androidx.test.junit)
androidTestImplementation(libs.androidx.test.espresso)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.acetimetracker", appContext.packageName)
assertEquals("com.example.acetimetracker.debug", appContext.packageName)
}
}
}
19 changes: 19 additions & 0 deletions app/src/debug/google-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"project_info": {
"project_number": "123456789012",
"project_id": "demo-acetimetracker",
"storage_bucket": "demo-acetimetracker.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:123456789012:android:0000000000000000000000",
"android_client_info": {
"package_name": "com.example.acetimetracker.debug"
}
},
"api_key": [{ "current_key": "debug-placeholder-not-a-production-api-key" }]
}
],
"configuration_version": "1"
}
10 changes: 2 additions & 8 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.acetimetracker">
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Expand Down Expand Up @@ -42,11 +41,6 @@
<activity
android:name=".settings"
android:exported="false" />
<activity
android:name=".timetracker"
android:exported="false"
android:label="@string/title_activity_timetracker"
android:theme="@style/Widget.AppCompat.PopupMenu" />
<activity
android:name=".Registration"
android:exported="false" />
Expand All @@ -68,4 +62,4 @@
</activity>
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.acetimetracker

import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.core.view.GravityCompat
import androidx.core.view.doOnLayout
import androidx.drawerlayout.widget.DrawerLayout

/** Close the navigation drawer on Back; let the activity handle Back when closed. */
fun ComponentActivity.handleDrawerBack(drawer: DrawerLayout) {
val callback = object : OnBackPressedCallback(drawer.isDrawerVisible(GravityCompat.START)) {
override fun handleOnBackPressed() {
drawer.closeDrawer(GravityCompat.START)
}
}
drawer.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() {
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
callback.isEnabled = slideOffset > 0f
}

override fun onDrawerOpened(drawerView: View) {
callback.isEnabled = true
}

override fun onDrawerClosed(drawerView: View) {
callback.isEnabled = false
}
})
// Restoring an open drawer before its first layout does not notify drawer listeners.
drawer.doOnLayout { callback.isEnabled = drawer.isDrawerVisible(GravityCompat.START) }
onBackPressedDispatcher.addCallback(this, callback)
}
6 changes: 3 additions & 3 deletions app/src/main/java/com/example/acetimetracker/Login.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import com.google.firebase.auth.auth
import com.google.firebase.Firebase

class Login : AppCompatActivity() {

Expand Down Expand Up @@ -111,4 +111,4 @@ class Login : AppCompatActivity() {
// Create and show the dialog
builder.create().show()
}
}
}
10 changes: 2 additions & 8 deletions app/src/main/java/com/example/acetimetracker/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class MainActivity : AppCompatActivity() {
)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
handleDrawerBack(drawerLayout)

navigationView.setNavigationItemSelectedListener { menuItem ->
when (menuItem.itemId) {
Expand Down Expand Up @@ -172,11 +173,4 @@ class MainActivity : AppCompatActivity() {
return super.onOptionsItemSelected(item)
}

override fun onBackPressed() {
if (drawerLayout.isDrawerOpen(navigationView)) {
drawerLayout.closeDrawer(navigationView)
} else {
super.onBackPressed()
}
}
}
}
Loading