Skip to content
Open
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
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/app/build
/captures
.externalNativeBuild
.cxx
local.properties
84 changes: 84 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.dagger.hilt.android")
id("com.google.devtools.ksp")
}

android {
namespace = "com.soulstice.app"
compileSdk = 34

defaultConfig {
applicationId = "com.soulstice.app"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
kotlinOptions {
jvmTarget = "21"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.10"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}

dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.activity:activity-compose:1.8.2")
implementation(platform("androidx.compose:compose-bom:2024.02.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-extended")
implementation("com.google.android.material:material:1.11.0")

// Hilt
implementation("com.google.dagger:hilt-android:2.50")
ksp("com.google.dagger:hilt-compiler:2.50")
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")

// Room
val roomVersion = "2.6.1"
implementation("androidx.room:room-runtime:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
ksp("androidx.room:room-compiler:$roomVersion")

// Navigation
implementation("androidx.navigation:navigation-compose:2.7.7")

// Testing
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
18 changes: 18 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name=".SoulsticeApp"
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Soulstice">
<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>
90 changes: 90 additions & 0 deletions app/src/main/kotlin/com/soulstice/app/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.soulstice.app

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Inbox
import androidx.compose.material.icons.filled.GridView
import androidx.compose.material.icons.filled.AutoAwesome
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.soulstice.app.ui.screens.DashboardScreen
import com.soulstice.app.ui.screens.InboxScreen
import com.soulstice.app.ui.screens.ProjectsScreen
import com.soulstice.app.ui.screens.ReviewScreen
import com.soulstice.app.ui.theme.SoulsticeTheme
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SoulsticeTheme {
MainScreen()
}
}
}
}

sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
object Dashboard : Screen("dashboard", "Dashboard", Icons.Default.Home)
object Inbox : Screen("inbox", "Inbox", Icons.Default.Inbox)
object Projects : Screen("projects", "Projects", Icons.Default.GridView)
object Review : Screen("review", "Review", Icons.Default.AutoAwesome)
}

@Composable
fun MainScreen() {
val navController = rememberNavController()
val items = listOf(
Screen.Dashboard,
Screen.Inbox,
Screen.Projects,
Screen.Review
)

Scaffold(
bottomBar = {
NavigationBar(containerColor = androidx.compose.ui.graphics.Color.White) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
NavigationBarItem(
icon = { Icon(screen.icon, contentDescription = null) },
label = { Text(screen.label) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(navController, startDestination = Screen.Dashboard.route, Modifier.padding(innerPadding)) {
composable(Screen.Dashboard.route) { DashboardScreen() }
composable(Screen.Inbox.route) { InboxScreen() }
composable(Screen.Projects.route) { ProjectsScreen() }
composable(Screen.Review.route) { ReviewScreen() }
}
}
}
7 changes: 7 additions & 0 deletions app/src/main/kotlin/com/soulstice/app/SoulsticeApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.soulstice.app

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class SoulsticeApp : Application()
11 changes: 11 additions & 0 deletions app/src/main/kotlin/com/soulstice/app/data/local/AppDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.soulstice.app.data.local

import androidx.room.Database
import androidx.room.RoomDatabase
import com.soulstice.app.data.local.dao.SoulsticeDao
import com.soulstice.app.data.local.entities.*

@Database(entities = [Task::class, Project::class, Habit::class, Resource::class, Client::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun soulsticeDao(): SoulsticeDao
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.soulstice.app.data.local.dao

import androidx.room.*
import com.soulstice.app.data.local.entities.*
import kotlinx.coroutines.flow.Flow

@Dao
interface SoulsticeDao {
@Query("SELECT * FROM tasks ORDER BY createdAt DESC")
fun getAllTasks(): Flow<List<Task>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertTask(task: Task)

@Query("SELECT * FROM projects")
fun getAllProjects(): Flow<List<Project>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertProject(project: Project)

@Query("SELECT * FROM habits")
fun getAllHabits(): Flow<List<Habit>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertHabit(habit: Habit)

@Query("SELECT * FROM resources")
fun getAllResources(): Flow<List<Resource>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertResource(resource: Resource)

@Query("SELECT * FROM clients")
fun getAllClients(): Flow<List<Client>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertClient(client: Client)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.soulstice.app.data.local.entities

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "clients")
data class Client(
@PrimaryKey val id: String,
val name: String,
val email: String?,
val phone: String?,
val status: String, // "lead", "active", "previous"
val createdAt: Long
)
14 changes: 14 additions & 0 deletions app/src/main/kotlin/com/soulstice/app/data/local/entities/Habit.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.soulstice.app.data.local.entities

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "habits")
data class Habit(
@PrimaryKey val id: String,
val title: String,
val frequency: String, // "daily", "weekly"
val streak: Int,
val lastCompleted: Long?,
val createdAt: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.soulstice.app.data.local.entities

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "projects")
data class Project(
@PrimaryKey val id: String,
val name: String,
val description: String?,
val status: String, // "active", "completed", "archived"
val createdAt: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.soulstice.app.data.local.entities

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "resources")
data class Resource(
@PrimaryKey val id: String,
val title: String,
val type: String, // "note", "link", "document"
val content: String?,
val url: String?,
val projectId: String?,
val createdAt: Long
)
17 changes: 17 additions & 0 deletions app/src/main/kotlin/com/soulstice/app/data/local/entities/Task.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.soulstice.app.data.local.entities

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "tasks")
data class Task(
@PrimaryKey val id: String,
val title: String,
val description: String?,
val status: String, // "todo", "in_progress", "done"
val priority: String, // "low", "medium", "high"
val energy: String, // "low", "high"
val projectId: String?,
val dueDate: Long?,
val createdAt: Long
)
32 changes: 32 additions & 0 deletions app/src/main/kotlin/com/soulstice/app/di/DatabaseModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.soulstice.app.di

import android.content.Context
import androidx.room.Room
import com.soulstice.app.data.local.AppDatabase
import com.soulstice.app.data.local.dao.SoulsticeDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {

@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"soulstice_db"
).build()
}

@Provides
fun provideSoulsticeDao(database: AppDatabase): SoulsticeDao {
return database.soulsticeDao()
}
}
Loading