This commit is contained in:
2026-01-27 16:39:04 +00:00
commit 9095806bf3
722 changed files with 60943 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
package com.example.cuida;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.example.cuida.databinding.ActivityMainBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.example.cuida.ui.auth.LoginActivity;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check if user is logged in
boolean isLoggedIn = getSharedPreferences("prefs", Context.MODE_PRIVATE)
.getBoolean("is_logged_in", false);
if (!isLoggedIn) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();
return;
}
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
BottomNavigationView navView = binding.navView;
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
.findFragmentById(R.id.nav_host_fragment);
if (navHostFragment != null) {
NavController navController = navHostFragment.getNavController();
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_appointments, R.id.navigation_medication,
R.id.navigation_sns24, R.id.navigation_profile)
.build();
// NavigationUI.setupActionBarWithNavController(this, navController,
// appBarConfiguration);
NavigationUI.setupWithNavController(binding.navView, navController);
}
}
}

View File

@@ -0,0 +1,65 @@
package com.example.cuida.data;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.annotation.NonNull;
import com.example.cuida.data.dao.UserDao;
import com.example.cuida.data.dao.AppointmentDao;
import com.example.cuida.data.dao.MedicationDao;
import com.example.cuida.data.model.User;
import com.example.cuida.data.model.Appointment;
import com.example.cuida.data.model.Medication;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Database(entities = { User.class, Appointment.class, Medication.class }, version = 3, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
public abstract AppointmentDao appointmentDao();
public abstract MedicationDao medicationDao();
private static volatile AppDatabase INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
public static AppDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "cuida_database")
.fallbackToDestructiveMigration()
.addCallback(sRoomDatabaseCallback)
.build();
}
}
}
return INSTANCE;
}
private static RoomDatabase.Callback sRoomDatabaseCallback = new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
databaseWriteExecutor.execute(() -> {
// Populate the database in the background.
MedicationDao medDao = INSTANCE.medicationDao();
medDao.insert(new Medication("Paracetamol", "08:00", "1 comp", "Tomar com água"));
medDao.insert(new Medication("Ibuprofeno", "14:00", "1 comp", "Após refeição"));
medDao.insert(new Medication("Vitamina C", "20:00", "1 comp", "Antes de dormir"));
AppointmentDao apptDao = INSTANCE.appointmentDao();
apptDao.insert(new Appointment("Medicina Geral", "25/01/2026", "10:00", false));
apptDao.insert(new Appointment("Cardiologia", "02/02/2026", "15:30", false));
apptDao.insert(new Appointment("Oftalmologia", "10/01/2025", "09:00", true));
});
}
};
}

View File

@@ -0,0 +1,23 @@
package com.example.cuida.data.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import com.example.cuida.data.model.Appointment;
import java.util.List;
@Dao
public interface AppointmentDao {
@Insert
void insert(Appointment appointment);
@Query("SELECT * FROM appointments WHERE isPast = 0 ORDER BY date ASC, time ASC")
LiveData<List<Appointment>> getFutureAppointments();
@Query("SELECT * FROM appointments WHERE isPast = 1 ORDER BY date DESC, time DESC")
LiveData<List<Appointment>> getPastAppointments();
@Query("SELECT time FROM appointments WHERE date = :date")
List<String> getBookedTimesForDate(String date);
}

View File

@@ -0,0 +1,23 @@
package com.example.cuida.data.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import com.example.cuida.data.model.Medication;
import java.util.List;
@Dao
public interface MedicationDao {
@Insert
void insert(Medication medication);
@androidx.room.Update
void update(Medication medication);
@Query("SELECT * FROM medications ORDER BY time ASC")
LiveData<List<Medication>> getAllMedications();
@Query("SELECT * FROM medications ORDER BY time ASC LIMIT 1")
LiveData<Medication> getNextMedication();
}

View File

@@ -0,0 +1,26 @@
package com.example.cuida.data.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import androidx.room.OnConflictStrategy;
import com.example.cuida.data.model.User;
@Dao
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(User user);
@Update
void update(User user);
@Query("SELECT * FROM users WHERE email = :email AND password = :password LIMIT 1")
User login(String email, String password);
@Query("SELECT * FROM users WHERE email = :email LIMIT 1")
User checkUser(String email);
@Query("delete from users")
void deleteAll();
}

