commit 897afb99d4b448b981bb99a95b8ed960fa088e9f Author: Sanders Date: Thu May 29 12:03:51 2025 +0300 init diff --git a/.aider.chat.history.md b/.aider.chat.history.md new file mode 100644 index 0000000..757721a --- /dev/null +++ b/.aider.chat.history.md @@ -0,0 +1,3 @@ + +# aider chat started at 2025-05-29 12:01:57 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..ae733f1 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..74dd639 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..bc4b9e1 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,60 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'box.sanders.financeapp' + compileSdk 35 + + defaultConfig { + applicationId "box.sanders.financeapp" + minSdk 33 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + buildFeatures { + viewBinding true + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation libs.constraintlayout + implementation libs.lifecycle.livedata.ktx + implementation libs.lifecycle.viewmodel.ktx + implementation libs.navigation.fragment + implementation libs.navigation.ui + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core + + implementation libs.fragment + implementation libs.recyclerview + implementation libs.cardview + implementation libs.mpandroidchart + + // Room database + implementation libs.room.runtime + annotationProcessor libs.room.compiler + + // Lifecycle + implementation libs.lifecycle.viewmodel + implementation libs.lifecycle.livedata + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/box/sanders/financeapp/ExampleInstrumentedTest.java b/app/src/androidTest/java/box/sanders/financeapp/ExampleInstrumentedTest.java new file mode 100644 index 0000000..f25746b --- /dev/null +++ b/app/src/androidTest/java/box/sanders/financeapp/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package box.sanders.financeapp; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("box.sanders.financeapp", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0257115 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/MainActivity.java b/app/src/main/java/box/sanders/financeapp/MainActivity.java new file mode 100644 index 0000000..c652d4a --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/MainActivity.java @@ -0,0 +1,44 @@ +package box.sanders.financeapp; + +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.example.myfinances.fragment.HomeFragment; +import com.example.myfinances.fragment.ReportsFragment; +import com.example.myfinances.fragment.SettingsFragment; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation); + bottomNav.setOnItemSelectedListener(item -> { + Fragment selectedFragment = null; + + int itemId = item.getItemId(); + if (itemId == R.id.nav_home) { + selectedFragment = new HomeFragment(); + } else if (itemId == R.id.nav_reports) { + selectedFragment = new ReportsFragment(); + } else if (itemId == R.id.nav_settings) { + selectedFragment = new SettingsFragment(); + } + + if (selectedFragment != null) { + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragment_container, selectedFragment) + .commit(); + } + return true; + }); + + // Load default fragment + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragment_container, new HomeFragment()) + .commit(); + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/adapter/TransactionAdapter.java b/app/src/main/java/box/sanders/financeapp/adapter/TransactionAdapter.java new file mode 100644 index 0000000..f09adbe --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/adapter/TransactionAdapter.java @@ -0,0 +1,92 @@ +package box.sanders.financeapp.adapter; + + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import box.sanders.financeapp.R; +import box.sanders.financeapp.model.TransactionWithCategory; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Locale; + +public class TransactionAdapter extends RecyclerView.Adapter { + + private List transactions; + private Context context; + private NumberFormat currencyFormat; + private SimpleDateFormat dateFormat; + + public TransactionAdapter(Context context) { + this.context = context; + this.currencyFormat = NumberFormat.getCurrencyInstance(Locale.US); + this.dateFormat = new SimpleDateFormat("MMM d, yyyy", Locale.US); + } + + @NonNull + @Override + public TransactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_transaction, parent, false); + return new TransactionViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) { + TransactionWithCategory transactionWithCategory = transactions.get(position); + + holder.tvMerchant.setText(transactionWithCategory.transaction.getMerchant()); + + if (transactionWithCategory.category != null) { + holder.tvCategory.setText(transactionWithCategory.category.getName()); + holder.ivIcon.setColorFilter(transactionWithCategory.category.getColorCode()); + } else { + holder.tvCategory.setText("Income"); + holder.ivIcon.setColorFilter(context.getResources().getColor(R.color.green, null)); + } + + double amount = transactionWithCategory.transaction.getAmount(); + String amountText = ("expense".equals(transactionWithCategory.transaction.getType()) ? "- " : "+ ") + + currencyFormat.format(amount); + holder.tvAmount.setText(amountText); + + // Set color based on transaction type + if ("expense".equals(transactionWithCategory.transaction.getType())) { + holder.tvAmount.setTextColor(context.getResources().getColor(R.color.red, null)); + } else { + holder.tvAmount.setTextColor(context.getResources().getColor(R.color.green, null)); + } + + holder.tvDate.setText(dateFormat.format(transactionWithCategory.transaction.getDate())); + } + + @Override + public int getItemCount() { + return transactions != null ? transactions.size() : 0; + } + + public void setTransactions(List transactions) { + this.transactions = transactions; + notifyDataSetChanged(); + } + + static class TransactionViewHolder extends RecyclerView.ViewHolder { + ImageView ivIcon; + TextView tvMerchant, tvCategory, tvAmount, tvDate; + + public TransactionViewHolder(@NonNull View itemView) { + super(itemView); + ivIcon = itemView.findViewById(R.id.iv_icon); + tvMerchant = itemView.findViewById(R.id.tv_merchant); + tvCategory = itemView.findViewById(R.id.tv_category); + tvAmount = itemView.findViewById(R.id.tv_amount); + tvDate = itemView.findViewById(R.id.tv_date); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/dao/CategoryDao.java b/app/src/main/java/box/sanders/financeapp/dao/CategoryDao.java new file mode 100644 index 0000000..c9f037f --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/dao/CategoryDao.java @@ -0,0 +1,30 @@ +package box.sanders.financeapp.dao; + +import androidx.lifecycle.LiveData; +import androidx.room.*; +import box.sanders.financeapp.model.Category; +import java.util.List; + +@Dao +public interface CategoryDao { + @Query("SELECT * FROM categories") + LiveData> getAllCategories(); + + @Query("SELECT * FROM categories WHERE id = :id") + LiveData getCategoryById(int id); + + @Insert + void insert(Category category); + + @Insert + long[] insertAll(Category... categories); + + @Update + void update(Category category); + + @Delete + void delete(Category category); + + @Query("DELETE FROM categories") + void deleteAll(); +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/dao/TransactionDao.java b/app/src/main/java/box/sanders/financeapp/dao/TransactionDao.java new file mode 100644 index 0000000..77b8ba4 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/dao/TransactionDao.java @@ -0,0 +1,54 @@ +package box.sanders.financeapp.dao; + +import androidx.lifecycle.LiveData; +import androidx.room.*; +import box.sanders.financeapp.model.TransactionRecord; +import box.sanders.financeapp.model.TransactionWithCategory; +import box.sanders.financeapp.model.CategorySpending; +import java.util.List; +import java.util.Date; + +@Dao +public interface TransactionDao { + @Query("SELECT * FROM transactions ORDER BY date DESC") + LiveData> getAllTransactions(); + + @Query("SELECT * FROM transactions WHERE type = :type ORDER BY date DESC") + LiveData> getTransactionsByType(String type); + + @Query("SELECT * FROM transactions WHERE date >= :startDate AND date <= :endDate ORDER BY date DESC") + LiveData> getTransactionsByDateRange(Date startDate, Date endDate); + + @Transaction + @Query("SELECT * FROM transactions ORDER BY date DESC LIMIT 10") + LiveData> getRecentTransactionsWithCategory(); + + @Query("SELECT c.name as categoryName, SUM(t.amount) as totalAmount, c.colorCode " + + "FROM transactions t " + + "INNER JOIN categories c ON t.categoryId = c.id " + + "WHERE t.type = 'expense' " + + "GROUP BY t.categoryId, c.name, c.colorCode " + + "ORDER BY totalAmount DESC") + LiveData> getCategorySpending(); + + @Query("SELECT SUM(amount) FROM transactions WHERE type = 'income'") + LiveData getTotalIncome(); + + @Query("SELECT SUM(amount) FROM transactions WHERE type = 'expense'") + LiveData getTotalExpenses(); + + @Insert + void insert(TransactionRecord transaction); + + @Insert + void insertAll(TransactionRecord... transactions); + + @Update + void update(TransactionRecord transaction); + + @Delete + void delete(TransactionRecord transaction); + + @Query("DELETE FROM transactions") + void deleteAll(); +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/database/DateConverter.java b/app/src/main/java/box/sanders/financeapp/database/DateConverter.java new file mode 100644 index 0000000..30d8109 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/database/DateConverter.java @@ -0,0 +1,16 @@ +package box.sanders.financeapp.database; + +import androidx.room.TypeConverter; +import java.util.Date; + +public class DateConverter { + @TypeConverter + public static Date fromTimestamp(Long value) { + return value == null ? null : new Date(value); + } + + @TypeConverter + public static Long dateToTimestamp(Date date) { + return date == null ? null : date.getTime(); + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/database/FinanceDatabase.java b/app/src/main/java/box/sanders/financeapp/database/FinanceDatabase.java new file mode 100644 index 0000000..f6895db --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/database/FinanceDatabase.java @@ -0,0 +1,108 @@ +package box.sanders.financeapp.database; + +import android.content.Context; +import androidx.room.Database; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.room.TypeConverters; +import box.sanders.financeapp.dao.CategoryDao; +import box.sanders.financeapp.dao.TransactionDao; +import box.sanders.financeapp.model.Category; +import box.sanders.financeapp.model.TransactionRecord; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Database( + entities = {Category.class, TransactionRecord.class}, + version = 1, + exportSchema = false +) +@TypeConverters({DateConverter.class}) +public abstract class FinanceDatabase extends RoomDatabase { + + public abstract CategoryDao categoryDao(); + public abstract TransactionDao transactionDao(); + + private static volatile FinanceDatabase INSTANCE; + private static final int NUMBER_OF_THREADS = 4; + public static final ExecutorService databaseWriteExecutor = + Executors.newFixedThreadPool(NUMBER_OF_THREADS); + + public static FinanceDatabase getDatabase(final Context context) { + if (INSTANCE == null) { + synchronized (FinanceDatabase.class) { + if (INSTANCE == null) { + INSTANCE = Room.databaseBuilder(context.getApplicationContext(), + FinanceDatabase.class, "finance_database") + .addCallback(sRoomDatabaseCallback) + .build(); + } + } + } + return INSTANCE; + } + + private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback() { + @Override + public void onCreate(androidx.sqlite.db.SupportSQLiteDatabase db) { + super.onCreate(db); + databaseWriteExecutor.execute(() -> { + populateDatabase(INSTANCE); + }); + } + }; + + private static void populateDatabase(FinanceDatabase db) { + CategoryDao categoryDao = db.categoryDao(); + TransactionDao transactionDao = db.transactionDao(); + + // Clear existing data + categoryDao.deleteAll(); + transactionDao.deleteAll(); + + // Insert categories + Category[] categories = { + new Category("Groceries", 0xFFFF6B6B, android.R.drawable.ic_menu_gallery), + new Category("Restaurant", 0xFFFFA726, android.R.drawable.ic_menu_gallery), + new Category("Shopping", 0xFF42A5F5, android.R.drawable.ic_menu_gallery), + new Category("Utilities", 0xFF26A69A, android.R.drawable.ic_menu_gallery), + new Category("Subscriptions", 0xFF7E57C2, android.R.drawable.ic_menu_gallery), + new Category("Transportation", 0xFFEC407A, android.R.drawable.ic_menu_gallery), + new Category("Entertainment", 0xFF66BB6A, android.R.drawable.ic_menu_gallery), + new Category("Healthcare", 0xFFFF7043, android.R.drawable.ic_menu_gallery) + }; + + long[] categoryIds = categoryDao.insertAll(categories); + + // Insert test transactions + TransactionRecord[] transactions = { + // Recent transactions (expenses) + new TransactionRecord("expense", 150.00, (int)categoryIds[0], + new java.util.Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000), "Unknown Merchant"), + new TransactionRecord("expense", 45.00, (int)categoryIds[0], + new java.util.Date(System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000), "Whole Foods Market"), + new TransactionRecord("expense", 39.00, (int)categoryIds[4], + new java.util.Date(System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000), "Netflix Premium"), + new TransactionRecord("expense", 85.50, (int)categoryIds[1], + new java.util.Date(System.currentTimeMillis() - 3 * 24 * 60 * 60 * 1000), "Pizza Palace"), + new TransactionRecord("expense", 120.30, (int)categoryIds[2], + new java.util.Date(System.currentTimeMillis() - 4 * 24 * 60 * 60 * 1000), "Amazon"), + new TransactionRecord("expense", 75.20, (int)categoryIds[3], + new java.util.Date(System.currentTimeMillis() - 5 * 24 * 60 * 60 * 1000), "Electric Company"), + new TransactionRecord("expense", 25.00, (int)categoryIds[1], + new java.util.Date(System.currentTimeMillis() - 6 * 24 * 60 * 60 * 1000), "Starbucks"), + new TransactionRecord("expense", 45.80, (int)categoryIds[2], + new java.util.Date(System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1000), "Target"), + new TransactionRecord("expense", 15.99, (int)categoryIds[4], + new java.util.Date(System.currentTimeMillis() - 8 * 24 * 60 * 60 * 1000), "Spotify Premium"), + new TransactionRecord("expense", 32.50, (int)categoryIds[0], + new java.util.Date(System.currentTimeMillis() - 9 * 24 * 60 * 60 * 1000), "Local Grocery"), + + // Income transactions + new TransactionRecord("income", 1200.00, null, + new java.util.Date(System.currentTimeMillis() - 5 * 24 * 60 * 60 * 1000), "Salary"), + }; + + transactionDao.insertAll(transactions); + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/fragment/HomeFragment.java b/app/src/main/java/box/sanders/financeapp/fragment/HomeFragment.java new file mode 100644 index 0000000..e18662a --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/fragment/HomeFragment.java @@ -0,0 +1,144 @@ +package box.sanders.financeapp.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import box.sanders.financeapp.R; +import box.sanders.financeapp.adapter.TransactionAdapter; +import box.sanders.financeapp.viewmodel.FinanceViewModel; +import box.sanders.financeapp.model.CategorySpending; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class HomeFragment extends Fragment { + + private PieChart pieChart; + private RecyclerView transactionsRecyclerView; + private TransactionAdapter transactionAdapter; + private FinanceViewModel viewModel; + private TextView tvNetBalance, tvIncome, tvExpenses; + private NumberFormat currencyFormat; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_home, container, false); + + currencyFormat = NumberFormat.getCurrencyInstance(Locale.US); + initViews(view); + setupRecyclerView(); + setupViewModel(); + + return view; + } + + private void initViews(View view) { + pieChart = view.findViewById(R.id.pie_chart); + transactionsRecyclerView = view.findViewById(R.id.recycler_transactions); + tvNetBalance = view.findViewById(R.id.tv_net_balance); + tvIncome = view.findViewById(R.id.tv_income); + tvExpenses = view.findViewById(R.id.tv_expenses); + } + + private void setupRecyclerView() { + transactionAdapter = new TransactionAdapter(getContext()); + transactionsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + transactionsRecyclerView.setAdapter(transactionAdapter); + } + + private void setupViewModel() { + viewModel = new ViewModelProvider(this).get(FinanceViewModel.class); + + // Observe financial summary data + viewModel.getNetBalance().observe(getViewLifecycleOwner(), netBalance -> { + if (netBalance != null) { + String balanceText = (netBalance >= 0 ? "+ " : "- ") + currencyFormat.format(Math.abs(netBalance)); + tvNetBalance.setText(balanceText); + tvNetBalance.setTextColor(getResources().getColor( + netBalance >= 0 ? R.color.green : R.color.red, null)); + } + }); + + viewModel.getTotalIncome().observe(getViewLifecycleOwner(), income -> { + if (income != null) { + tvIncome.setText(currencyFormat.format(income)); + } + }); + + viewModel.getTotalExpenses().observe(getViewLifecycleOwner(), expenses -> { + if (expenses != null) { + tvExpenses.setText(currencyFormat.format(expenses)); + } + }); + + // Observe transactions + viewModel.getRecentTransactionsWithCategory().observe(getViewLifecycleOwner(), transactions -> { + transactionAdapter.setTransactions(transactions); + }); + + // Observe category spending for pie chart + viewModel.getCategorySpending().observe(getViewLifecycleOwner(), categorySpendingList -> { + setupPieChart(categorySpendingList); + }); + } + + private void setupPieChart(List categorySpendingList) { + if (categorySpendingList == null || categorySpendingList.isEmpty()) { + return; + } + + // Calculate total for percentages + double total = 0; + for (CategorySpending spending : categorySpendingList) { + total += spending.getTotalAmount(); + } + + List entries = new ArrayList<>(); + List colors = new ArrayList<>(); + + for (CategorySpending spending : categorySpendingList) { + float percentage = (float) ((spending.getTotalAmount() / total) * 100); + spending.setPercentage(percentage); + + entries.add(new PieEntry(percentage, spending.getCategoryName())); + colors.add(spending.getColorCode()); + } + + PieDataSet dataSet = new PieDataSet(entries, ""); + dataSet.setColors(colors); + dataSet.setValueTextColor(getResources().getColor(R.color.white, null)); + dataSet.setValueTextSize(12f); + + PieData data = new PieData(dataSet); + pieChart.setData(data); + pieChart.setUsePercentValues(true); + pieChart.getDescription().setEnabled(false); + pieChart.setHoleRadius(40f); + pieChart.setTransparentCircleRadius(45f); + pieChart.setHoleColor(getResources().getColor(R.color.dark_gray, null)); + pieChart.setDrawCenterText(true); + pieChart.setCenterText("Total\n" + currencyFormat.format(total)); + pieChart.setCenterTextColor(getResources().getColor(R.color.white, null)); + pieChart.setCenterTextSize(16f); + + Legend legend = pieChart.getLegend(); + legend.setEnabled(false); + + pieChart.invalidate(); + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/fragment/ReportsFragment.java b/app/src/main/java/box/sanders/financeapp/fragment/ReportsFragment.java new file mode 100644 index 0000000..7bd0698 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/fragment/ReportsFragment.java @@ -0,0 +1,24 @@ +package box.sanders.financeapp.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import box.sanders.financeapp.R; + +public class ReportsFragment extends Fragment { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_reports, container, false); + + TextView textView = view.findViewById(R.id.tv_reports_title); + textView.setText("Reports"); + + return view; + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/fragment/SettingsFragment.java b/app/src/main/java/box/sanders/financeapp/fragment/SettingsFragment.java new file mode 100644 index 0000000..dc21b09 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/fragment/SettingsFragment.java @@ -0,0 +1,24 @@ +package box.sanders.financeapp.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import box.sanders.financeapp.R; + +public class SettingsFragment extends Fragment { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_settings, container, false); + + TextView textView = view.findViewById(R.id.tv_settings_title); + textView.setText("Settings"); + + return view; + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/model/Category.java b/app/src/main/java/box/sanders/financeapp/model/Category.java new file mode 100644 index 0000000..03cd752 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/model/Category.java @@ -0,0 +1,32 @@ +package box.sanders.financeapp.model; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "categories") +public class Category { + @PrimaryKey(autoGenerate = true) + private int id; + private String name; + private int colorCode; + private int iconResourceId; + + public Category(String name, int colorCode, int iconResourceId) { + this.name = name; + this.colorCode = colorCode; + this.iconResourceId = iconResourceId; + } + + // Getters and Setters + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public int getColorCode() { return colorCode; } + public void setColorCode(int colorCode) { this.colorCode = colorCode; } + + public int getIconResourceId() { return iconResourceId; } + public void setIconResourceId(int iconResourceId) { this.iconResourceId = iconResourceId; } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/model/CategorySpending.java b/app/src/main/java/box/sanders/financeapp/model/CategorySpending.java new file mode 100644 index 0000000..6eda8d7 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/model/CategorySpending.java @@ -0,0 +1,27 @@ +package box.sanders.financeapp.model; + +public class CategorySpending { + private String categoryName; + private double totalAmount; + private int colorCode; + private double percentage; + + public CategorySpending(String categoryName, double totalAmount, int colorCode) { + this.categoryName = categoryName; + this.totalAmount = totalAmount; + this.colorCode = colorCode; + } + + // Getters and Setters + public String getCategoryName() { return categoryName; } + public void setCategoryName(String categoryName) { this.categoryName = categoryName; } + + public double getTotalAmount() { return totalAmount; } + public void setTotalAmount(double totalAmount) { this.totalAmount = totalAmount; } + + public int getColorCode() { return colorCode; } + public void setColorCode(int colorCode) { this.colorCode = colorCode; } + + public double getPercentage() { return percentage; } + public void setPercentage(double percentage) { this.percentage = percentage; } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/model/TransactionRecord.java b/app/src/main/java/box/sanders/financeapp/model/TransactionRecord.java new file mode 100644 index 0000000..c6b6849 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/model/TransactionRecord.java @@ -0,0 +1,54 @@ +package box.sanders.financeapp.model; + +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import java.util.Date; + +@Entity( + tableName = "transactions", + foreignKeys = @ForeignKey( + entity = Category.class, + parentColumns = "id", + childColumns = "categoryId", + onDelete = ForeignKey.SET_NULL + ), + indices = {@Index("categoryId")} +) +public class TransactionRecord { + @PrimaryKey(autoGenerate = true) + private int id; + private String type; // 'income' or 'expense' + private double amount; + private Integer categoryId; // Reference to category (null for income) + private Date date; + private String merchant; + + public TransactionRecord(String type, double amount, Integer categoryId, Date date, String merchant) { + this.type = type; + this.amount = amount; + this.categoryId = categoryId; + this.date = date; + this.merchant = merchant; + } + + // Getters and Setters + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public double getAmount() { return amount; } + public void setAmount(double amount) { this.amount = amount; } + + public Integer getCategoryId() { return categoryId; } + public void setCategoryId(Integer categoryId) { this.categoryId = categoryId; } + + public Date getDate() { return date; } + public void setDate(Date date) { this.date = date; } + + public String getMerchant() { return merchant; } + public void setMerchant(String merchant) { this.merchant = merchant; } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/model/TransactionWithCategory.java b/app/src/main/java/box/sanders/financeapp/model/TransactionWithCategory.java new file mode 100644 index 0000000..2992ab4 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/model/TransactionWithCategory.java @@ -0,0 +1,15 @@ +package box.sanders.financeapp.model; + +import androidx.room.Embedded; +import androidx.room.Relation; + +public class TransactionWithCategory { + @Embedded + public TransactionRecord transaction; + + @Relation( + parentColumn = "categoryId", + entityColumn = "id" + ) + public Category category; +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/repository/FinanceRepository.java b/app/src/main/java/box/sanders/financeapp/repository/FinanceRepository.java new file mode 100644 index 0000000..68cf163 --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/repository/FinanceRepository.java @@ -0,0 +1,74 @@ +package box.sanders.financeapp.repository; + +import android.app.Application; +import androidx.lifecycle.LiveData; +import box.sanders.financeapp.dao.CategoryDao; +import box.sanders.financeapp.dao.TransactionDao; +import box.sanders.financeapp.database.FinanceDatabase; +import box.sanders.financeapp.model.Category; +import box.sanders.financeapp.model.TransactionRecord; +import box.sanders.financeapp.model.TransactionWithCategory; +import box.sanders.financeapp.model.CategorySpending; +import java.util.List; + +public class FinanceRepository { + private CategoryDao categoryDao; + private TransactionDao transactionDao; + private LiveData> allCategories; + private LiveData> allTransactions; + private LiveData> recentTransactionsWithCategory; + private LiveData> categorySpending; + private LiveData totalIncome; + private LiveData totalExpenses; + + public FinanceRepository(Application application) { + FinanceDatabase db = FinanceDatabase.getDatabase(application); + categoryDao = db.categoryDao(); + transactionDao = db.transactionDao(); + + allCategories = categoryDao.getAllCategories(); + allTransactions = transactionDao.getAllTransactions(); + recentTransactionsWithCategory = transactionDao.getRecentTransactionsWithCategory(); + categorySpending = transactionDao.getCategorySpending(); + totalIncome = transactionDao.getTotalIncome(); + totalExpenses = transactionDao.getTotalExpenses(); + } + + // Categories + public LiveData> getAllCategories() { + return allCategories; + } + + public void insert(Category category) { + FinanceDatabase.databaseWriteExecutor.execute(() -> { + categoryDao.insert(category); + }); + } + + // Transactions + public LiveData> getAllTransactions() { + return allTransactions; + } + + public LiveData> getRecentTransactionsWithCategory() { + return recentTransactionsWithCategory; + } + + public LiveData> getCategorySpending() { + return categorySpending; + } + + public LiveData getTotalIncome() { + return totalIncome; + } + + public LiveData getTotalExpenses() { + return totalExpenses; + } + + public void insert(TransactionRecord transaction) { + FinanceDatabase.databaseWriteExecutor.execute(() -> { + transactionDao.insert(transaction); + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/box/sanders/financeapp/viewmodel/FinanceViewModel.java b/app/src/main/java/box/sanders/financeapp/viewmodel/FinanceViewModel.java new file mode 100644 index 0000000..dc9b03c --- /dev/null +++ b/app/src/main/java/box/sanders/financeapp/viewmodel/FinanceViewModel.java @@ -0,0 +1,90 @@ +package box.sanders.financeapp.viewmodel; + +import android.app.Application; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MediatorLiveData; +import box.sanders.financeapp.model.Category; +import box.sanders.financeapp.model.TransactionRecord; +import box.sanders.financeapp.model.TransactionWithCategory; +import box.sanders.financeapp.model.CategorySpending; +import box.sanders.financeapp.repository.FinanceRepository; +import java.util.List; + +public class FinanceViewModel extends AndroidViewModel { + private FinanceRepository repository; + private LiveData> allCategories; + private LiveData> allTransactions; + private LiveData> recentTransactionsWithCategory; + private LiveData> categorySpending; + private LiveData totalIncome; + private LiveData totalExpenses; + private MediatorLiveData netBalance; + + public FinanceViewModel(Application application) { + super(application); + repository = new FinanceRepository(application); + + allCategories = repository.getAllCategories(); + allTransactions = repository.getAllTransactions(); + recentTransactionsWithCategory = repository.getRecentTransactionsWithCategory(); + categorySpending = repository.getCategorySpending(); + totalIncome = repository.getTotalIncome(); + totalExpenses = repository.getTotalExpenses(); + + // Calculate net balance + netBalance = new MediatorLiveData<>(); + netBalance.addSource(totalIncome, income -> { + Double expenses = totalExpenses.getValue(); + updateNetBalance(income, expenses); + }); + netBalance.addSource(totalExpenses, expenses -> { + Double income = totalIncome.getValue(); + updateNetBalance(income, expenses); + }); + } + + private void updateNetBalance(Double income, Double expenses) { + if (income != null && expenses != null) { + netBalance.setValue(income - expenses); + } + } + + // Getters + public LiveData> getAllCategories() { + return allCategories; + } + + public LiveData> getAllTransactions() { + return allTransactions; + } + + public LiveData> getRecentTransactionsWithCategory() { + return recentTransactionsWithCategory; + } + + public LiveData> getCategorySpending() { + return categorySpending; + } + + public LiveData getTotalIncome() { + return totalIncome; + } + + public LiveData getTotalExpenses() { + return totalExpenses; + } + + public LiveData getNetBalance() { + return netBalance; + } + + // Insert methods + public void insert(Category category) { + repository.insert(category); + } + + public void insert(TransactionRecord transaction) { + repository.insert(transaction); + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_camera.xml b/app/src/main/res/drawable/ic_menu_camera.xml new file mode 100644 index 0000000..634fe92 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_camera.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu_gallery.xml b/app/src/main/res/drawable/ic_menu_gallery.xml new file mode 100644 index 0000000..03c7709 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_gallery.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_slideshow.xml b/app/src/main/res/drawable/ic_menu_slideshow.xml new file mode 100644 index 0000000..5e9e163 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_slideshow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml new file mode 100644 index 0000000..6d81870 --- /dev/null +++ b/app/src/main/res/drawable/side_nav_bar.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file 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..6c7dd7c --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml new file mode 100644 index 0000000..3c9b57b --- /dev/null +++ b/app/src/main/res/layout/app_bar_main.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..6e0ea39 --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_gallery.xml b/app/src/main/res/layout/fragment_gallery.xml new file mode 100644 index 0000000..643fe25 --- /dev/null +++ b/app/src/main/res/layout/fragment_gallery.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml new file mode 100644 index 0000000..f3d9b08 --- /dev/null +++ b/app/src/main/res/layout/fragment_home.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_slideshow.xml b/app/src/main/res/layout/fragment_slideshow.xml new file mode 100644 index 0000000..2141a33 --- /dev/null +++ b/app/src/main/res/layout/fragment_slideshow.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml new file mode 100644 index 0000000..c145545 --- /dev/null +++ b/app/src/main/res/layout/nav_header_main.xml @@ -0,0 +1,35 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml new file mode 100644 index 0000000..d7f2df2 --- /dev/null +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -0,0 +1,20 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml new file mode 100644 index 0000000..412d5f8 --- /dev/null +++ b/app/src/main/res/menu/main.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml new file mode 100644 index 0000000..f0f16ea --- /dev/null +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml new file mode 100644 index 0000000..22d7f00 --- /dev/null +++ b/app/src/main/res/values-land/dimens.xml @@ -0,0 +1,3 @@ + + 48dp + \ No newline at end of file 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..0db0828 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values-w1240dp/dimens.xml b/app/src/main/res/values-w1240dp/dimens.xml new file mode 100644 index 0000000..d73f4a3 --- /dev/null +++ b/app/src/main/res/values-w1240dp/dimens.xml @@ -0,0 +1,3 @@ + + 200dp + \ No newline at end of file diff --git a/app/src/main/res/values-w600dp/dimens.xml b/app/src/main/res/values-w600dp/dimens.xml new file mode 100644 index 0000000..22d7f00 --- /dev/null +++ b/app/src/main/res/values-w600dp/dimens.xml @@ -0,0 +1,3 @@ + + 48dp + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..4ab4520 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,8 @@ + + + 16dp + 16dp + 8dp + 176dp + 16dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..fdeecd9 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,13 @@ + + FinanceApp + Open navigation drawer + Close navigation drawer + Android Studio + android.studio@android.com + Navigation header + Settings + + Home + Gallery + Slideshow + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..412c028 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,25 @@ + + + + + + +