diff --git a/README.md b/README.md
index 5234a55..e95b365 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ This project is based on money management for anyone who likes the **20-30-50 ru
### Built With
-- Java / Kotlin
+- Java
- XML
- Python
diff --git a/app/build.gradle b/app/build.gradle
index a8383b9..67ffc28 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,4 +1,3 @@
-
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
@@ -44,16 +43,19 @@ dependencies {
implementation 'com.google.firebase:firebase-auth'
implementation 'com.google.firebase:firebase-firestore'
+ // Firebase Tasks
+ implementation 'com.google.android.gms:play-services-tasks:18.2.0'
+
// Other dependencies
implementation 'com.google.android.gms:play-services-auth:21.2.0'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.google.android.material:material:1.12.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation 'com.orhanobut:dialogplus:1.11@aar'
// Fragments
- implementation "androidx.fragment:fragment:1.8.4"
- implementation "androidx.fragment:fragment-ktx:1.8.4"
+ implementation "androidx.fragment:fragment:1.8.5"
+ implementation "androidx.fragment:fragment-ktx:1.8.5"
// Testing
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a1fe52c..cec167f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -9,25 +9,25 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/umoneylogo_round"
android:supportsRtl="true"
- android:theme="@style/Theme.AppCompat.DayNight">
-
-
-
+ android:theme="@style/Theme.UMoney">
+
+
+
+ android:name=".ui.splash.SplashActivity"
+ android:theme="@style/Theme.UMoney.Splash"
+ android:exported="true">
-
-
-
+
+
+
+
diff --git a/app/src/main/java/com/elececo/umoney/AddTransactionActivity.java b/app/src/main/java/com/elececo/umoney/AddTransactionActivity.java
deleted file mode 100644
index 0a5c6bb..0000000
--- a/app/src/main/java/com/elececo/umoney/AddTransactionActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.elececo.umoney;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.Toast;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import com.elececo.umoney.model.Transaction;
-import com.elececo.umoney.viewmodel.TransactionViewModel;
-import com.google.firebase.Timestamp;
-
-public class AddTransactionActivity extends AppCompatActivity {
-
- private EditText amountEditText;
- private EditText descriptionEditText;
- private Button saveButton;
- private TransactionViewModel viewModel;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_add_transaction);
-
- amountEditText = findViewById(R.id.amountEditText);
- descriptionEditText = findViewById(R.id.descriptionEditText);
- saveButton = findViewById(R.id.saveButton);
-
- String category = getIntent().getStringExtra("category");
- String transactionType = getIntent().getStringExtra("transactionType");
-
- viewModel = new TransactionViewModel();
-
- saveButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- saveTransaction(category, transactionType);
- }
- });
- }
-
- private void saveTransaction(String category, String transactionType) {
- String amountStr = amountEditText.getText().toString();
- String description = descriptionEditText.getText().toString();
-
- if (amountStr.isEmpty() || description.isEmpty()) {
- Toast.makeText(this, "Please fill all fields", Toast.LENGTH_SHORT).show();
- return;
- }
-
- double amount = Double.parseDouble(amountStr);
- if (transactionType.equals("Taken")) {
- amount = -amount; // Make the amount negative for "Taken" transactions
- }
-
- Transaction transaction = new Transaction(amount, Timestamp.now(), description, category);
- viewModel.addTransaction(transaction);
-
- Toast.makeText(this, "Transaction saved", Toast.LENGTH_SHORT).show();
- finish();
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/DashboardFragment.java b/app/src/main/java/com/elececo/umoney/DashboardFragment.java
deleted file mode 100644
index 20ccf6e..0000000
--- a/app/src/main/java/com/elececo/umoney/DashboardFragment.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.elececo.umoney;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import androidx.fragment.app.Fragment;
-
-public class DashboardFragment extends Fragment {
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_dashboard, container, false);
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/Google_Login.java b/app/src/main/java/com/elececo/umoney/Google_Login.java
deleted file mode 100644
index 12c838a..0000000
--- a/app/src/main/java/com/elececo/umoney/Google_Login.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.elececo.umoney;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AppCompatActivity;
-
-import com.google.android.gms.auth.api.signin.GoogleSignIn;
-import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
-import com.google.android.gms.auth.api.signin.GoogleSignInClient;
-import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
-import com.google.android.gms.common.SignInButton;
-import com.google.android.gms.common.api.ApiException;
-import com.google.android.gms.tasks.OnCompleteListener;
-import com.google.android.gms.tasks.Task;
-import com.google.firebase.auth.AuthCredential;
-import com.google.firebase.auth.AuthResult;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.FirebaseUser;
-import com.google.firebase.auth.GoogleAuthProvider;
-
-public class Google_Login extends AppCompatActivity {
-
- private static final String TAG = "GoogleLogin";
- private static final int RC_SIGN_IN = 9001;
-
- private FirebaseAuth mAuth;
- private GoogleSignInClient mGoogleSignInClient;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_google_login);
-
- // Initialize Firebase Auth
- mAuth = FirebaseAuth.getInstance();
-
- // Configure Google Sign In
- GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
- .requestIdToken(getString(R.string.default_web_client_id))
- .requestEmail()
- .build();
-
- mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
-
- SignInButton signInButton = findViewById(R.id.googleSignInButton);
- signInButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- signIn();
- }
- });
- }
-
- @Override
- public void onStart() {
- super.onStart();
- // Check if user is signed in (non-null) and update UI accordingly.
- FirebaseUser currentUser = mAuth.getCurrentUser();
- updateUI(currentUser);
- }
-
- private void signIn() {
- Intent signInIntent = mGoogleSignInClient.getSignInIntent();
- startActivityForResult(signInIntent, RC_SIGN_IN);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
- if (requestCode == RC_SIGN_IN) {
- Task task = GoogleSignIn.getSignedInAccountFromIntent(data);
- try {
- // Google Sign In was successful, authenticate with Firebase
- GoogleSignInAccount account = task.getResult(ApiException.class);
- Log.d(TAG, "firebaseAuthWithGoogle:" + account.getId());
- firebaseAuthWithGoogle(account.getIdToken());
- } catch (ApiException e) {
- // Google Sign In failed, update UI appropriately
- Log.w(TAG, "Google sign in failed", e);
- updateUI(null);
- }
- }
- }
-
- private void firebaseAuthWithGoogle(String idToken) {
- AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
- mAuth.signInWithCredential(credential)
- .addOnCompleteListener(this, new OnCompleteListener() {
- @Override
- public void onComplete(@NonNull Task task) {
- if (task.isSuccessful()) {
- // Sign in success, update UI with the signed-in user's information
- Log.d(TAG, "signInWithCredential:success");
- FirebaseUser user = mAuth.getCurrentUser();
- updateUI(user);
- } else {
- // If sign in fails, display a message to the user.
- Log.w(TAG, "signInWithCredential:failure", task.getException());
- updateUI(null);
- }
- }
- });
- }
-
- private void updateUI(FirebaseUser user) {
- if (user != null) {
- // User is signed in, navigate to the main activity
- Intent intent = new Intent(Google_Login.this, MainActivity.class);
- startActivity(intent);
- finish();
- } else {
- // User is signed out, show a toast message
- Toast.makeText(this, "Please sign in to continue", Toast.LENGTH_SHORT).show();
- }
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/MainActivity.java b/app/src/main/java/com/elececo/umoney/MainActivity.java
deleted file mode 100644
index 0b4c617..0000000
--- a/app/src/main/java/com/elececo/umoney/MainActivity.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.elececo.umoney;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.Toast;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
-
-import com.google.android.material.bottomnavigation.BottomNavigationView;
-
-public class MainActivity extends AppCompatActivity {
-
- private BottomNavigationView bottomNavigationView;
- private View transactionButtonsLayout;
- private Button givenButton, takenButton;
- private String currentFragmentTag;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- bottomNavigationView = findViewById(R.id.bottomNavigationView);
- transactionButtonsLayout = findViewById(R.id.transactionButtonsLayout);
-
- if (transactionButtonsLayout == null) {
- Log.e("MainActivity", "transactionButtonsLayout is null");
- } else {
- givenButton = transactionButtonsLayout.findViewById(R.id.givenButton);
- takenButton = transactionButtonsLayout.findViewById(R.id.takenButton);
-
- if (givenButton == null || takenButton == null) {
- Log.e("MainActivity", "Button views are null");
- } else {
- Log.d("MainActivity", "All views found successfully");
- }
- }
-
- bottomNavigationView.setOnItemSelectedListener(item -> {
- int itemId = item.getItemId();
- if (itemId == R.id.Dashboard) {
- replaceFragment(new DashboardFragment(), "Dashboard");
- } else if (itemId == R.id.Needs) {
- replaceFragment(new NeedsFragment(), "Needs");
- } else if (itemId == R.id.Wants) {
- replaceFragment(new WantsFragment(), "Wants");
- } else if (itemId == R.id.Savings) {
- replaceFragment(new SavingsFragment(), "Savings");
- }
- return true;
- });
-
- givenButton.setOnClickListener(v -> handleTransactionClick("Given"));
- takenButton.setOnClickListener(v -> handleTransactionClick("Taken"));
-
- // Set default fragment
- replaceFragment(new DashboardFragment(), "Dashboard");
-
- // Force visibility of transaction buttons
- if (transactionButtonsLayout != null) {
- transactionButtonsLayout.setVisibility(View.VISIBLE);
- Log.d("MainActivity", "Forcing transaction buttons visibility to VISIBLE");
- }
-
- updateTransactionButtonsVisibility();
- }
-
- private void replaceFragment(Fragment fragment, String tag) {
- FragmentManager fragmentManager = getSupportFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
- fragmentTransaction.replace(R.id.frame_layout, fragment);
- fragmentTransaction.commit();
-
- currentFragmentTag = tag;
- updateTransactionButtonsVisibility();
- }
-
- private void updateTransactionButtonsVisibility() {
- if (transactionButtonsLayout != null) {
- if (currentFragmentTag.equals("Needs") || currentFragmentTag.equals("Wants") || currentFragmentTag.equals("Savings")) {
- transactionButtonsLayout.setVisibility(View.VISIBLE);
- Log.d("MainActivity", "Setting transaction buttons to VISIBLE for " + currentFragmentTag);
- } else {
- transactionButtonsLayout.setVisibility(View.GONE);
- Log.d("MainActivity", "Setting transaction buttons to GONE for " + currentFragmentTag);
- }
- Log.d("MainActivity", "Current visibility: " + (transactionButtonsLayout.getVisibility() == View.VISIBLE ? "VISIBLE" : "GONE"));
- } else {
- Log.e("MainActivity", "transactionButtonsLayout is null");
- }
- }
-
- private void handleTransactionClick(String transactionType) {
- String category = currentFragmentTag; // This will be "Needs", "Wants", or "Savings"
-
- Intent intent = new Intent(this, AddTransactionActivity.class);
- intent.putExtra("category", category);
- intent.putExtra("transactionType", transactionType);
- startActivity(intent);
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/NeedsFragment.java b/app/src/main/java/com/elececo/umoney/NeedsFragment.java
deleted file mode 100644
index 0733d89..0000000
--- a/app/src/main/java/com/elececo/umoney/NeedsFragment.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.elececo.umoney;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.util.Log;
-
-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.elececo.umoney.adapter.TransactionAdapter;
-import com.elececo.umoney.viewmodel.TransactionViewModel;
-
-public class NeedsFragment extends Fragment {
-
- private TransactionViewModel viewModel;
- private RecyclerView recyclerView;
- private TransactionAdapter adapter;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_needs, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- recyclerView = view.findViewById(R.id.recyclerView);
- recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- adapter = new TransactionAdapter();
- recyclerView.setAdapter(adapter);
-
- viewModel = new ViewModelProvider(this).get(TransactionViewModel.class);
- viewModel.loadTransactionsByCategory("Needs");
- viewModel.getTransactions().observe(getViewLifecycleOwner(), transactions -> {
- adapter.setTransactions(transactions);
- });
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/SavingsFragment.java b/app/src/main/java/com/elececo/umoney/SavingsFragment.java
deleted file mode 100644
index 58d0817..0000000
--- a/app/src/main/java/com/elececo/umoney/SavingsFragment.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.elececo.umoney;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-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.elececo.umoney.adapter.TransactionAdapter;
-import com.elececo.umoney.viewmodel.TransactionViewModel;
-
-public class SavingsFragment extends Fragment {
-
- private TransactionViewModel viewModel;
- private RecyclerView recyclerView;
- private TransactionAdapter adapter;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_savings, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- recyclerView = view.findViewById(R.id.recyclerView);
- recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- adapter = new TransactionAdapter();
- recyclerView.setAdapter(adapter);
-
- viewModel = new ViewModelProvider(this).get(TransactionViewModel.class);
- viewModel.loadTransactionsByCategory("Savings"); // Replace "Category" with "Needs", "Wants", or "Savings"
- viewModel.getTransactions().observe(getViewLifecycleOwner(), transactions -> {
- adapter.setTransactions(transactions);
- });
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/WantsFragment.java b/app/src/main/java/com/elececo/umoney/WantsFragment.java
deleted file mode 100644
index 925a624..0000000
--- a/app/src/main/java/com/elececo/umoney/WantsFragment.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.elececo.umoney;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-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.elececo.umoney.adapter.TransactionAdapter;
-import com.elececo.umoney.viewmodel.TransactionViewModel;
-
-public class WantsFragment extends Fragment {
-
- private TransactionViewModel viewModel;
- private RecyclerView recyclerView;
- private TransactionAdapter adapter;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_wants, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- recyclerView = view.findViewById(R.id.recyclerView);
- recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- adapter = new TransactionAdapter();
- recyclerView.setAdapter(adapter);
-
- viewModel = new ViewModelProvider(this).get(TransactionViewModel.class);
- viewModel.loadTransactionsByCategory("Wants");
- viewModel.getTransactions().observe(getViewLifecycleOwner(), transactions -> {
- adapter.setTransactions(transactions);
- });
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/adapter/TransactionAdapter.java b/app/src/main/java/com/elececo/umoney/adapter/TransactionAdapter.java
deleted file mode 100644
index dbc48a7..0000000
--- a/app/src/main/java/com/elececo/umoney/adapter/TransactionAdapter.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.elececo.umoney.adapter;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.elececo.umoney.R;
-import com.elececo.umoney.model.Transaction;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-public class TransactionAdapter extends RecyclerView.Adapter {
-
- private List transactions = new ArrayList<>();
-
- public void setTransactions(List transactions) {
- this.transactions = transactions;
- notifyDataSetChanged();
- }
-
- @NonNull
- @Override
- public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_transaction, parent, false);
- return new ViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
- Transaction transaction = transactions.get(position);
- holder.bind(transaction);
- }
-
- @Override
- public int getItemCount() {
- return transactions.size();
- }
-
- static class ViewHolder extends RecyclerView.ViewHolder {
- TextView amountTextView;
- TextView dateTextView;
- TextView descriptionTextView;
-
- ViewHolder(@NonNull View itemView) {
- super(itemView);
- amountTextView = itemView.findViewById(R.id.amountTextView);
- dateTextView = itemView.findViewById(R.id.dateTextView);
- descriptionTextView = itemView.findViewById(R.id.descriptionTextView);
- }
-
- void bind(Transaction transaction) {
- amountTextView.setText(String.format("₹ %.2f", transaction.getAmount()));
- SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yy • hh:mm a", Locale.getDefault());
- dateTextView.setText(sdf.format(transaction.getTimestamp().toDate()));
- descriptionTextView.setText(transaction.getDescription());
- }
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/data/model/AuthResult.java b/app/src/main/java/com/elececo/umoney/data/model/AuthResult.java
new file mode 100644
index 0000000..57ea683
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/data/model/AuthResult.java
@@ -0,0 +1,19 @@
+package com.elececo.umoney.data.model;
+
+public class AuthResult {
+ private final boolean success;
+ private final String errorMessage;
+
+ public AuthResult(boolean success, String errorMessage) {
+ this.success = success;
+ this.errorMessage = errorMessage;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/data/model/Transaction.java b/app/src/main/java/com/elececo/umoney/data/model/Transaction.java
new file mode 100644
index 0000000..eb8018d
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/data/model/Transaction.java
@@ -0,0 +1,21 @@
+package com.elececo.umoney.data.model;
+
+import com.google.firebase.Timestamp;
+
+public class Transaction {
+ private String id;
+ private double amount;
+ private String type; // INCOME, NEEDS, WANTS, SAVINGS
+ private Timestamp timestamp;
+ private String recipient;
+ private String category;
+ private String attachmentUrl;
+ private String description;
+ private String status; // PENDING, COMPLETED
+ private String source; // MANUAL, SMS, EMAIL, API
+
+ // Constructor, getters, and setters
+ public Transaction() {
+ // Required empty constructor for Firestore
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/data/model/User.java b/app/src/main/java/com/elececo/umoney/data/model/User.java
new file mode 100644
index 0000000..c902c9e
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/data/model/User.java
@@ -0,0 +1,80 @@
+package com.elececo.umoney.data.model;
+
+import com.google.firebase.Timestamp;
+import java.util.HashMap;
+import java.util.Map;
+
+public class User {
+ private String userId;
+ private String displayName;
+ private String email;
+ private String photoUrl;
+ private Timestamp dateOfBirth;
+ private String employmentType;
+ private String currency;
+ private double monthlyIncome;
+ private Map distributionRules;
+ private boolean firstTimeUser = true;
+
+ // Required empty constructor for Firestore
+ public User() {
+ distributionRules = new HashMap<>();
+ initializeDefaultDistribution();
+ this.firstTimeUser = true;
+ }
+
+ // Constructor with all required fields
+ public User(String userId, String displayName, String email, String photoUrl) {
+ this.userId = userId;
+ this.displayName = displayName;
+ this.email = email;
+ this.photoUrl = photoUrl;
+ this.distributionRules = new HashMap<>();
+ initializeDefaultDistribution();
+ this.firstTimeUser = true;
+ }
+
+ private void initializeDefaultDistribution() {
+ distributionRules.put("needs", 50);
+ distributionRules.put("wants", 30);
+ distributionRules.put("savings", 20);
+ }
+
+ // Getters and Setters
+ public String getUserId() { return userId; }
+ public void setUserId(String userId) { this.userId = userId; }
+
+ public String getDisplayName() { return displayName; }
+ public void setDisplayName(String displayName) { this.displayName = displayName; }
+
+ public String getEmail() { return email; }
+ public void setEmail(String email) { this.email = email; }
+
+ public String getPhotoUrl() { return photoUrl; }
+ public void setPhotoUrl(String photoUrl) { this.photoUrl = photoUrl; }
+
+ public Timestamp getDateOfBirth() { return dateOfBirth; }
+ public void setDateOfBirth(Timestamp dateOfBirth) { this.dateOfBirth = dateOfBirth; }
+
+ public String getEmploymentType() { return employmentType; }
+ public void setEmploymentType(String employmentType) { this.employmentType = employmentType; }
+
+ public String getCurrency() { return currency; }
+ public void setCurrency(String currency) { this.currency = currency; }
+
+ public double getMonthlyIncome() { return monthlyIncome; }
+ public void setMonthlyIncome(double monthlyIncome) { this.monthlyIncome = monthlyIncome; }
+
+ public Map getDistributionRules() { return distributionRules; }
+ public void setDistributionRules(Map distributionRules) {
+ this.distributionRules = distributionRules;
+ }
+
+ public boolean isFirstTimeUser() {
+ return firstTimeUser;
+ }
+
+ public void setFirstTimeUser(boolean firstTimeUser) {
+ this.firstTimeUser = firstTimeUser;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/data/repository/AuthRepository.java b/app/src/main/java/com/elececo/umoney/data/repository/AuthRepository.java
new file mode 100644
index 0000000..f2e07ed
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/data/repository/AuthRepository.java
@@ -0,0 +1,50 @@
+package com.elececo.umoney.data.repository;
+
+import android.content.Context;
+import android.content.Intent;
+import com.elececo.umoney.data.model.User;
+import com.google.android.gms.auth.api.signin.GoogleSignIn;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+import com.google.android.gms.auth.api.signin.GoogleSignInClient;
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.firestore.FirebaseFirestore;
+import com.google.firebase.firestore.DocumentSnapshot;
+
+public class AuthRepository {
+ private final FirebaseFirestore firestore;
+ private final GoogleSignInClient googleSignInClient;
+
+ public AuthRepository(Context context) {
+ firestore = FirebaseFirestore.getInstance();
+ GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestEmail()
+ .requestProfile()
+ .build();
+ googleSignInClient = GoogleSignIn.getClient(context, gso);
+ }
+
+ public Intent getSignInIntent() {
+ return googleSignInClient.getSignInIntent();
+ }
+
+ public Task handleSignInResult(Intent data) {
+ return GoogleSignIn.getSignedInAccountFromIntent(data);
+ }
+
+ public Task getUserData(String uid) {
+ return firestore.collection("users").document(uid).get();
+ }
+
+ public Task createNewUser(User user) {
+ return firestore.collection("users").document(user.getUserId()).set(user);
+ }
+
+ public GoogleSignInAccount getCurrentUser(Context context) {
+ return GoogleSignIn.getLastSignedInAccount(context);
+ }
+
+ public void signOut() {
+ googleSignInClient.signOut();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/data/repository/FirestoreRepository.java b/app/src/main/java/com/elececo/umoney/data/repository/FirestoreRepository.java
new file mode 100644
index 0000000..e92caf2
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/data/repository/FirestoreRepository.java
@@ -0,0 +1,46 @@
+package com.elececo.umoney.data.repository;
+
+import com.google.firebase.firestore.FirebaseFirestore;
+import com.google.firebase.firestore.CollectionReference;
+import com.google.firebase.firestore.DocumentReference;
+import com.elececo.umoney.data.model.Transaction;
+import com.elececo.umoney.data.model.User;
+
+public class FirestoreRepository {
+ private final FirebaseFirestore db;
+ private final String currentUserId;
+
+ public FirestoreRepository(String userId) {
+ this.db = FirebaseFirestore.getInstance();
+ this.currentUserId = userId;
+ }
+
+ // User operations
+ public DocumentReference getUserProfile() {
+ return db.collection("users")
+ .document(currentUserId)
+ .collection("profile")
+ .document("userProfile");
+ }
+
+ // Transaction operations
+ public CollectionReference getTransactions() {
+ return db.collection("users")
+ .document(currentUserId)
+ .collection("transactions");
+ }
+
+ // Categories operations
+ public CollectionReference getCategories() {
+ return db.collection("users")
+ .document(currentUserId)
+ .collection("categories");
+ }
+
+ // Recipients operations
+ public CollectionReference getRecipients() {
+ return db.collection("users")
+ .document(currentUserId)
+ .collection("recipients");
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/model/Transaction.java b/app/src/main/java/com/elececo/umoney/model/Transaction.java
deleted file mode 100644
index 1c6d6ca..0000000
--- a/app/src/main/java/com/elececo/umoney/model/Transaction.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.elececo.umoney.model;
-
-import com.google.firebase.Timestamp;
-
-public class Transaction {
- private double amount;
- private Timestamp timestamp;
- private String description;
- private String category;
-
- // Default constructor for Firestore
- public Transaction() {}
-
- public Transaction(double amount, Timestamp timestamp, String description, String category) {
- this.amount = amount;
- this.timestamp = timestamp;
- this.description = description;
- this.category = category;
- }
-
- public double getAmount() {
- return amount;
- }
-
- public void setAmount(double amount) {
- this.amount = amount;
- }
-
- public Timestamp getTimestamp() {
- return timestamp;
- }
-
- public void setTimestamp(Timestamp timestamp) {
- this.timestamp = timestamp;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public String getCategory() {
- return category;
- }
-
- public void setCategory(String category) {
- this.category = category;
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/repository/TransactionRepository.java b/app/src/main/java/com/elececo/umoney/repository/TransactionRepository.java
deleted file mode 100644
index f730407..0000000
--- a/app/src/main/java/com/elececo/umoney/repository/TransactionRepository.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.elececo.umoney.repository;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-
-import com.elececo.umoney.model.Transaction;
-import com.google.firebase.firestore.FirebaseFirestore;
-import com.google.firebase.firestore.ListenerRegistration;
-import com.google.firebase.firestore.Query;
-
-import java.util.List;
-
-public class TransactionRepository {
- private FirebaseFirestore db;
-
- public TransactionRepository() {
- db = FirebaseFirestore.getInstance();
- }
-
- public void addTransaction(Transaction transaction) {
- db.collection("transactions").add(transaction);
- }
-
- public LiveData> getTransactionsByCategory(String category) {
- MutableLiveData> transactionsLiveData = new MutableLiveData<>();
-
- Query query = db.collection("transactions")
- .whereEqualTo("category", category)
- .orderBy("timestamp", Query.Direction.DESCENDING);
-
- ListenerRegistration listener = query.addSnapshotListener((value, error) -> {
- if (error != null) {
- // Handle error
- return;
- }
- if (value != null) {
- List transactions = value.toObjects(Transaction.class);
- transactionsLiveData.setValue(transactions);
- }
- });
-
- return transactionsLiveData;
- }
-}
diff --git a/app/src/main/java/com/elececo/umoney/ui/auth/GoogleLoginActivity.java b/app/src/main/java/com/elececo/umoney/ui/auth/GoogleLoginActivity.java
new file mode 100644
index 0000000..f1994e8
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/auth/GoogleLoginActivity.java
@@ -0,0 +1,55 @@
+package com.elececo.umoney.ui.auth;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.auth.viewmodel.AuthViewModel;
+import com.elececo.umoney.ui.dashboard.DashboardActivity;
+
+public class GoogleLoginActivity extends AppCompatActivity {
+ private static final int RC_SIGN_IN = 9001;
+ private AuthViewModel authViewModel;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_google_login);
+
+ authViewModel = new ViewModelProvider(this).get(AuthViewModel.class);
+
+ findViewById(R.id.sign_in_button).setOnClickListener(v -> signIn());
+
+ authViewModel.getAuthResult().observe(this, result -> {
+ if (result.isSuccess()) {
+ startMainActivity();
+ } else {
+ // Show error message
+ Toast.makeText(this, "Sign in failed", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ private void signIn() {
+ Intent signInIntent = authViewModel.getSignInIntent();
+ startActivityForResult(signInIntent, RC_SIGN_IN);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == RC_SIGN_IN) {
+ authViewModel.handleSignInResult(data);
+ }
+ }
+
+ private void startMainActivity() {
+ Intent intent = new Intent(this, DashboardActivity.class);
+ startActivity(intent);
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/auth/UserSetupActivity.java b/app/src/main/java/com/elececo/umoney/ui/auth/UserSetupActivity.java
new file mode 100644
index 0000000..dbe33b9
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/auth/UserSetupActivity.java
@@ -0,0 +1,37 @@
+package com.elececo.umoney.ui.auth;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import androidx.appcompat.app.AppCompatActivity;
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.dashboard.DashboardActivity;
+
+public class UserSetupActivity extends AppCompatActivity {
+ private EditText monthlyIncomeInput;
+ private EditText employmentTypeInput;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_user_setup);
+
+ monthlyIncomeInput = findViewById(R.id.monthly_income_input);
+ employmentTypeInput = findViewById(R.id.employment_type_input);
+ Button continueButton = findViewById(R.id.continue_button);
+
+ continueButton.setOnClickListener(v -> saveUserSetup());
+ }
+
+ private void saveUserSetup() {
+ // TODO: Implement saving user setup data
+ startDashboardActivity();
+ }
+
+ private void startDashboardActivity() {
+ Intent intent = new Intent(this, DashboardActivity.class);
+ startActivity(intent);
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/auth/viewmodel/AuthViewModel.java b/app/src/main/java/com/elececo/umoney/ui/auth/viewmodel/AuthViewModel.java
new file mode 100644
index 0000000..1fded83
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/auth/viewmodel/AuthViewModel.java
@@ -0,0 +1,101 @@
+package com.elececo.umoney.ui.auth.viewmodel;
+
+import android.app.Application;
+import android.content.Intent;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import com.elececo.umoney.data.model.User;
+import com.elececo.umoney.data.model.AuthResult;
+import com.elececo.umoney.data.repository.AuthRepository;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+
+public class AuthViewModel extends AndroidViewModel {
+ private final AuthRepository authRepository;
+ private final MutableLiveData userLiveData;
+ private final MutableLiveData isFirstTimeUser;
+ private final MutableLiveData authResult;
+
+ public AuthViewModel(Application application) {
+ super(application);
+ authRepository = new AuthRepository(application);
+ userLiveData = new MutableLiveData<>();
+ isFirstTimeUser = new MutableLiveData<>();
+ authResult = new MutableLiveData<>();
+ }
+
+ public Intent getSignInIntent() {
+ return authRepository.getSignInIntent();
+ }
+
+ public void handleSignInResult(Intent data) {
+ authRepository.handleSignInResult(data)
+ .addOnSuccessListener(account -> {
+ User user = new User(
+ account.getId(),
+ account.getDisplayName(),
+ account.getEmail(),
+ account.getPhotoUrl() != null ? account.getPhotoUrl().toString() : null
+ );
+ userLiveData.setValue(user);
+ checkOrCreateUser(user);
+ })
+ .addOnFailureListener(e -> {
+ authResult.setValue(new AuthResult(false, e.getMessage()));
+ });
+ }
+
+ public GoogleSignInAccount getCurrentUser() {
+ return authRepository.getCurrentUser(getApplication());
+ }
+
+ public void checkUserStatus() {
+ GoogleSignInAccount account = getCurrentUser();
+ if (account != null) {
+ authRepository.getUserData(account.getId())
+ .addOnSuccessListener(documentSnapshot -> {
+ if (documentSnapshot.exists()) {
+ User user = documentSnapshot.toObject(User.class);
+ isFirstTimeUser.setValue(user != null && user.isFirstTimeUser());
+ } else {
+ // If document doesn't exist, treat as first time user
+ isFirstTimeUser.setValue(true);
+ }
+ })
+ .addOnFailureListener(e -> {
+ // Handle failure case
+ authResult.setValue(new AuthResult(false, e.getMessage()));
+ });
+ }
+ }
+
+ private void checkOrCreateUser(User user) {
+ authRepository.getUserData(user.getUserId())
+ .addOnSuccessListener(documentSnapshot -> {
+ if (!documentSnapshot.exists()) {
+ // New user
+ authRepository.createNewUser(user)
+ .addOnSuccessListener(aVoid -> {
+ isFirstTimeUser.setValue(true);
+ authResult.setValue(new AuthResult(true, null));
+ });
+ } else {
+ User existingUser = documentSnapshot.toObject(User.class);
+ isFirstTimeUser.setValue(existingUser != null && existingUser.isFirstTimeUser());
+ authResult.setValue(new AuthResult(true, null));
+ }
+ });
+ }
+
+ public LiveData getUserLiveData() {
+ return userLiveData;
+ }
+
+ public LiveData getIsFirstTimeUser() {
+ return isFirstTimeUser;
+ }
+
+ public LiveData getAuthResult() {
+ return authResult;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/base/BaseFragment.java b/app/src/main/java/com/elececo/umoney/ui/base/BaseFragment.java
new file mode 100644
index 0000000..d409396
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/base/BaseFragment.java
@@ -0,0 +1,23 @@
+package com.elececo.umoney.ui.base;
+
+import android.os.Bundle;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+public abstract class BaseFragment extends Fragment {
+ protected VM viewModel;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ viewModel = new ViewModelProvider(this).get(getViewModelClass());
+ setupObservers();
+ }
+
+ protected abstract Class getViewModelClass();
+ protected abstract void setupObservers();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/common/TransactionEntryDialog.java b/app/src/main/java/com/elececo/umoney/ui/common/TransactionEntryDialog.java
new file mode 100644
index 0000000..a0b2fe9
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/common/TransactionEntryDialog.java
@@ -0,0 +1,51 @@
+package com.elececo.umoney.ui.common;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.widget.AutoCompleteTextView;
+import androidx.annotation.NonNull;
+import com.elececo.umoney.R;
+import com.elececo.umoney.data.model.Transaction;
+import com.google.android.material.textfield.TextInputEditText;
+import java.util.Date;
+
+public class TransactionEntryDialog extends Dialog {
+ private final TransactionEntryListener listener;
+ private final String type; // "INCOME", "NEEDS", "WANTS", "SAVINGS"
+
+ public TransactionEntryDialog(@NonNull Context context, String type, TransactionEntryListener listener) {
+ super(context);
+ this.type = type;
+ this.listener = listener;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.dialog_transaction_entry);
+
+ setupViews();
+ setupListeners();
+ }
+
+ private void setupViews() {
+ // Initialize views and set up category adapter based on type
+ // TODO: Implement view setup
+ }
+
+ private void setupListeners() {
+ findViewById(R.id.save_button).setOnClickListener(v -> saveTransaction());
+ findViewById(R.id.cancel_button).setOnClickListener(v -> dismiss());
+ // TODO: Implement other listeners
+ }
+
+ private void saveTransaction() {
+ // Validate and create transaction
+ // TODO: Implement transaction saving
+ }
+
+ public interface TransactionEntryListener {
+ void onTransactionSaved(Transaction transaction);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/dashboard/DashboardActivity.java b/app/src/main/java/com/elececo/umoney/ui/dashboard/DashboardActivity.java
new file mode 100644
index 0000000..13efdec
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/dashboard/DashboardActivity.java
@@ -0,0 +1,134 @@
+package com.elececo.umoney.ui.dashboard;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.fragment.app.Fragment;
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.auth.GoogleLoginActivity;
+import com.elececo.umoney.ui.dashboard.fragments.DashboardFragment;
+import com.elececo.umoney.ui.needs.NeedsFragment;
+import com.elececo.umoney.ui.wants.WantsFragment;
+import com.elececo.umoney.ui.savings.SavingsFragment;
+import com.elececo.umoney.ui.income.IncomeFragment;
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.android.material.navigation.NavigationView;
+import com.google.firebase.auth.FirebaseAuth;
+
+public class DashboardActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
+
+ private DrawerLayout drawerLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_dashboard);
+
+ // Setup Toolbar
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ // Setup DrawerLayout and NavigationView
+ drawerLayout = findViewById(R.id.drawer_layout);
+ NavigationView navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ // Setup ActionBarDrawerToggle
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawerLayout, toolbar,
+ R.string.navigation_drawer_open,
+ R.string.navigation_drawer_close
+ );
+ drawerLayout.addDrawerListener(toggle);
+ toggle.syncState();
+
+ // Setup Bottom Navigation
+ BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
+ bottomNav.setOnItemSelectedListener(item -> {
+ Fragment selectedFragment = null;
+
+ int itemId = item.getItemId();
+ if (itemId == R.id.navigation_dashboard) {
+ selectedFragment = new DashboardFragment();
+ } else if (itemId == R.id.navigation_needs) {
+ selectedFragment = new NeedsFragment();
+ } else if (itemId == R.id.navigation_wants) {
+ selectedFragment = new WantsFragment();
+ } else if (itemId == R.id.navigation_savings) {
+ selectedFragment = new SavingsFragment();
+ } else if (itemId == R.id.navigation_income) {
+ selectedFragment = new IncomeFragment();
+ }
+
+ if (selectedFragment != null) {
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.fragment_container, selectedFragment)
+ .commit();
+ }
+
+ return true;
+ });
+
+ // Set default fragment
+ if (savedInstanceState == null) {
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.fragment_container, new DashboardFragment())
+ .commit();
+ }
+
+ // Setup Navigation Header
+ setupNavHeader(navigationView);
+ }
+
+ private void setupNavHeader(NavigationView navigationView) {
+ View headerView = navigationView.getHeaderView(0);
+ TextView nameTextView = headerView.findViewById(R.id.nav_header_name);
+ TextView emailTextView = headerView.findViewById(R.id.nav_header_email);
+
+ // TODO: Get user data from FirebaseAuth and update header
+ FirebaseAuth auth = FirebaseAuth.getInstance();
+ if (auth.getCurrentUser() != null) {
+ nameTextView.setText(auth.getCurrentUser().getDisplayName());
+ emailTextView.setText(auth.getCurrentUser().getEmail());
+ }
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ int itemId = item.getItemId();
+
+ if (itemId == R.id.nav_profile) {
+ // TODO: Navigate to Profile
+ } else if (itemId == R.id.nav_settings) {
+ // TODO: Navigate to Settings
+ } else if (itemId == R.id.nav_categories) {
+ // TODO: Navigate to Categories
+ } else if (itemId == R.id.nav_about) {
+ // TODO: Navigate to About
+ } else if (itemId == R.id.nav_logout) {
+ FirebaseAuth.getInstance().signOut();
+ startActivity(new Intent(this, GoogleLoginActivity.class));
+ finish();
+ }
+
+ drawerLayout.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
+ drawerLayout.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/dashboard/fragments/DashboardFragment.java b/app/src/main/java/com/elececo/umoney/ui/dashboard/fragments/DashboardFragment.java
new file mode 100644
index 0000000..aaa4e96
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/dashboard/fragments/DashboardFragment.java
@@ -0,0 +1,27 @@
+package com.elececo.umoney.ui.dashboard.fragments;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.base.BaseFragment;
+import com.elececo.umoney.ui.dashboard.viewmodel.DashboardViewModel;
+
+public class DashboardFragment extends BaseFragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_dashboard, container, false);
+ }
+
+ @Override
+ protected Class getViewModelClass() {
+ return DashboardViewModel.class;
+ }
+
+ @Override
+ protected void setupObservers() {
+ // TODO: Setup observers for dashboard data
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/dashboard/viewmodel/DashboardViewModel.java b/app/src/main/java/com/elececo/umoney/ui/dashboard/viewmodel/DashboardViewModel.java
new file mode 100644
index 0000000..dc9e201
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/dashboard/viewmodel/DashboardViewModel.java
@@ -0,0 +1,7 @@
+package com.elececo.umoney.ui.dashboard.viewmodel;
+
+import androidx.lifecycle.ViewModel;
+
+public class DashboardViewModel extends ViewModel {
+ // TODO: Implement dashboard logic
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/income/IncomeFragment.java b/app/src/main/java/com/elececo/umoney/ui/income/IncomeFragment.java
new file mode 100644
index 0000000..9cb23c9
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/income/IncomeFragment.java
@@ -0,0 +1,27 @@
+package com.elececo.umoney.ui.income;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.base.BaseFragment;
+import com.elececo.umoney.ui.income.viewmodel.IncomeViewModel;
+
+public class IncomeFragment extends BaseFragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_income, container, false);
+ }
+
+ @Override
+ protected Class getViewModelClass() {
+ return IncomeViewModel.class;
+ }
+
+ @Override
+ protected void setupObservers() {
+ // TODO: Setup observers for income data
+ }
+}
diff --git a/app/src/main/java/com/elececo/umoney/ui/income/viewmodel/IncomeViewModel.java b/app/src/main/java/com/elececo/umoney/ui/income/viewmodel/IncomeViewModel.java
new file mode 100644
index 0000000..eec6a27
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/income/viewmodel/IncomeViewModel.java
@@ -0,0 +1,6 @@
+package com.elececo.umoney.ui.income.viewmodel;
+
+import androidx.lifecycle.ViewModel;
+
+public class IncomeViewModel extends ViewModel {
+}
diff --git a/app/src/main/java/com/elececo/umoney/ui/needs/NeedsFragment.java b/app/src/main/java/com/elececo/umoney/ui/needs/NeedsFragment.java
new file mode 100644
index 0000000..125c201
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/needs/NeedsFragment.java
@@ -0,0 +1,27 @@
+package com.elececo.umoney.ui.needs;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.base.BaseFragment;
+import com.elececo.umoney.ui.needs.viewmodel.NeedsViewModel;
+
+public class NeedsFragment extends BaseFragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_needs, container, false);
+ }
+
+ @Override
+ protected Class getViewModelClass() {
+ return NeedsViewModel.class;
+ }
+
+ @Override
+ protected void setupObservers() {
+ // TODO: Setup observers for needs data
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/needs/viewmodel/NeedsViewModel.java b/app/src/main/java/com/elececo/umoney/ui/needs/viewmodel/NeedsViewModel.java
new file mode 100644
index 0000000..d2a82ac
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/needs/viewmodel/NeedsViewModel.java
@@ -0,0 +1,6 @@
+package com.elececo.umoney.ui.needs.viewmodel;
+
+import androidx.lifecycle.ViewModel;
+
+public class NeedsViewModel extends ViewModel {
+}
diff --git a/app/src/main/java/com/elececo/umoney/ui/savings/SavingsFragment.java b/app/src/main/java/com/elececo/umoney/ui/savings/SavingsFragment.java
new file mode 100644
index 0000000..235154a
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/savings/SavingsFragment.java
@@ -0,0 +1,49 @@
+package com.elececo.umoney.ui.savings;
+
+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 com.elececo.umoney.R;
+import com.elececo.umoney.ui.base.BaseFragment;
+import com.elececo.umoney.ui.savings.viewmodel.SavingsViewModel;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+public class SavingsFragment extends BaseFragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_savings, container, false);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // Set card title
+ TextView cardTitle = view.findViewById(R.id.card_title);
+ cardTitle.setText("Savings Summary");
+
+ // Set FAB icon for deducting funds
+ FloatingActionButton fab = view.findViewById(R.id.fab_action);
+ fab.setImageResource(R.drawable.ic_remove);
+ fab.setOnClickListener(v -> showDeductDialog());
+ }
+
+ @Override
+ protected Class getViewModelClass() {
+ return SavingsViewModel.class;
+ }
+
+ @Override
+ protected void setupObservers() {
+ // TODO: Setup observers for savings data
+ }
+
+ private void showDeductDialog() {
+ // TODO: Implement deduct funds dialog according to CoreLogic.md
+ }
+}
diff --git a/app/src/main/java/com/elececo/umoney/ui/savings/viewmodel/SavingsViewModel.java b/app/src/main/java/com/elececo/umoney/ui/savings/viewmodel/SavingsViewModel.java
new file mode 100644
index 0000000..561ee2f
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/savings/viewmodel/SavingsViewModel.java
@@ -0,0 +1,6 @@
+package com.elececo.umoney.ui.savings.viewmodel;
+
+import androidx.lifecycle.ViewModel;
+
+public class SavingsViewModel extends ViewModel {
+}
diff --git a/app/src/main/java/com/elececo/umoney/ui/splash/SplashActivity.java b/app/src/main/java/com/elececo/umoney/ui/splash/SplashActivity.java
new file mode 100644
index 0000000..bdddbd2
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/splash/SplashActivity.java
@@ -0,0 +1,74 @@
+package com.elececo.umoney.ui.splash;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.lifecycle.ViewModelProvider;
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.auth.GoogleLoginActivity;
+import com.elececo.umoney.ui.auth.UserSetupActivity;
+import com.elececo.umoney.ui.dashboard.DashboardActivity;
+import com.elececo.umoney.ui.auth.viewmodel.AuthViewModel;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+
+public class SplashActivity extends AppCompatActivity {
+ private static final long SPLASH_DELAY = 2000; // 2 seconds
+ private AuthViewModel authViewModel;
+ private final Handler handler = new Handler();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_splash);
+
+ authViewModel = new ViewModelProvider(this).get(AuthViewModel.class);
+
+ handler.postDelayed(this::checkAuthStatus, SPLASH_DELAY);
+ }
+
+ private void checkAuthStatus() {
+ GoogleSignInAccount currentUser = authViewModel.getCurrentUser();
+ if (currentUser == null) {
+ startLoginActivity();
+ } else {
+ checkUserStatus();
+ }
+ }
+
+ private void checkUserStatus() {
+ authViewModel.getIsFirstTimeUser().observe(this, isFirstTime -> {
+ if (isFirstTime) {
+ startSetupActivity();
+ } else {
+ startMainActivity();
+ }
+ });
+
+ authViewModel.checkUserStatus();
+ }
+
+ private void startLoginActivity() {
+ Intent intent = new Intent(this, GoogleLoginActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ private void startSetupActivity() {
+ Intent intent = new Intent(this, UserSetupActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ private void startMainActivity() {
+ Intent intent = new Intent(this, DashboardActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ handler.removeCallbacksAndMessages(null);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/elececo/umoney/ui/wants/WantsFragment.java b/app/src/main/java/com/elececo/umoney/ui/wants/WantsFragment.java
new file mode 100644
index 0000000..1c5b11f
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/wants/WantsFragment.java
@@ -0,0 +1,27 @@
+package com.elececo.umoney.ui.wants;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.elececo.umoney.R;
+import com.elececo.umoney.ui.base.BaseFragment;
+import com.elececo.umoney.ui.wants.viewmodel.WantsViewModel;
+
+public class WantsFragment extends BaseFragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_wants, container, false);
+ }
+
+ @Override
+ protected Class getViewModelClass() {
+ return WantsViewModel.class;
+ }
+
+ @Override
+ protected void setupObservers() {
+ // TODO: Setup observers for wants data
+ }
+}
diff --git a/app/src/main/java/com/elececo/umoney/ui/wants/viewmodel/WantsViewModel.java b/app/src/main/java/com/elececo/umoney/ui/wants/viewmodel/WantsViewModel.java
new file mode 100644
index 0000000..560bba6
--- /dev/null
+++ b/app/src/main/java/com/elececo/umoney/ui/wants/viewmodel/WantsViewModel.java
@@ -0,0 +1,7 @@
+package com.elececo.umoney.ui.wants.viewmodel;
+
+import androidx.lifecycle.ViewModel;
+
+public class WantsViewModel extends ViewModel {
+ // TODO: Implement wants logic
+}
diff --git a/app/src/main/java/com/elececo/umoney/viewmodel/TransactionViewModel.java b/app/src/main/java/com/elececo/umoney/viewmodel/TransactionViewModel.java
deleted file mode 100644
index 04df582..0000000
--- a/app/src/main/java/com/elececo/umoney/viewmodel/TransactionViewModel.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.elececo.umoney.viewmodel;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.ViewModel;
-
-import com.elececo.umoney.model.Transaction;
-import com.elececo.umoney.repository.TransactionRepository;
-
-import java.util.List;
-
-public class TransactionViewModel extends ViewModel {
- private TransactionRepository repository;
- private LiveData> transactions;
-
- public TransactionViewModel() {
- repository = new TransactionRepository();
- }
-
- public LiveData> getTransactions() {
- return transactions;
- }
-
- public void loadTransactionsByCategory(String category) {
- transactions = repository.getTransactionsByCategory(category);
- }
-
- public void addTransaction(Transaction transaction) {
- repository.addTransaction(transaction);
- }
-}
diff --git a/app/src/main/res/drawable/b1.xml b/app/src/main/res/drawable/b1.xml
index 05461a8..684a8a1 100644
--- a/app/src/main/res/drawable/b1.xml
+++ b/app/src/main/res/drawable/b1.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/app/src/main/res/drawable/category.xml b/app/src/main/res/drawable/category.xml
new file mode 100644
index 0000000..76b250d
--- /dev/null
+++ b/app/src/main/res/drawable/category.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml
new file mode 100644
index 0000000..9f83b8f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_remove.xml b/app/src/main/res/drawable/ic_remove.xml
new file mode 100644
index 0000000..04c262d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_remove.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/income.xml b/app/src/main/res/drawable/income.xml
new file mode 100644
index 0000000..8dec161
--- /dev/null
+++ b/app/src/main/res/drawable/income.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/info.xml b/app/src/main/res/drawable/info.xml
new file mode 100644
index 0000000..ef3a1fe
--- /dev/null
+++ b/app/src/main/res/drawable/info.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logout.xml b/app/src/main/res/drawable/logout.xml
new file mode 100644
index 0000000..c22a96f
--- /dev/null
+++ b/app/src/main/res/drawable/logout.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/settings.xml b/app/src/main/res/drawable/settings.xml
new file mode 100644
index 0000000..6593f3a
--- /dev/null
+++ b/app/src/main/res/drawable/settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_dashboard.xml b/app/src/main/res/layout/activity_dashboard.xml
new file mode 100644
index 0000000..3dfffbf
--- /dev/null
+++ b/app/src/main/res/layout/activity_dashboard.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_google_login.xml b/app/src/main/res/layout/activity_google_login.xml
index 3a0ec87..70fecb8 100644
--- a/app/src/main/res/layout/activity_google_login.xml
+++ b/app/src/main/res/layout/activity_google_login.xml
@@ -1,57 +1,43 @@
-
+ android:padding="16dp">
-
-
+ app:layout_constraintTop_toBottomOf="@+id/logo" />
+ app:layout_constraintTop_toBottomOf="@+id/welcome_text" />
-
+
\ 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
deleted file mode 100644
index 30698d4..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml
new file mode 100644
index 0000000..47db513
--- /dev/null
+++ b/app/src/main/res/layout/activity_splash.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_add_transaction.xml b/app/src/main/res/layout/activity_user_setup.xml
similarity index 51%
rename from app/src/main/res/layout/activity_add_transaction.xml
rename to app/src/main/res/layout/activity_user_setup.xml
index 7c52e05..399398a 100644
--- a/app/src/main/res/layout/activity_add_transaction.xml
+++ b/app/src/main/res/layout/activity_user_setup.xml
@@ -1,30 +1,30 @@
-
+ android:hint="Monthly Income"
+ android:inputType="numberDecimal"/>
+ android:hint="Employment Type"
+ android:layout_marginTop="16dp"/>
-
-
+ android:text="Continue"
+ android:layout_marginTop="24dp"/>
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_transaction_entry.xml b/app/src/main/res/layout/dialog_transaction_entry.xml
new file mode 100644
index 0000000..1bde160
--- /dev/null
+++ b/app/src/main/res/layout/dialog_transaction_entry.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
index 570d9ee..6addc01 100644
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -1,17 +1,9 @@
-
+
+
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_income.xml b/app/src/main/res/layout/fragment_income.xml
new file mode 100644
index 0000000..9f8dea5
--- /dev/null
+++ b/app/src/main/res/layout/fragment_income.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_needs.xml b/app/src/main/res/layout/fragment_needs.xml
index 0a58f38..5d164c1 100644
--- a/app/src/main/res/layout/fragment_needs.xml
+++ b/app/src/main/res/layout/fragment_needs.xml
@@ -1,16 +1,12 @@
-
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_savings.xml b/app/src/main/res/layout/fragment_savings.xml
index 0a58f38..5d164c1 100644
--- a/app/src/main/res/layout/fragment_savings.xml
+++ b/app/src/main/res/layout/fragment_savings.xml
@@ -1,16 +1,12 @@
-
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_wants.xml b/app/src/main/res/layout/fragment_wants.xml
index 0a58f38..2899a18 100644
--- a/app/src/main/res/layout/fragment_wants.xml
+++ b/app/src/main/res/layout/fragment_wants.xml
@@ -1,16 +1,9 @@
-
+
+
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_transaction.xml b/app/src/main/res/layout/item_transaction.xml
deleted file mode 100644
index d5c789c..0000000
--- a/app/src/main/res/layout/item_transaction.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/layout_transaction_base.xml b/app/src/main/res/layout/layout_transaction_base.xml
new file mode 100644
index 0000000..7802755
--- /dev/null
+++ b/app/src/main/res/layout/layout_transaction_base.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_transaction_card.xml b/app/src/main/res/layout/layout_transaction_card.xml
new file mode 100644
index 0000000..b38b3b5
--- /dev/null
+++ b/app/src/main/res/layout/layout_transaction_card.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml
new file mode 100644
index 0000000..dd28853
--- /dev/null
+++ b/app/src/main/res/layout/nav_header.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/transaction_buttons.xml b/app/src/main/res/layout/transaction_buttons.xml
deleted file mode 100644
index 947b332..0000000
--- a/app/src/main/res/layout/transaction_buttons.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml
new file mode 100644
index 0000000..4fd74b0
--- /dev/null
+++ b/app/src/main/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,27 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/bottomnavmenu.xml b/app/src/main/res/menu/bottomnavmenu.xml
deleted file mode 100644
index b91c9bc..0000000
--- a/app/src/main/res/menu/bottomnavmenu.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/drawer_menu.xml b/app/src/main/res/menu/drawer_menu.xml
new file mode 100644
index 0000000..fe78958
--- /dev/null
+++ b/app/src/main/res/menu/drawer_menu.xml
@@ -0,0 +1,30 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/home_left_menu.xml b/app/src/main/res/menu/home_left_menu.xml
deleted file mode 100644
index 11785dd..0000000
--- a/app/src/main/res/menu/home_left_menu.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/logout.xml b/app/src/main/res/menu/logout.xml
deleted file mode 100644
index 0a5399d..0000000
--- a/app/src/main/res/menu/logout.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 82db521..58bb097 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,28 +1,31 @@
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
+
+ #0052CC
+ #002984
+ #5479FF
+
+ #3498DB
+ #2980B9
+ #64B5F6
- #0052CC
- #3498DB
- #FFFFFF
-
-
- #0052CC
- #3498DB
- #FFFFFF
-
-
- #002984
- #2980B9
- #121212
+
+ #FFFFFF
+ #FFFFFF
+ #B00020
+ #000000
+
#4CAF50
#E53935
+
+ #000000
+ #FFFFFF
+ #00000000
+
+ #121212
+ #1E1E1E
+ #FFFFFF
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0e464c2..b1834b5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -52,8 +52,13 @@
- Open Drawer
- Close Drawer
+ Open navigation drawer
+ Close navigation drawer
+ About UMoney
+ UMoney helps you track and manage your finances by categorizing transactions into needs, wants, and savings.
+ Logout
+ Are you sure you want to logout?
+ Logout
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 2c97e95..f20a287 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,14 +1,33 @@
-
-
- #000000
- #FFFFFF
-
- #FFFFFF
- #333333
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index ec0af7e..998d5c5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,10 +7,11 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:8.6.0'
classpath 'com.google.gms:google-services:4.4.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21" // Add this line
}
}
+
+
task clean(type: Delete) {
delete rootProject.buildDir
}
\ No newline at end of file
diff --git a/doc/Architecture.md b/doc/Architecture.md
new file mode 100644
index 0000000..f81eadd
--- /dev/null
+++ b/doc/Architecture.md
@@ -0,0 +1,153 @@
+# Architecture
+
+This document provides an overview of the architecture and core components used in the project. It’s designed to help contributors understand the project's structure, data flow, and key architectural decisions.
+
+## 1. Overall Design Pattern
+
+The project follows the *MVVM (Model-View-ViewModel)* pattern. This design pattern is chosen to separate UI and business logic, making it easier to maintain and test the code.
+
+- *Current Status*: We are not committed to specific frameworks or libraries yet. As development progresses, we may integrate libraries to streamline MVVM implementation, such as using LiveData, ViewModel, or other Android Jetpack components.
+
+## 2. Modules or Layers
+
+Our app is structured into several logical layers, as follows:
+
+- *UI Layer (View)*: Contains Activities, Fragments, and other UI components. This layer is responsible for rendering data and handling user interactions.
+
+- *ViewModel Layer*: The ViewModel holds UI-related data that survives configuration changes. It interacts with the Repository to retrieve and process data for the View layer.
+
+- *Data Layer*: Currently, Firestore is used as the primary database to manage user data. The Data layer will evolve to include data caching and offline support as the app develops.
+
+- *Future Enhancements*: We may add separate modules as the app grows, such as modules for specific features or different data sources (e.g., SMS, email, API).
+
+### 2.1 Package Structure
+
+The project follows this package structure:
+
+- **data/**: Contains all data-related components
+ - `local/`: Local storage components (Room DB, SharedPreferences)
+ - `remote/`: Network and Firebase interactions
+ - `model/`: Data models/entities
+ - `repository/`: Single source of truth for data operations
+
+- **di/**: Dependency injection modules
+
+- **ui/**: User interface components organized by features
+ - Each feature (auth, dashboard, needs, etc.) contains:
+ - Activities/Fragments
+ - ViewModels
+ - Adapters for RecyclerViews
+
+- **utils/**: Helper classes and utility functions
+
+app/src/main/java/com/elececo/umoney/
+├── data/
+│ ├── local/
+│ │ ├── dao/
+│ │ ├── entity/
+│ │ └── preferences/
+│ ├── remote/
+│ │ ├── api/
+│ │ └── firebase/
+│ ├── model/
+│ │ ├── Transaction.java
+│ │ ├── User.java
+│ │ └── Category.java
+│ └── repository/
+│ ├── TransactionRepository.java
+│ └── UserRepository.java
+├── di/
+│ └── AppModule.java
+├── ui/
+│ ├── auth/
+│ │ ├── GoogleLoginActivity.java
+│ │ └── viewmodel/
+│ │ └── AuthViewModel.java
+│ ├── dashboard/
+│ │ ├── DashboardActivity.java
+│ │ ├── adapter/
+│ │ └── viewmodel/
+│ │ └── DashboardViewModel.java
+│ ├── needs/
+│ │ ├── NeedsFragment.java
+│ │ ├── adapter/
+│ │ └── viewmodel/
+│ │ └── NeedsViewModel.java
+│ ├── wants/
+│ │ ├── WantsFragment.java
+│ │ ├── adapter/
+│ │ └── viewmodel/
+│ │ └── WantsViewModel.java
+│ └── savings/
+│ ├── SavingsFragment.java
+│ ├── adapter/
+│ └── viewmodel/
+│ └── SavingsViewModel.java
+└── utils/
+ ├── Constants.java
+ ├── DateUtils.java
+ └── CurrencyUtils.java
+
+## 3. Data Flow
+
+Data in this app can enter through two primary means:
+
+- *Manual Entry*: Users can manually input data, which will be prioritized in the initial development phases.
+
+- *Automated Detection*: Later on, we will add automation to capture data from external sources such as SMS, email, and APIs.
+
+For now, the ViewModel and Repository components will manage the flow of data between Firestore and the UI layer.
+
+## 4. Navigation
+
+The app will use a combination of *Bottom Navigation* and *Drawer Navigation*:
+
+- *Bottom Navigation: This menu includes tabs for *Dashboard, Needs, Wants, Savings, and Income.
+ - Each tab (except Dashboard) will have a uniform data entry screen, allowing users to input related transactions.
+ - The Income tab will auto-distribute the entered amount based on preset percentages: 50% for Needs, 30% for Wants, and 20% for Savings. Users can modify these percentages in the *Settings*, accessible via the left-side drawer.
+
+- *Drawer Navigation*: The left-side drawer provides access to settings and other configuration options.
+
+Each screen will have a summary card showing transaction data for that category.
+
+## 5. Error Handling and Logging
+
+To prevent app crashes and improve debugging:
+
+- *Error Checks*: Code should include checks to validate data types and expected formats. For example, when a function expects an integer, code should verify that it’s receiving an integer, not a string or double.
+
+This approach will help identify issues early and make the app more robust.
+
+## 6. Threading and Concurrency
+
+*Status*: No specific threading or concurrency strategy has been decided yet.
+
+Asynchronous tasks will likely be handled using *coroutines* or *RxJava* to ensure smooth performance. We’ll determine the best approach based on the final project requirements.
+
+## 7. Caching and Data Persistence
+
+The app supports *offline functionality* so that users can still access data without an internet connection.
+
+- *Syncing with Firestore*: Once the user regains internet access, data changes will sync with Firestore.
+
+- *User Profile*: User-specific data, such as profile details and income preferences, are stored in local storage (e.g., SharedPreferences) and will sync with Firestore as needed.
+
+- *Auto Refresh*: Data will refresh automatically if there are updates in Firestore or the user manually enters new data.
+
+## 8. Testing
+
+*Planned Testing Approach*:
+- Testing will begin after the manual data entry system is fully implemented.
+- We aim to incorporate both manual and automated tests to validate data entry and synchronization with Firestore.
+
+Currently, we have not yet selected specific testing tools or frameworks. This section will be updated as we define our testing strategy.
+
+## 9. Security Considerations
+
+*Data Security*: User data security is a priority, especially since the app will be used by multiple individuals.
+
+- *User Authentication*: Google Sign-In is required to access the app. Users cannot proceed without logging in.
+
+- *Data Privacy*: Each user’s data is isolated to prevent unauthorized access. Only developers may access user data for testing and development purposes, adhering to data privacy standards.
+
+As the app evolves, additional security measures will be implemented to safeguard user data.
\ No newline at end of file
diff --git a/doc/CoreLogic.md b/doc/CoreLogic.md
new file mode 100644
index 0000000..bf2b531
--- /dev/null
+++ b/doc/CoreLogic.md
@@ -0,0 +1,129 @@
+# Core Logic Documentation
+
+This document outlines the core functionalities and business logic that power the Umoney app. It covers transaction flow, budget allocation, error handling, categorization, and other essential logic that drives the app’s user experience and performance.
+
+---
+
+## 1. Transaction Flow
+
+### 1.1 First-Time User Setup
+When a user starts the app for the first time, they will be guided through a **setup wizard**. The wizard collects essential information such as:
+- Name
+- Date of Birth (DOB), with automatic age calculation
+- Employment type (salaried, business, self-employed, etc.)
+- Approximate monthly income and expenses
+
+This information is saved for future reference and can be modified later through the **Settings** page. The app also supports international users, allowing them to choose their preferred **currency type**, with a default set based on their locale.
+
+### 1.2 Navigation and Ledger System
+The app uses a **bottom navigation menu** with five tabs:
+- **Dashboard**
+- **Needs**
+- **Wants**
+- **Savings**
+- **Income**
+
+Each tab (except for the dashboard) has the following structure:
+- A **transaction summary card** showing "In" (Income), "Out" (Expenses), and "Hold" (Balance).
+- A **RecyclerView** displaying the list of transactions.
+- A **"+" or "-" button** for adding or removing funds.
+ - **Income Tab**: "+" button to add income entries.
+ - **Needs, Wants, and Savings Tabs**: "-" button to deduct funds.
+
+### 1.3 Transaction Details for "-" (Expense)
+When users click the "-" button, they can input the following details for a transaction:
+1. **Amount**: The expense value.
+2. **Date and Time**: Defaults to the current timestamp but can be changed.
+3. **Given To**: A list of common recipients, such as contacts, UPI IDs, bank accounts, or merchants like Amazon or Flipkart, is maintained. Suggestions are made based on frequently used recipients.
+4. **Category**: A list of predefined categories is available, specific to each tab (e.g., food, rent, groceries for Needs). Users can also create custom categories.
+5. **Attachment (optional)**: Users can attach receipts, images, or other relevant files for reference.
+
+### 1.4 Transaction Logic
+- **Income Tab**: Funds are added via the "+" button. Once income is received, it can be distributed into the Needs, Wants, and Savings tabs for spending.
+- **Needs, Wants, and Savings Tabs**: Funds are deducted via the "-" button for specific expenses (e.g., rent, groceries). The app ensures expenses are taken from the correct ledger.
+
+---
+
+## 2. Budget Allocation (20-30-50 Rule)
+
+Upon initial setup, the app explains the **20-30-50 Rule**, which allocates:
+- 50% of income to **Needs**
+- 30% to **Wants**
+- 20% to **Savings**
+
+Users can modify these percentages during setup or later via the **Settings** page. The app ensures that the sum of the three categories is always **100%**.
+
+---
+
+## 3. Error Handling
+
+The app uses **robust validation** to ensure data integrity. Common examples include:
+- **Budget Allocation**: The total percentage for Needs, Wants, and Savings must equal 100%. If the user enters values that do not sum to 100%, an error will prompt them to adjust the inputs.
+- **Transaction Entry**: Negative entries are not allowed in income, and incorrect formats or missing fields are validated with prompts.
+- **General Data Handling**: Any invalid data or errors in the flow are caught and handled gracefully with appropriate error messages and suggestions for correction.
+
+---
+
+## 4. Categorization of Expenses
+
+Each tab has predefined **categories** to ensure clarity:
+- **Needs**: Food, Groceries, Rent, Personal Care, etc.
+- **Wants**: Entertainment, Dining Out, etc.
+- **Savings**: Emergency Fund, Investments, etc.
+
+Some categories, like **Food**, may appear in multiple tabs, allowing users flexibility. Users can also create **custom categories** to suit their needs.
+
+---
+
+## 5. Manual Entry vs. Automated Entry
+
+### 5.1 Manual Entry
+Manual entries follow the same parameters outlined above and are saved directly into the corresponding tab (Needs, Wants, Savings, or Income).
+
+### 5.2 Automated Entry
+Automated transactions (via SMS, email, or future API integrations) are initially categorized as **uncategorized** and appear at the bottom of the **Dashboard**. Users must manually assign these entries to the appropriate tab (Needs, Wants, Savings, or Income) before they are finalized.
+
+---
+
+## 6. Real-Time Updates
+
+The app implements **real-time synchronization** of transaction data with the database (Firestore). Data refresh occurs automatically when:
+- The user manually enters new transactions.
+- Updates happen through automated SMS/email (future development).
+
+---
+
+## 7. Data Synchronization
+
+Best practices are used to ensure **data consistency** and minimize **API calls to Firestore**. Synchronization happens automatically to keep local and remote data in sync, and offline access will be supported with a **caching mechanism**.
+
+---
+
+## 8. Security and User Data Protection
+
+### 8.1 User Data Protection
+Each user's financial and personal information is stored in a **secure directory** that only they can access. Users cannot view or interact with each other's data. All sensitive data (e.g., bank details) is **encrypted**.
+
+### 8.2 Authentication and Access Control
+The app uses **Google Authentication** to ensure that only the user can access their account. Sessions are protected, and re-authentication is required after session expiration.
+
+---
+
+## 9. Future Development
+
+### 9.1 Investment and Goal Tracking
+The manual entry system is a priority, but future updates will include tracking for investments and financial goals. **High-debt loans** will take priority in the tracking system, ensuring users focus on paying off debt before pursuing other goals.
+
+### 9.2 AI and Machine Learning
+Future versions will integrate **AI and machine learning** for better predictions of spending patterns and investment suggestions.
+
+---
+
+## 10. Performance Optimizations
+
+Optimizations are applied to minimize **Firestore API calls** and improve app responsiveness. For example, transaction data is retrieved and cached locally where possible, and API calls are batched to reduce overhead.
+
+---
+
+This documentation will continue to evolve as more features are developed and feedback is received from users.
+