View File

@@ -0,0 +1,22 @@
package com.example.cuida.data.model;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "appointments")
public class Appointment {
@PrimaryKey(autoGenerate = true)
public int id;
public String type; // e.g. "Medicina Geral", "Cardiologia"
public String date; // dd/MM/yyyy
public String time; // HH:mm
public boolean isPast;
public Appointment(String type, String date, String time, boolean isPast) {
this.type = type;
this.date = date;
this.time = time;
this.isPast = isPast;
}
}

View File

@@ -0,0 +1,24 @@
package com.example.cuida.data.model;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "medications")
public class Medication {
@PrimaryKey(autoGenerate = true)
public int id;
public String name;
public String time; // HH:mm
public String dosage; // e.g. "1 compprimido"
public String notes;
public boolean isTaken;
public Medication(String name, String time, String dosage, String notes) {
this.name = name;
this.time = time;
this.dosage = dosage;
this.notes = notes;
this.isTaken = false;
}
}

View File

@@ -0,0 +1,24 @@
package com.example.cuida.data.model;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
public int uid;
public String name;
public String email;
public String password;
public int age;
public String utenteNumber;
public User(String name, String email, String password, int age, String utenteNumber) {
this.name = name;
this.email = email;
this.password = password;
this.age = age;
this.utenteNumber = utenteNumber;
}
}

View File

@@ -0,0 +1,53 @@
package com.example.cuida.ui.appointments;
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.example.cuida.R;
import com.example.cuida.data.model.Appointment;
import java.util.ArrayList;
import java.util.List;
public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.AppointmentViewHolder> {
private List<Appointment> appointmentList = new ArrayList<>();
public void setAppointments(List<Appointment> appointments) {
this.appointmentList = appointments;
notifyDataSetChanged();
}
@NonNull
@Override
public AppointmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_appointment, parent, false);
return new AppointmentViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull AppointmentViewHolder holder, int position) {
Appointment appointment = appointmentList.get(position);
holder.textType.setText(appointment.type);
holder.textDate.setText(appointment.date);
holder.textTime.setText(appointment.time);
}
@Override
public int getItemCount() {
return appointmentList.size();
}
public static class AppointmentViewHolder extends RecyclerView.ViewHolder {
TextView textType, textDate, textTime;
public AppointmentViewHolder(@NonNull View itemView) {
super(itemView);
textType = itemView.findViewById(R.id.text_type);
textDate = itemView.findViewById(R.id.text_date);
textTime = itemView.findViewById(R.id.text_time);
}
}
}

View File

