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"/>