@@ -0,0 +1,56 @@
package com.example.cuida.ui.appointments;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.example.cuida.R;
import com.example.cuida.databinding.FragmentAppointmentsBinding;
public class AppointmentsFragment extends Fragment {
private FragmentAppointmentsBinding binding;
private AppointmentsViewModel appointmentsViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
appointmentsViewModel = new ViewModelProvider(this).get(AppointmentsViewModel.class);
binding = FragmentAppointmentsBinding.inflate(inflater, container, false);
// Future Appointments
AppointmentAdapter futureAdapter = new AppointmentAdapter();
binding.recyclerAppointmentsFuture.setLayoutManager(new LinearLayoutManager(getContext()));
binding.recyclerAppointmentsFuture.setAdapter(futureAdapter);
appointmentsViewModel.getFutureAppointments().observe(getViewLifecycleOwner(), appointments -> {
futureAdapter.setAppointments(appointments);
});
// Past Appointments
AppointmentAdapter pastAdapter = new AppointmentAdapter();
binding.recyclerAppointmentsPast.setLayoutManager(new LinearLayoutManager(getContext()));
binding.recyclerAppointmentsPast.setAdapter(pastAdapter);
appointmentsViewModel.getPastAppointments().observe(getViewLifecycleOwner(), appointments -> {
pastAdapter.setAppointments(appointments);
});
binding.fabAddAppointment.setOnClickListener(v -> {
androidx.navigation.Navigation.findNavController(v).navigate(R.id.action_appointments_to_schedule);
});
return binding.getRoot();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@@ -0,0 +1,41 @@
package com.example.cuida.ui.appointments;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.example.cuida.data.AppDatabase;
import com.example.cuida.data.dao.AppointmentDao;
import com.example.cuida.data.model.Appointment;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AppointmentsViewModel extends AndroidViewModel {
private final AppointmentDao appointmentDao;
private final LiveData<List<Appointment>> futureAppointments;
private final LiveData<List<Appointment>> pastAppointments;
private final ExecutorService executorService;
public AppointmentsViewModel(@NonNull Application application) {
super(application);
AppDatabase db = AppDatabase.getDatabase(application);
appointmentDao = db.appointmentDao();
futureAppointments = appointmentDao.getFutureAppointments();
pastAppointments = appointmentDao.getPastAppointments();
executorService = Executors.newSingleThreadExecutor();
}
public LiveData<List<Appointment>> getFutureAppointments() {
return futureAppointments;
}
public LiveData<List<Appointment>> getPastAppointments() {
return pastAppointments;
}
public void insert(Appointment appointment) {
executorService.execute(() -> appointmentDao.insert(appointment));
}
}

View File

@@ -0,0 +1,31 @@
package com.example.cuida.ui.auth;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.cuida.databinding.ActivityForgotPasswordBinding;
public class ForgotPasswordActivity extends AppCompatActivity {
private ActivityForgotPasswordBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityForgotPasswordBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.resetButton.setOnClickListener(v -> {
String email = binding.emailEditText.getText().toString();
if (email.isEmpty()) {
Toast.makeText(this, "Por favor insira o seu email.", Toast.LENGTH_SHORT).show();
} else {
// Mock reset logic
Toast.makeText(this, "Email de recuperação enviado para " + email, Toast.LENGTH_LONG).show();
finish();
}
});
binding.backToLogin.setOnClickListener(v -> finish());
}
}

View File

@@ -0,0 +1,66 @@
package com.example.cuida.ui.auth;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.cuida.MainActivity;
import com.example.cuida.data.AppDatabase;
import com.example.cuida.data.dao.UserDao;
import com.example.cuida.data.model.User;
import com.example.cuida.databinding.ActivityLoginBinding;
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityLoginBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.loginButton.setOnClickListener(v -> login());
binding.registerLink.setOnClickListener(v -> {
startActivity(new Intent(this, RegisterActivity.class));
finish();
});
binding.forgotPasswordLink.setOnClickListener(v -> {
startActivity(new Intent(this, ForgotPasswordActivity.class));
});
}
private void login() {
String email = binding.emailEditText.getText().toString();
String password = binding.passwordEditText.getText().toString();
if (email.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "Preencha todos os campos", Toast.LENGTH_SHORT).show();
return;
}
AppDatabase db = AppDatabase.getDatabase(getApplicationContext());
UserDao userDao = db.userDao();
AppDatabase.databaseWriteExecutor.execute(() -> {
User user = userDao.login(email, password);
runOnUiThread(() -> {
if (user != null) {
// Login Success
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
prefs.edit().putBoolean("is_logged_in", true).apply();
prefs.edit().putString("user_name", user.name).apply();
prefs.edit().putString("user_email", user.email).apply();
Toast.makeText(this, "Bem-vindo " + user.name, Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, MainActivity.class));
finish();
} else {
Toast.makeText(this, "Credenciais inválidas", Toast.LENGTH_SHORT).show();
}
});
});
}
}

View File

@@ -0,0 +1,61 @@
package com.example.cuida.ui.auth;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.cuida.data.AppDatabase;
import com.example.cuida.data.dao.UserDao;
import com.example.cuida.data.model.User;
import com.example.cuida.databinding.ActivityRegisterBinding;
public class RegisterActivity extends AppCompatActivity {
private ActivityRegisterBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityRegisterBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.registerButton.setOnClickListener(v -> register());
binding.loginLink.setOnClickListener(v -> {
startActivity(new Intent(this, LoginActivity.class));
finish();
});
}
private void register() {
String name = binding.nameEditText.getText().toString();
String ageStr = binding.ageEditText.getText().toString();
String utenteStr = binding.utenteEditText.getText().toString();
String email = binding.emailEditText.getText().toString();
String password = binding.passwordEditText.getText().toString();
if (name.isEmpty() || ageStr.isEmpty() || email.isEmpty() || password.isEmpty() || utenteStr.isEmpty()) {
Toast.makeText(this, "Preencha todos os campos", Toast.LENGTH_SHORT).show();
return;
}
int age = Integer.parseInt(ageStr);
AppDatabase db = AppDatabase.getDatabase(getApplicationContext());
UserDao userDao = db.userDao();
AppDatabase.databaseWriteExecutor.execute(() -> {
User existing = userDao.checkUser(email);
if (existing != null) {
runOnUiThread(() -> Toast.makeText(this, "Email já registado", Toast.LENGTH_SHORT).show());
} else {
User newUser = new User(name, email, password, age, utenteStr);
userDao.insert(newUser);
runOnUiThread(() -> {
Toast.makeText(this, "Conta criada com sucesso!", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, LoginActivity.class));
finish();
});
}
});
}
}

View File

@@ -0,0 +1,89 @@
package com.example.cuida.ui.home;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.app.TimePickerDialog;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.example.cuida.databinding.FragmentHomeBinding;
import com.example.cuida.ui.medication.MedicationViewModel;
import com.example.cuida.ui.appointments.AppointmentsViewModel;
import com.example.cuida.data.model.Appointment;
import java.util.Calendar;
import java.util.Locale;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
private MedicationViewModel medicationViewModel;
private AppointmentsViewModel appointmentsViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentHomeBinding.inflate(inflater, container, false);
// --- Greeting ---
SharedPreferences prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE);
String name = prefs.getString("user_name", "Utilizador");
binding.textGreeting.setText("Olá, " + name + "!");
// --- Next Medication ---
medicationViewModel = new ViewModelProvider(this).get(MedicationViewModel.class);
medicationViewModel.getNextMedication().observe(getViewLifecycleOwner(), medication -> {
if (medication != null) {
binding.nextMedName.setText(medication.name + " (" + medication.dosage + ")");
binding.nextMedTime.setText("Hoje, " + medication.time);
} else {
binding.nextMedName.setText("Sem medicação");
binding.nextMedTime.setText("--:--");
}
});
// --- Book Appointment ---
appointmentsViewModel = new ViewModelProvider(this).get(AppointmentsViewModel.class);
binding.buttonBookAppointment.setOnClickListener(v -> showTimePicker());
return binding.getRoot();
}
private void showTimePicker() {
Calendar mcurrentTime = Calendar.getInstance();
int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
int minute = mcurrentTime.get(Calendar.MINUTE);
TimePickerDialog mTimePicker;
mTimePicker = new TimePickerDialog(getContext(), (timePicker, selectedHour, selectedMinute) -> {
// Round to 15 minutes
int roundedMinute = (selectedMinute / 15) * 15;
String time = String.format(Locale.getDefault(), "%02d:%02d", selectedHour, roundedMinute);
// For MVP, assume date is tomorrow (or let user pick date, but brief said
// "Hora" mostly)
// Let's assume a default date for now or just "Amanhã" text logic
String date = "Amanhã";
// Save to DB
Appointment newAppt = new Appointment("Consulta Geral", date, time, false);
appointmentsViewModel.insert(newAppt);
binding.buttonBookAppointment.setText("Consulta marcada: " + time);
Toast.makeText(getContext(), "Consulta marcada para " + time, Toast.LENGTH_SHORT).show();
}, hour, minute, true);
mTimePicker.setTitle("Selecione a hora");
mTimePicker.show();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@@ -0,0 +1,76 @@
package com.example.cuida.ui.medication;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.cuida.R;
import com.example.cuida.data.model.Medication;
import java.util.ArrayList;
import java.util.List;
public class MedicationAdapter extends RecyclerView.Adapter<MedicationAdapter.MedicationViewHolder> {
private List<Medication> medicationList = new ArrayList<>();
private final OnItemClickListener listener;
public interface OnItemClickListener {
void onCheckClick(Medication medication);
}
public MedicationAdapter(OnItemClickListener listener) {
this.listener = listener;
}
public void setMedications(List<Medication> medications) {
this.medicationList = medications;
notifyDataSetChanged();
}
@NonNull
@Override
public MedicationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_medication, parent, false);
return new MedicationViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MedicationViewHolder holder, int position) {
Medication medication = medicationList.get(position);
holder.textName.setText(medication.name);
holder.textDosage.setText(medication.dosage);
holder.textTime.setText(medication.time);
holder.textNotes.setText(medication.notes);
// Remove listener temporarily to avoid triggering it during bind
holder.checkBoxTaken.setOnCheckedChangeListener(null);
holder.checkBoxTaken.setChecked(medication.isTaken);
holder.checkBoxTaken.setOnCheckedChangeListener((buttonView, isChecked) -> {
medication.isTaken = isChecked;
listener.onCheckClick(medication);
});
}
@Override
public int getItemCount() {
return medicationList.size();
}
public static class MedicationViewHolder extends RecyclerView.ViewHolder {
TextView textName, textDosage, textTime, textNotes;
CheckBox checkBoxTaken;
public MedicationViewHolder(@NonNull View itemView) {
super(itemView);
textName = itemView.findViewById(R.id.text_med_name);
textDosage = itemView.findViewById(R.id.text_med_dosage);
textTime = itemView.findViewById(R.id.text_med_time);
textNotes = itemView.findViewById(R.id.text_med_notes);
checkBoxTaken = itemView.findViewById(R.id.checkbox_taken);
}
}
}

View File

@@ -0,0 +1,42 @@
package com.example.cuida.ui.medication;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.example.cuida.databinding.FragmentMedicationBinding;
public class MedicationFragment extends Fragment {
private FragmentMedicationBinding binding;
private MedicationViewModel medicationViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
medicationViewModel = new ViewModelProvider(this).get(MedicationViewModel.class);
binding = FragmentMedicationBinding.inflate(inflater, container, false);
MedicationAdapter adapter = new MedicationAdapter(medication -> {
medicationViewModel.update(medication);
});
binding.recyclerMedication.setLayoutManager(new LinearLayoutManager(getContext()));
binding.recyclerMedication.setAdapter(adapter);
medicationViewModel.getAllMedications().observe(getViewLifecycleOwner(), medications -> {
adapter.setMedications(medications);
});
return binding.getRoot();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@@ -0,0 +1,38 @@
package com.example.cuida.ui.medication;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.example.cuida.data.AppDatabase;
import com.example.cuida.data.dao.MedicationDao;
import com.example.cuida.data.model.Medication;
import java.util.List;
public class MedicationViewModel extends AndroidViewModel {
private final LiveData<List<Medication>> allMedications;
private final LiveData<Medication> nextMedication;
public MedicationViewModel(@NonNull Application application) {
super(application);
AppDatabase db = AppDatabase.getDatabase(application);
MedicationDao medicationDao = db.medicationDao();
allMedications = medicationDao.getAllMedications();
nextMedication = medicationDao.getNextMedication();
}
public LiveData<List<Medication>> getAllMedications() {
return allMedications;
}
public LiveData<Medication> getNextMedication() {
return nextMedication;
}
public void update(Medication medication) {
AppDatabase.databaseWriteExecutor.execute(() -> {
AppDatabase.getDatabase(getApplication()).medicationDao().update(medication);
});
}
}

View File

@@ -0,0 +1,150 @@
package com.example.cuida.ui.profile;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.example.cuida.data.AppDatabase;
import com.example.cuida.data.dao.UserDao;
import com.example.cuida.data.model.User;
import com.example.cuida.R;
import com.example.cuida.databinding.FragmentProfileBinding;
import com.example.cuida.ui.auth.LoginActivity;
public class ProfileFragment extends Fragment {
private FragmentProfileBinding binding;
private User currentUser;
private UserDao userDao;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentProfileBinding.inflate(inflater, container, false);
userDao = AppDatabase.getDatabase(requireContext()).userDao();
loadUserData();
binding.buttonEditProfile.setOnClickListener(v -> showEditDialog());
binding.buttonLogout.setOnClickListener(v -> {
getContext().getSharedPreferences("prefs", Context.MODE_PRIVATE).edit().clear().apply();
Intent intent = new Intent(getContext(), LoginActivity.class);
startActivity(intent);
getActivity().finish();
});
return binding.getRoot();
}
private void loadUserData() {
SharedPreferences prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE);
String email = prefs.getString("user_email", "");
AppDatabase.databaseWriteExecutor.execute(() -> {
currentUser = userDao.checkUser(email);
if (currentUser != null) {
getActivity().runOnUiThread(() -> {
binding.profileName.setText(currentUser.name);
binding.profileEmail.setText(currentUser.email);
binding.profileAge.setText(String.valueOf(currentUser.age));
binding.profileUtente.setText(currentUser.utenteNumber != null ? currentUser.utenteNumber : "N/A");
});
}
});
}
private void showEditDialog() {
if (currentUser == null)
return;
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
// Inflate custom layout
LayoutInflater inflater = requireActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.dialog_edit_profile, null);
builder.setView(dialogView);
AlertDialog dialog = builder.create();
// Bind Views
EditText editName = dialogView.findViewById(R.id.edit_name);
EditText editAge = dialogView.findViewById(R.id.edit_age);
EditText editUtente = dialogView.findViewById(R.id.edit_utente);
EditText editEmail = dialogView.findViewById(R.id.edit_email);
EditText editPassword = dialogView.findViewById(R.id.edit_password);
View btnSave = dialogView.findViewById(R.id.button_save);
View btnCancel = dialogView.findViewById(R.id.button_cancel);
// Pre-fill data
editName.setText(currentUser.name);
editAge.setText(String.valueOf(currentUser.age));
editUtente.setText(currentUser.utenteNumber);
editEmail.setText(currentUser.email);
editPassword.setText(currentUser.password);
btnSave.setOnClickListener(v -> {
String newName = editName.getText().toString();
String ageStr = editAge.getText().toString();
String newUtente = editUtente.getText().toString();
String newEmail = editEmail.getText().toString();
String newPassword = editPassword.getText().toString();
if (newName.isEmpty() || ageStr.isEmpty() || newUtente.isEmpty() || newEmail.isEmpty()
|| newPassword.isEmpty()) {
Toast.makeText(getContext(), "Preencha todos os campos", Toast.LENGTH_SHORT).show();
return;
}
int newAge = Integer.parseInt(ageStr);
boolean emailChanged = !newEmail.equals(currentUser.email);
currentUser.name = newName;
currentUser.age = newAge;
currentUser.utenteNumber = newUtente;
currentUser.email = newEmail;
currentUser.password = newPassword;
AppDatabase.databaseWriteExecutor.execute(() -> {
userDao.insert(currentUser);
});
// Update SharedPreferences if email changed (key for login persistence)
if (emailChanged) {
getContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
.edit()
.putString("user_email", newEmail)
.apply();
}
// UI update
binding.profileName.setText(newName);
binding.profileEmail.setText(newEmail);
binding.profileAge.setText(String.valueOf(newAge));
binding.profileUtente.setText(newUtente);
Toast.makeText(getContext(), "Dados atualizados com sucesso!", Toast.LENGTH_SHORT).show();
dialog.dismiss();
});
btnCancel.setOnClickListener(v -> dialog.dismiss());
dialog.show();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@@ -0,0 +1,85 @@
package com.example.cuida.ui.schedule;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.cuida.R;
import java.util.Calendar;
public class ScheduleAppointmentFragment extends Fragment {
private ScheduleViewModel scheduleViewModel;
private DatePicker datePicker;
private RecyclerView recyclerTimeSlots;
private Button btnConfirm;
private TimeSlotAdapter timeSlotAdapter;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_schedule_appointment, container, false);
scheduleViewModel = new ViewModelProvider(this).get(ScheduleViewModel.class);
datePicker = root.findViewById(R.id.datePicker);
recyclerTimeSlots = root.findViewById(R.id.recycler_time_slots);
btnConfirm = root.findViewById(R.id.btn_confirm_appointment);
setupDatePicker();
setupRecyclerView();
setupObservers();
btnConfirm.setOnClickListener(v -> scheduleViewModel.confirmAppointment());
return root;
}
private void setupDatePicker() {
Calendar today = Calendar.getInstance();
datePicker.init(today.get(Calendar.YEAR), today.get(Calendar.MONTH),
today.get(Calendar.DAY_OF_MONTH), (view, year, monthOfYear, dayOfMonth) -> {
scheduleViewModel.setDate(year, monthOfYear, dayOfMonth);
});
// Set initial valid date in VM
scheduleViewModel.setDate(today.get(Calendar.YEAR), today.get(Calendar.MONTH),
today.get(Calendar.DAY_OF_MONTH));
// Prevent past dates
datePicker.setMinDate(System.currentTimeMillis() - 1000);
}
private void setupRecyclerView() {
timeSlotAdapter = new TimeSlotAdapter();
timeSlotAdapter.setOnTimeSlotSelectedListener(time -> scheduleViewModel.setTime(time));
recyclerTimeSlots.setLayoutManager(new GridLayoutManager(getContext(), 4));
recyclerTimeSlots.setAdapter(timeSlotAdapter);
}
private void setupObservers() {
scheduleViewModel.getTimeSlots().observe(getViewLifecycleOwner(), slots -> {
timeSlotAdapter.setTimeSlots(slots);
});
scheduleViewModel.getSaveSuccess().observe(getViewLifecycleOwner(), success -> {
if (success) {
Toast.makeText(getContext(), "Consulta agendada com sucesso!", Toast.LENGTH_SHORT).show();
NavController navController = Navigation.findNavController(getView());
navController.popBackStack();
}
});
}
}

View File

@@ -0,0 +1,110 @@
package com.example.cuida.ui.schedule;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.cuida.data.AppDatabase;
import com.example.cuida.data.dao.AppointmentDao;
import com.example.cuida.data.model.Appointment;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ScheduleViewModel extends AndroidViewModel {
private final AppointmentDao appointmentDao;
private final ExecutorService executorService;
private final MutableLiveData<String> selectedDate = new MutableLiveData<>();
private final MutableLiveData<String> selectedTime = new MutableLiveData<>();
private final MutableLiveData<List<TimeSlot>> timeSlots = new MutableLiveData<>();
private final MutableLiveData<Boolean> saveSuccess = new MutableLiveData<>();
public ScheduleViewModel(@NonNull Application application) {
super(application);
AppDatabase db = AppDatabase.getDatabase(application);
appointmentDao = db.appointmentDao();
executorService = Executors.newSingleThreadExecutor();
// Initial empty state or default date
}
public void setDate(int year, int month, int dayOfMonth) {
// Format to dd/MM/yyyy
String date = String.format("%02d/%02d/%04d", dayOfMonth, month + 1, year);
selectedDate.setValue(date);
loadTimeSlots(date);
}
public LiveData<String> getSelectedDate() {
return selectedDate;
}
public void setTime(String time) {
selectedTime.setValue(time);
// Update selection state in the list
List<TimeSlot> currentSlots = timeSlots.getValue();
if (currentSlots != null) {
for (TimeSlot slot : currentSlots) {
slot.setSelected(slot.getTime().equals(time));
}
timeSlots.setValue(currentSlots);
}
}
public LiveData<String> getSelectedTime() {
return selectedTime;
}
public LiveData<List<TimeSlot>> getTimeSlots() {
return timeSlots;
}
public LiveData<Boolean> getSaveSuccess() {
return saveSuccess;
}
private void loadTimeSlots(String date) {
executorService.execute(() -> {
List<String> bookedTimes = appointmentDao.getBookedTimesForDate(date);
List<TimeSlot> slots = generateTimeSlots(bookedTimes);
timeSlots.postValue(slots);
});
}
private List<TimeSlot> generateTimeSlots(List<String> bookedTimes) {
List<TimeSlot> slots = new ArrayList<>();
int startHour = 8;
int endHour = 20;
for (int hour = startHour; hour < endHour; hour++) {
addSlot(slots, String.format("%02d:00", hour), bookedTimes);
addSlot(slots, String.format("%02d:30", hour), bookedTimes);
}
return slots;
}
private void addSlot(List<TimeSlot> slots, String time, List<String> bookedTimes) {
boolean isBooked = bookedTimes.contains(time);
// If current selected time is now booked (e.g. concurrent), deselect it?
// For simplicity, just check booked state.
boolean isSelected = time.equals(selectedTime.getValue());
slots.add(new TimeSlot(time, isBooked, isSelected));
}
public void confirmAppointment() {
String date = selectedDate.getValue();
String time = selectedTime.getValue();
if (date != null && time != null) {
Appointment appointment = new Appointment("Consulta Geral", date, time, false);
executorService.execute(() -> {
appointmentDao.insert(appointment);
saveSuccess.postValue(true);
});
}
}
}

View File

@@ -0,0 +1,52 @@
package com.example.cuida.ui.schedule;
import java.util.Objects;
public class TimeSlot {
private String time;
private boolean isBooked;
private boolean isSelected;
public TimeSlot(String time, boolean isBooked, boolean isSelected) {
this.time = time;
this.isBooked = isBooked;
this.isSelected = isSelected;
}
public String getTime() {
return time;
}
public boolean isBooked() {
return isBooked;
}
public void setBooked(boolean booked) {
isBooked = booked;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
TimeSlot timeSlot = (TimeSlot) o;
return isBooked == timeSlot.isBooked &&
isSelected == timeSlot.isSelected &&
Objects.equals(time, timeSlot.time);
}
@Override
public int hashCode() {
return Objects.hash(time, isBooked, isSelected);
}
}

View File

@@ -0,0 +1,75 @@
package com.example.cuida.ui.schedule;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.cuida.R;
import com.google.android.material.button.MaterialButton;
import java.util.ArrayList;
import java.util.List;
public class TimeSlotAdapter extends RecyclerView.Adapter<TimeSlotAdapter.ViewHolder> {
private List<TimeSlot> timeSlots = new ArrayList<>();
private OnTimeSlotSelectedListener listener;
public interface OnTimeSlotSelectedListener {
void onTimeSlotSelected(String time);
}
public void setOnTimeSlotSelectedListener(OnTimeSlotSelectedListener listener) {
this.listener = listener;
}
public void setTimeSlots(List<TimeSlot> slots) {
this.timeSlots = slots;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_time_slot, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
TimeSlot slot = timeSlots.get(position);
holder.btnTimeSlot.setText(slot.getTime());
if (slot.isBooked()) {
holder.btnTimeSlot.setEnabled(false);
holder.btnTimeSlot.setAlpha(0.5f);
holder.btnTimeSlot.setChecked(false);
holder.btnTimeSlot.setOnClickListener(null);
} else {
holder.btnTimeSlot.setEnabled(true);
holder.btnTimeSlot.setAlpha(1.0f);
holder.btnTimeSlot.setChecked(slot.isSelected());
holder.btnTimeSlot.setOnClickListener(v -> {
if (listener != null) {
listener.onTimeSlotSelected(slot.getTime());
}
});
}
}
@Override
public int getItemCount() {
return timeSlots.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
MaterialButton btnTimeSlot;
ViewHolder(View itemView) {
super(itemView);
btnTimeSlot = itemView.findViewById(R.id.btn_time_slot);
}
}
}

View File

@@ -0,0 +1,59 @@
package com.example.cuida.ui.sns24;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import com.example.cuida.databinding.FragmentSns24Binding;
public class Sns24Fragment extends Fragment {
private FragmentSns24Binding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentSns24Binding.inflate(inflater, container, false);
binding.buttonCallSns.setOnClickListener(v -> {
try {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:808242424"));
startActivity(intent);
} catch (Exception e) {
// In case no dialer app is found
e.printStackTrace();
}
});
binding.buttonTriage.setOnClickListener(v -> performTriage());
return binding.getRoot();
}
private void performTriage() {
boolean fever = binding.checkFever.isChecked();
boolean breath = binding.checkBreath.isChecked();
boolean pain = binding.checkPain.isChecked();
if (breath || pain) {
binding.textTriageResult.setText("URGENTE: Dirija-se imediatamente ao hospital ou ligue 112.");
binding.textTriageResult.setTextColor(getResources().getColor(android.R.color.holo_red_dark));
} else if (fever) {
binding.textTriageResult.setText("GRAVE: Ligue SNS 24 para aconselhamento antes de sair de casa.");
binding.textTriageResult.setTextColor(getResources().getColor(android.R.color.holo_orange_dark));
} else {
binding.textTriageResult.setText("NÃO GRAVE: Fique em casa e monitorize os sintomas.");
binding.textTriageResult.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}