refactor: update medication adapter and schedule appointment fragment logic while adding gitignore
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
package com.example.cuida;
|
||||
|
||||
import android.app.Application;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.FirebaseFirestoreSettings;
|
||||
|
||||
public class CuidaApplication extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// Ativar persistência offline globalmente para toda a aplicação
|
||||
FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
|
||||
.setPersistenceEnabled(true)
|
||||
.build();
|
||||
db.setFirestoreSettings(settings);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
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.example.cuida.ui.auth.LoginActivity;
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.example.cuida.utils.NotificationHelper;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityMainBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Initialize Notification Channels
|
||||
new NotificationHelper(this);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Check for Notification Permission on Android 13+ after ensuring user is
|
||||
// logged in
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.POST_NOTIFICATIONS }, 101);
|
||||
}
|
||||
}
|
||||
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
// Find Navigation Host Fragment and setup Bottom Navigation
|
||||
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
|
||||
.findFragmentById(R.id.nav_host_fragment);
|
||||
if (navHostFragment != null) {
|
||||
NavController navController = navHostFragment.getNavController();
|
||||
NavigationUI.setupWithNavController(binding.navView, navController);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.example.cuida.data.model;
|
||||
|
||||
import com.google.firebase.firestore.DocumentId;
|
||||
|
||||
public class Appointment {
|
||||
|
||||
@DocumentId
|
||||
public String id;
|
||||
|
||||
public String type; // e.g. "Medicina Geral", "Cardiologia"
|
||||
public String date; // dd/MM/yyyy
|
||||
public String time; // HH:mm
|
||||
public String reason;
|
||||
public boolean isPast;
|
||||
public String userId;
|
||||
public String status; // "Pendente", "Aceite", "Rejeitada"
|
||||
|
||||
// Required empty constructor for Firestore deserialization
|
||||
public Appointment() {
|
||||
}
|
||||
|
||||
public Appointment(String type, String date, String time, String reason, boolean isPast, String userId, String status) {
|
||||
this.type = type;
|
||||
this.date = date;
|
||||
this.time = time;
|
||||
this.reason = reason;
|
||||
this.isPast = isPast;
|
||||
this.userId = userId;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.example.cuida.data.model;
|
||||
|
||||
public class Comprimido {
|
||||
public String nome;
|
||||
public String dosagem;
|
||||
|
||||
public Comprimido() {}
|
||||
|
||||
public Comprimido(String nome, String dosagem) {
|
||||
this.nome = nome;
|
||||
this.dosagem = dosagem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return nome;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.example.cuida.data.model;
|
||||
|
||||
import com.google.firebase.firestore.PropertyName;
|
||||
|
||||
public class Medication {
|
||||
private String id;
|
||||
|
||||
@PropertyName("nome")
|
||||
public String name;
|
||||
|
||||
@PropertyName("hora")
|
||||
public String time;
|
||||
|
||||
@PropertyName("dosagem")
|
||||
public String dosage;
|
||||
|
||||
@PropertyName("notas")
|
||||
public String notes;
|
||||
|
||||
public boolean isTaken;
|
||||
public String userId;
|
||||
|
||||
public Medication() {
|
||||
// Obrigatório para o Firestore
|
||||
}
|
||||
|
||||
public Medication(String name, String time, String dosage, String notes, String userId) {
|
||||
this.name = name;
|
||||
this.time = time;
|
||||
this.dosage = dosage;
|
||||
this.notes = notes;
|
||||
this.isTaken = false;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
// --- Getters e Setters com compatibilidade para nomes antigos (name, time, dosage, notes) ---
|
||||
|
||||
@PropertyName("nome")
|
||||
public String getName() { return name; }
|
||||
|
||||
@PropertyName("nome")
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
@PropertyName("name") // Suporte para dados antigos
|
||||
public void setNameOld(String name) { if (this.name == null) this.name = name; }
|
||||
|
||||
@PropertyName("hora")
|
||||
public String getTime() { return time; }
|
||||
|
||||
@PropertyName("hora")
|
||||
public void setTime(String time) { this.time = time; }
|
||||
|
||||
@PropertyName("time") // Suporte para dados antigos
|
||||
public void setTimeOld(String time) { if (this.time == null) this.time = time; }
|
||||
|
||||
@PropertyName("dosagem")
|
||||
public String getDosage() { return dosage; }
|
||||
|
||||
@PropertyName("dosagem")
|
||||
public void setDosage(String dosage) { this.dosage = dosage; }
|
||||
|
||||
@PropertyName("dosage") // Suporte para dados antigos
|
||||
public void setDosageOld(String dosage) { if (this.dosage == null) this.dosage = dosage; }
|
||||
|
||||
@PropertyName("notas")
|
||||
public String getNotes() { return notes; }
|
||||
|
||||
@PropertyName("notas")
|
||||
public void setNotes(String notes) { this.notes = notes; }
|
||||
|
||||
@PropertyName("notes") // Suporte para dados antigos
|
||||
public void setNotesOld(String notes) { if (this.notes == null) this.notes = notes; }
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.example.cuida.data.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Perfil {
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("nome_completo")
|
||||
private String nome_completo;
|
||||
|
||||
@SerializedName("idade")
|
||||
private int idade;
|
||||
|
||||
@SerializedName("numero_utente")
|
||||
private String numero_utente;
|
||||
|
||||
@SerializedName("gmail")
|
||||
private String gmail;
|
||||
|
||||
@SerializedName("sexo")
|
||||
private String sexo;
|
||||
|
||||
public Perfil(String id, String nome, int idade, String utente, String email, String sexo) {
|
||||
this.id = id;
|
||||
this.nome_completo = nome;
|
||||
this.idade = idade;
|
||||
this.numero_utente = utente;
|
||||
this.gmail = email;
|
||||
this.sexo = sexo;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getNomeCompleto() {
|
||||
return nome_completo;
|
||||
}
|
||||
|
||||
public int getIdade() {
|
||||
return idade;
|
||||
}
|
||||
|
||||
public String getNumeroUtente() {
|
||||
return numero_utente;
|
||||
}
|
||||
|
||||
public String getGmail() {
|
||||
return gmail;
|
||||
}
|
||||
|
||||
public String getSexo() {
|
||||
return sexo;
|
||||
}
|
||||
|
||||
// Setters
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setNomeCompleto(String nome_completo) {
|
||||
this.nome_completo = nome_completo;
|
||||
}
|
||||
|
||||
public void setIdade(int idade) {
|
||||
this.idade = idade;
|
||||
}
|
||||
|
||||
public void setNumeroUtente(String numero_utente) {
|
||||
this.numero_utente = numero_utente;
|
||||
}
|
||||
|
||||
public void setGmail(String gmail) {
|
||||
this.gmail = gmail;
|
||||
}
|
||||
|
||||
public void setSexo(String sexo) {
|
||||
this.sexo = sexo;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.example.cuida.data.model;
|
||||
|
||||
import com.google.firebase.firestore.DocumentId;
|
||||
|
||||
public class User {
|
||||
|
||||
@DocumentId
|
||||
@com.google.firebase.firestore.Exclude
|
||||
public String id;
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("nome_completo")
|
||||
public String name;
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("email")
|
||||
public String email;
|
||||
|
||||
@com.google.firebase.firestore.Exclude
|
||||
public String password;
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("idade")
|
||||
public int age;
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("numero_utente")
|
||||
public String utenteNumber;
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("profilePictureUri")
|
||||
public String profilePictureUri;
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("tipo")
|
||||
public String tipo = "paciente";
|
||||
|
||||
// Required empty constructor for Firestore deserialization
|
||||
public User() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("nome_completo")
|
||||
public String getName() { return name; }
|
||||
@com.google.firebase.firestore.PropertyName("nome_completo")
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("idade")
|
||||
public int getAge() { return age; }
|
||||
@com.google.firebase.firestore.PropertyName("idade")
|
||||
public void setAge(int age) { this.age = age; }
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("numero_utente")
|
||||
public String getUtenteNumber() { return utenteNumber; }
|
||||
@com.google.firebase.firestore.PropertyName("numero_utente")
|
||||
public void setUtenteNumber(String utenteNumber) { this.utenteNumber = utenteNumber; }
|
||||
|
||||
@com.google.firebase.firestore.PropertyName("tipo")
|
||||
public String getTipo() { return tipo; }
|
||||
@com.google.firebase.firestore.PropertyName("tipo")
|
||||
public void setTipo(String tipo) { this.tipo = tipo; }
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.example.cuida.services;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import com.example.cuida.utils.NotificationHelper;
|
||||
|
||||
public class AlarmReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "AlarmReceiver";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent != null) {
|
||||
String title = intent.getStringExtra("EXTRA_TITLE");
|
||||
String message = intent.getStringExtra("EXTRA_MESSAGE");
|
||||
int notificationId = intent.getIntExtra("EXTRA_NOTIFICATION_ID", (int) System.currentTimeMillis());
|
||||
|
||||
Log.d(TAG, "Alarm received! Title: " + title + " Msg: " + message);
|
||||
|
||||
NotificationHelper notificationHelper = new NotificationHelper(context);
|
||||
if (title != null && title.contains("Medicamento")) {
|
||||
notificationHelper.sendNotification(title, message, notificationId, "MEDICATION_CHANNEL_ID");
|
||||
} else {
|
||||
notificationHelper.sendNotification(title, message, notificationId, "APPOINTMENT_CHANNEL_ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package com.example.cuida.services;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Gemini {
|
||||
private static final String API_KEY = "AIzaSyBYar6Yv0rhrZX8cIQQxd77TLERHRsjAtY";
|
||||
private static final String MODEL_NAME = "gemini-2.0-flash";
|
||||
private static final String API_URL = "https://generativelanguage.googleapis.com/v1beta/models/" + MODEL_NAME + ":generateContent?key=" + API_KEY;
|
||||
private final OkHttpClient client;
|
||||
private final Handler mainHandler;
|
||||
|
||||
public Gemini() {
|
||||
this.client = new OkHttpClient();
|
||||
this.mainHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
public interface GeminiCallback {
|
||||
void onSuccess(String result);
|
||||
void onError(Throwable t);
|
||||
}
|
||||
|
||||
public void fazerPergunta(String promptUtilizador, GeminiCallback callback) {
|
||||
try {
|
||||
JSONObject jsonBody = new JSONObject();
|
||||
JSONArray contents = new JSONArray();
|
||||
JSONObject content = new JSONObject();
|
||||
JSONArray parts = new JSONArray();
|
||||
JSONObject part = new JSONObject();
|
||||
|
||||
part.put("text", promptUtilizador);
|
||||
parts.put(part);
|
||||
content.put("parts", parts);
|
||||
contents.put(content);
|
||||
jsonBody.put("contents", contents);
|
||||
|
||||
RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.parse("application/json; charset=utf-8"));
|
||||
Request request = new Request.Builder()
|
||||
.url(API_URL)
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
mainHandler.post(() -> callback.onError(e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (response.isSuccessful()) {
|
||||
try {
|
||||
String responseBody = response.body().string();
|
||||
JSONObject jsonObject = new JSONObject(responseBody);
|
||||
JSONArray candidates = jsonObject.getJSONArray("candidates");
|
||||
JSONObject firstCandidate = candidates.getJSONObject(0);
|
||||
JSONObject contentObj = firstCandidate.getJSONObject("content");
|
||||
JSONArray partsArr = contentObj.getJSONArray("parts");
|
||||
String textResult = partsArr.getJSONObject(0).getString("text");
|
||||
|
||||
mainHandler.post(() -> callback.onSuccess(textResult));
|
||||
} catch (Exception e) {
|
||||
mainHandler.post(() -> callback.onError(new Exception("Erro ao ler resposta da IA", e)));
|
||||
}
|
||||
} else {
|
||||
String errorBody = response.body() != null ? response.body().string() : "Unknown error";
|
||||
mainHandler.post(() -> callback.onError(new Exception("Erro da API HTTP " + response.code() + ": " + errorBody)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
mainHandler.post(() -> callback.onError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
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);
|
||||
holder.textReason.setText("Motivo: " + (appointment.reason != null ? appointment.reason : "--"));
|
||||
|
||||
String status = appointment.status != null ? appointment.status : "Pendente";
|
||||
holder.textStatus.setText(status);
|
||||
|
||||
if ("Aceite".equalsIgnoreCase(status)) {
|
||||
holder.textStatus.setTextColor(android.graphics.Color.parseColor("#388E3C")); // Green
|
||||
holder.textStatus.setBackgroundColor(android.graphics.Color.parseColor("#C8E6C9"));
|
||||
} else if ("Rejeitada".equalsIgnoreCase(status)) {
|
||||
holder.textStatus.setTextColor(android.graphics.Color.parseColor("#D32F2F")); // Red
|
||||
holder.textStatus.setBackgroundColor(android.graphics.Color.parseColor("#FFCDD2"));
|
||||
} else {
|
||||
holder.textStatus.setTextColor(android.graphics.Color.parseColor("#F57C00")); // Orange
|
||||
holder.textStatus.setBackgroundColor(android.graphics.Color.parseColor("#FFE0B2"));
|
||||
holder.textStatus.setText("Pendente");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return appointmentList.size();
|
||||
}
|
||||
|
||||
public static class AppointmentViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView textType, textDate, textTime, textReason, textStatus;
|
||||
|
||||
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);
|
||||
textReason = itemView.findViewById(R.id.text_reason);
|
||||
textStatus = itemView.findViewById(R.id.text_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
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.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);
|
||||
});
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package com.example.cuida.ui.appointments;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.cuida.data.model.Appointment;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.Query;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AppointmentsViewModel extends AndroidViewModel {
|
||||
|
||||
private final MutableLiveData<List<Appointment>> futureAppointments = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<Appointment>> pastAppointments = new MutableLiveData<>(new ArrayList<>());
|
||||
private final FirebaseFirestore db;
|
||||
private final FirebaseAuth auth;
|
||||
|
||||
public AppointmentsViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
db = FirebaseFirestore.getInstance();
|
||||
auth = FirebaseAuth.getInstance();
|
||||
fetchAppointments();
|
||||
}
|
||||
|
||||
private void fetchAppointments() {
|
||||
if (auth.getCurrentUser() == null)
|
||||
return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
// 1. Fetch Future Appointments
|
||||
db.collection("consultas")
|
||||
.whereEqualTo("userId", userId)
|
||||
.whereEqualTo("isPast", false)
|
||||
.addSnapshotListener((value, error) -> {
|
||||
if (error != null) {
|
||||
Log.e("AppointmentsVM", "Listen failed for future.", error);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Appointment> apps = new ArrayList<>();
|
||||
java.util.Date now = new java.util.Date();
|
||||
java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm",
|
||||
java.util.Locale.getDefault());
|
||||
|
||||
if (value != null) {
|
||||
for (QueryDocumentSnapshot doc : value) {
|
||||
Appointment app = doc.toObject(Appointment.class);
|
||||
try {
|
||||
java.util.Date appDate = format.parse(app.date + " " + app.time);
|
||||
if (appDate != null && appDate.before(now)) {
|
||||
// It passed out of date. Update in DB to move it to Past Appointments.
|
||||
db.collection("consultas").document(doc.getId()).update("isPast", true);
|
||||
} else {
|
||||
apps.add(app);
|
||||
}
|
||||
} catch (java.text.ParseException e) {
|
||||
apps.add(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort locally
|
||||
apps.sort((a, b) -> {
|
||||
try {
|
||||
java.util.Date dateA = format.parse(a.date + " " + a.time);
|
||||
java.util.Date dateB = format.parse(b.date + " " + b.time);
|
||||
return dateA.compareTo(dateB);
|
||||
} catch (java.text.ParseException e) {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
futureAppointments.setValue(apps);
|
||||
});
|
||||
|
||||
// 2. Fetch Past Appointments
|
||||
db.collection("consultas")
|
||||
.whereEqualTo("userId", userId)
|
||||
.whereEqualTo("isPast", true)
|
||||
.addSnapshotListener((value, error) -> {
|
||||
if (error != null) {
|
||||
Log.e("AppointmentsVM", "Listen failed for past.", error);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Appointment> apps = new ArrayList<>();
|
||||
if (value != null) {
|
||||
for (QueryDocumentSnapshot doc : value) {
|
||||
apps.add(doc.toObject(Appointment.class));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort locally descending
|
||||
java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm",
|
||||
java.util.Locale.getDefault());
|
||||
apps.sort((a, b) -> {
|
||||
try {
|
||||
java.util.Date dateA = format.parse(a.date + " " + a.time);
|
||||
java.util.Date dateB = format.parse(b.date + " " + b.time);
|
||||
if (dateA == null || dateB == null) return 0;
|
||||
return dateB.compareTo(dateA); // Reverse for descending
|
||||
} catch (java.text.ParseException e) {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
pastAppointments.setValue(apps);
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<List<Appointment>> getFutureAppointments() {
|
||||
return futureAppointments;
|
||||
}
|
||||
|
||||
public LiveData<List<Appointment>> getPastAppointments() {
|
||||
return pastAppointments;
|
||||
}
|
||||
|
||||
public void insert(Appointment appointment) {
|
||||
if (auth.getCurrentUser() == null)
|
||||
return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
appointment.userId = userId;
|
||||
|
||||
db.collection("consultas")
|
||||
.add(appointment)
|
||||
.addOnSuccessListener(documentReference -> Log.d("AppointmentsVM", "Appointment added"))
|
||||
.addOnFailureListener(e -> Log.w("AppointmentsVM", "Error adding appointment", e));
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
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().trim();
|
||||
if (email.isEmpty()) {
|
||||
Toast.makeText(this, "Por favor insira o seu email.", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
// Real Firebase Password Reset Logic
|
||||
com.google.firebase.auth.FirebaseAuth.getInstance().sendPasswordResetEmail(email)
|
||||
.addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
Toast.makeText(this, "Email de recuperação enviado para " + email, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
} else {
|
||||
String errorMsg = task.getException() != null ? task.getException().getMessage() : "Erro desconhecido";
|
||||
if (errorMsg != null) {
|
||||
if (errorMsg.contains("There is no user record")) {
|
||||
errorMsg = "Não existe conta associada a este email.";
|
||||
} else if (errorMsg.contains("badly formatted")) {
|
||||
errorMsg = "O formato do email é inválido.";
|
||||
}
|
||||
}
|
||||
Toast.makeText(this, "Erro: " + errorMsg, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
binding.backToLogin.setOnClickListener(v -> finish());
|
||||
}
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
package com.example.cuida.ui.auth;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import com.example.cuida.MainActivity;
|
||||
import com.example.cuida.data.model.User;
|
||||
import com.example.cuida.databinding.ActivityLoginBinding;
|
||||
import com.example.cuida.R;
|
||||
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
import androidx.biometric.BiometricManager;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
// gvjhbk
|
||||
private ActivityLoginBinding binding;
|
||||
private FirebaseAuth mAuth;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Initialize Firebase Auth
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
|
||||
// Check if user is already logged in and wants to be remembered
|
||||
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
|
||||
boolean isLoggedIn = prefs.getBoolean("is_logged_in", false);
|
||||
boolean rememberMe = prefs.getBoolean("remember_me", false);
|
||||
|
||||
if (isLoggedIn && rememberMe) {
|
||||
if (mAuth.getCurrentUser() != null) {
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Se não for para lembrar a sessão, garantimos que o estado de login é falso
|
||||
// mas NÃO limpamos as credenciais guardadas para a biometria.
|
||||
if (mAuth.getCurrentUser() != null) {
|
||||
mAuth.signOut();
|
||||
}
|
||||
prefs.edit().putBoolean("is_logged_in", false).apply();
|
||||
}
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
|
||||
|
||||
setupBiometrics();
|
||||
}
|
||||
|
||||
private void setupBiometrics() {
|
||||
BiometricManager biometricManager = BiometricManager.from(this);
|
||||
int canAuthenticate = biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL);
|
||||
|
||||
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
|
||||
String savedEmail = prefs.getString("saved_email", null);
|
||||
String savedPass = prefs.getString("saved_pass", null);
|
||||
|
||||
if (canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS && savedEmail != null && savedPass != null) {
|
||||
binding.biometricButton.setVisibility(android.view.View.VISIBLE);
|
||||
binding.biometricButton.setOnClickListener(v -> showBiometricPrompt(savedEmail, savedPass));
|
||||
}
|
||||
}
|
||||
|
||||
private void showBiometricPrompt(String email, String pass) {
|
||||
Executor executor = ContextCompat.getMainExecutor(this);
|
||||
BiometricPrompt biometricPrompt = new BiometricPrompt(LoginActivity.this, executor, new BiometricPrompt.AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
|
||||
super.onAuthenticationError(errorCode, errString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
||||
super.onAuthenticationSucceeded(result);
|
||||
// Perform login with saved credentials
|
||||
loginWithSavedCredentials(email, pass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed();
|
||||
}
|
||||
});
|
||||
|
||||
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle("Autenticação Biométrica")
|
||||
.setSubtitle("Entre na sua conta usando biometria")
|
||||
.setNegativeButtonText("Usar Password")
|
||||
.build();
|
||||
|
||||
biometricPrompt.authenticate(promptInfo);
|
||||
}
|
||||
|
||||
private void loginWithSavedCredentials(String email, String pass) {
|
||||
binding.loginButton.setEnabled(false);
|
||||
binding.loginButton.setText("A entrar...");
|
||||
|
||||
mAuth.signInWithEmailAndPassword(email, pass)
|
||||
.addOnCompleteListener(this, task -> {
|
||||
if (task.isSuccessful()) {
|
||||
FirebaseUser user = mAuth.getCurrentUser();
|
||||
if (user != null) {
|
||||
handleSuccessfulAuth(user, email, pass, true);
|
||||
}
|
||||
} else {
|
||||
binding.loginButton.setEnabled(true);
|
||||
binding.loginButton.setText(R.string.login_button);
|
||||
Toast.makeText(this, "Erro no login biométrico. Use a password.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
binding.loginButton.setEnabled(false);
|
||||
binding.loginButton.setText("A entrar...");
|
||||
|
||||
mAuth.signInWithEmailAndPassword(email, password)
|
||||
.addOnCompleteListener(this, task -> {
|
||||
if (task.isSuccessful()) {
|
||||
FirebaseUser user = mAuth.getCurrentUser();
|
||||
if (user != null) {
|
||||
boolean rememberMe = binding.checkboxRememberMe.isChecked();
|
||||
handleSuccessfulAuth(user, email, password, rememberMe);
|
||||
}
|
||||
} else {
|
||||
binding.loginButton.setEnabled(true);
|
||||
binding.loginButton.setText(R.string.login_button);
|
||||
|
||||
String errorMsg = task.getException() != null ? task.getException().getMessage()
|
||||
: "Erro desconhecido";
|
||||
if (errorMsg != null) {
|
||||
if (errorMsg.contains("invalid credential") || errorMsg.contains("password is invalid")
|
||||
|| errorMsg.contains("There is no user record")) {
|
||||
errorMsg = "Email ou palavra-passe incorretos.";
|
||||
} else if (errorMsg.contains("email address is badly formatted")) {
|
||||
errorMsg = "O formato do email é inválido.";
|
||||
} else if (errorMsg.contains("network")) {
|
||||
errorMsg = "Erro de rede. Verifique a sua ligação à internet.";
|
||||
}
|
||||
}
|
||||
Toast.makeText(LoginActivity.this, "Falha na autenticação: " + errorMsg, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSuccessfulAuth(FirebaseUser user, String email, String password, boolean rememberMe) {
|
||||
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
|
||||
prefs.edit().putBoolean("is_logged_in", true).apply();
|
||||
prefs.edit().putBoolean("remember_me", rememberMe).apply();
|
||||
|
||||
// Guardar sempre para biometria após login com sucesso
|
||||
prefs.edit().putString("saved_email", email).apply();
|
||||
prefs.edit().putString("saved_pass", password).apply();
|
||||
|
||||
// Tentar primeiro na coleção 'utilizadores'
|
||||
com.google.firebase.firestore.FirebaseFirestore db = com.google.firebase.firestore.FirebaseFirestore.getInstance();
|
||||
db.collection("utilizadores").document(user.getUid()).get()
|
||||
.addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful() && task.getResult() != null && task.getResult().exists()) {
|
||||
com.google.firebase.firestore.DocumentSnapshot doc = task.getResult();
|
||||
String name = doc.getString("name");
|
||||
String dbEmail = doc.getString("email");
|
||||
|
||||
if (name != null) prefs.edit().putString("user_name", name).apply();
|
||||
if (dbEmail != null) prefs.edit().putString("user_email", dbEmail).apply();
|
||||
|
||||
proceedToMain();
|
||||
} else {
|
||||
// Tentar na coleção 'medicos'
|
||||
db.collection("medicos").document(user.getUid()).get()
|
||||
.addOnCompleteListener(task2 -> {
|
||||
if (task2.isSuccessful() && task2.getResult() != null && task2.getResult().exists()) {
|
||||
com.google.firebase.firestore.DocumentSnapshot doc = task2.getResult();
|
||||
String name = doc.getString("nome"); // Notar campo 'nome' em vez de 'name'
|
||||
String dbEmail = doc.getString("email");
|
||||
|
||||
if (name != null) prefs.edit().putString("user_name", name).apply();
|
||||
if (dbEmail != null) prefs.edit().putString("user_email", dbEmail).apply();
|
||||
|
||||
proceedToMain();
|
||||
} else {
|
||||
// Fallback se não encontrar em lado nenhum
|
||||
prefs.edit().putString("user_email", user.getEmail()).apply();
|
||||
if (user.getDisplayName() != null && !user.getDisplayName().isEmpty()) {
|
||||
prefs.edit().putString("user_name", user.getDisplayName()).apply();
|
||||
} else {
|
||||
String authEmail = user.getEmail();
|
||||
if (authEmail != null && authEmail.contains("@")) {
|
||||
String fallbackName = authEmail.substring(0, authEmail.indexOf("@"));
|
||||
prefs.edit().putString("user_name", fallbackName).apply();
|
||||
}
|
||||
}
|
||||
proceedToMain();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void proceedToMain() {
|
||||
Toast.makeText(LoginActivity.this, "Bem-vindo!", Toast.LENGTH_SHORT).show();
|
||||
startActivity(new Intent(LoginActivity.this, com.example.cuida.MainActivity.class));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
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.model.User;
|
||||
import com.example.cuida.databinding.ActivityRegisterBinding;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
|
||||
public class RegisterActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityRegisterBinding binding;
|
||||
private FirebaseAuth mAuth;
|
||||
|
||||
@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();
|
||||
});
|
||||
|
||||
String[] genders = new String[]{"Masculino", "Feminino"};
|
||||
android.widget.ArrayAdapter<String> adapter = new android.widget.ArrayAdapter<>(
|
||||
this, android.R.layout.simple_dropdown_item_1line, genders);
|
||||
binding.genderAutoComplete.setAdapter(adapter);
|
||||
}
|
||||
|
||||
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();
|
||||
String gender = binding.genderAutoComplete.getText().toString();
|
||||
|
||||
if (name.isEmpty() || ageStr.isEmpty() || email.isEmpty() || password.isEmpty() || utenteStr.isEmpty() || gender.isEmpty()) {
|
||||
Toast.makeText(this, "Preencha todos os campos", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (utenteStr.length() != 9) {
|
||||
Toast.makeText(this, "O Nº de Utente tem de ter 9 dígitos", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
int age = Integer.parseInt(ageStr);
|
||||
|
||||
binding.registerButton.setEnabled(false);
|
||||
binding.registerButton.setText("A registar...");
|
||||
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
FirebaseFirestore db = FirebaseFirestore
|
||||
.getInstance();
|
||||
mAuth.createUserWithEmailAndPassword(email, password)
|
||||
.addOnCompleteListener(this, task -> {
|
||||
if (task.isSuccessful()) {
|
||||
// Registration success, save additional info to Firestore
|
||||
FirebaseUser firebaseUser = mAuth.getCurrentUser();
|
||||
if (firebaseUser != null) {
|
||||
saveUserData(firebaseUser.getUid(), name, email, ageStr, utenteStr, gender);
|
||||
}
|
||||
} else {
|
||||
Exception e = task.getException();
|
||||
if (e instanceof com.google.firebase.auth.FirebaseAuthUserCollisionException) {
|
||||
// Tenta fazer login automático para reparar o perfil se ele não existir no Firestore
|
||||
mAuth.signInWithEmailAndPassword(email, password)
|
||||
.addOnSuccessListener(authResult -> {
|
||||
saveUserData(authResult.getUser().getUid(), name, email, ageStr, utenteStr, gender);
|
||||
})
|
||||
.addOnFailureListener(err -> {
|
||||
binding.registerButton.setEnabled(true);
|
||||
binding.registerButton.setText("Registar");
|
||||
Toast.makeText(RegisterActivity.this, "Este email já está registado com outra password.", Toast.LENGTH_LONG).show();
|
||||
});
|
||||
} else {
|
||||
binding.registerButton.setEnabled(true);
|
||||
binding.registerButton.setText("Registar");
|
||||
String errorMsg = e != null ? e.getMessage() : "Erro desconhecido";
|
||||
Toast.makeText(RegisterActivity.this, "Erro: " + errorMsg, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveUserData(String userId, String name, String email, String ageStr, String utenteStr, String gender) {
|
||||
FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
java.util.Map<String, Object> userMap = new java.util.HashMap<>();
|
||||
userMap.put("id", userId);
|
||||
userMap.put("nome_completo", name);
|
||||
userMap.put("email", email);
|
||||
userMap.put("idade", ageStr);
|
||||
userMap.put("numero_utente", utenteStr);
|
||||
userMap.put("sexo", gender);
|
||||
userMap.put("tipo", "paciente");
|
||||
userMap.put("profilePictureUri", "");
|
||||
|
||||
db.collection("utilizadores").document(userId)
|
||||
.set(userMap)
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
Toast.makeText(RegisterActivity.this, "Conta configurada com sucesso!", Toast.LENGTH_SHORT).show();
|
||||
startActivity(new Intent(RegisterActivity.this, LoginActivity.class));
|
||||
finish();
|
||||
})
|
||||
.addOnFailureListener(e -> {
|
||||
binding.registerButton.setEnabled(true);
|
||||
binding.registerButton.setText("Registar");
|
||||
Toast.makeText(RegisterActivity.this, "Erro ao guardar no banco de dados: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.example.cuida.ui.auth;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.example.cuida.databinding.ActivityResetPasswordBinding;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
|
||||
public class ResetPasswordActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityResetPasswordBinding binding;
|
||||
private String oobCode;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityResetPasswordBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
// Use custom uri scheme or https scheme, extracting oobCode parameter
|
||||
Intent intent = getIntent();
|
||||
if (intent != null && intent.getData() != null) {
|
||||
Uri data = intent.getData();
|
||||
oobCode = data.getQueryParameter("oobCode");
|
||||
|
||||
if (oobCode == null || oobCode.isEmpty()) {
|
||||
Toast.makeText(this, "Link de redefinição inválido.", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "Nenhum código de redefinição encontrado.", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
binding.saveNewPasswordButton.setOnClickListener(v -> saveNewPassword());
|
||||
}
|
||||
|
||||
private void saveNewPassword() {
|
||||
String newPassword = binding.newPasswordEditText.getText().toString();
|
||||
String confirmPassword = binding.confirmNewPasswordEditText.getText().toString();
|
||||
|
||||
if (newPassword.isEmpty() || confirmPassword.isEmpty()) {
|
||||
Toast.makeText(this, "Preencha ambas as palavras-passe.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newPassword.equals(confirmPassword)) {
|
||||
Toast.makeText(this, "As palavras-passe não coincidem.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length() < 6) {
|
||||
Toast.makeText(this, "A palavra-passe deve ter pelo menos 6 caracteres.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
binding.saveNewPasswordButton.setEnabled(false);
|
||||
binding.saveNewPasswordButton.setText("A guardar...");
|
||||
|
||||
FirebaseAuth.getInstance().confirmPasswordReset(oobCode, newPassword)
|
||||
.addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
Toast.makeText(this, "Palavra-passe atualizada com sucesso!", Toast.LENGTH_LONG).show();
|
||||
// Go back to login screen
|
||||
Intent intent = new Intent(ResetPasswordActivity.this, LoginActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
} else {
|
||||
binding.saveNewPasswordButton.setEnabled(true);
|
||||
binding.saveNewPasswordButton.setText("Guardar Palavra-passe");
|
||||
|
||||
String errorMsg = task.getException() != null ? task.getException().getMessage() : "Erro desconhecido";
|
||||
Toast.makeText(this, "Erro: " + errorMsg, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
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 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 & Profile Picture ---
|
||||
com.google.firebase.auth.FirebaseAuth auth = com.google.firebase.auth.FirebaseAuth.getInstance();
|
||||
if (auth.getCurrentUser() != null) {
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("utilizadores").document(userId)
|
||||
.get()
|
||||
.addOnSuccessListener(documentSnapshot -> {
|
||||
if (documentSnapshot.exists() && isAdded()) {
|
||||
// Tenta 'nome_completo' (novo) ou 'name' (antigo)
|
||||
String name = documentSnapshot.getString("nome_completo");
|
||||
if (name == null || name.isEmpty()) name = documentSnapshot.getString("name");
|
||||
|
||||
if (name != null && !name.isEmpty()) {
|
||||
// Extract first name
|
||||
String firstName = name.split(" ")[0];
|
||||
binding.textGreeting.setText("Olá, " + firstName + "!");
|
||||
} else {
|
||||
binding.textGreeting.setText("Olá, Utilizador!");
|
||||
}
|
||||
|
||||
// Load Profile Picture
|
||||
String profilePictureUri = documentSnapshot.getString("profilePictureUri");
|
||||
if (profilePictureUri != null && !profilePictureUri.isEmpty()) {
|
||||
try {
|
||||
binding.imageProfileHome.setImageURI(android.net.Uri.parse(profilePictureUri));
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("HomeFragment", "Error loading profile pic view: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(e -> {
|
||||
if (isAdded())
|
||||
binding.textGreeting.setText("Olá, Utilizador!");
|
||||
});
|
||||
} else {
|
||||
binding.textGreeting.setText("Olá, Utilizador!");
|
||||
}
|
||||
|
||||
// --- 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 -> {
|
||||
androidx.navigation.Navigation.findNavController(v)
|
||||
.navigate(com.example.cuida.R.id.action_home_to_schedule_appointment);
|
||||
});
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.example.cuida.ui.medication;
|
||||
|
||||
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.Comprimido;
|
||||
import java.util.List;
|
||||
|
||||
public class ComprimidoRecyclerAdapter extends RecyclerView.Adapter<ComprimidoRecyclerAdapter.ViewHolder> {
|
||||
|
||||
private List<Comprimido> pills;
|
||||
private OnItemClickListener listener;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(Comprimido comprimido);
|
||||
}
|
||||
|
||||
public ComprimidoRecyclerAdapter(List<Comprimido> pills, OnItemClickListener listener) {
|
||||
this.pills = pills;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_comprimido_search, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Comprimido pill = pills.get(position);
|
||||
holder.textName.setText(pill.nome);
|
||||
holder.itemView.setOnClickListener(v -> listener.onItemClick(pill));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return pills.size();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView textName;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
textName = itemView.findViewById(R.id.text_pill_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
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);
|
||||
|
||||
void onItemClick(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 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);
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
int position = getAdapterPosition();
|
||||
if (listener != null && position != RecyclerView.NO_POSITION) {
|
||||
listener.onItemClick(medicationList.get(position));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,368 +0,0 @@
|
||||
package com.example.cuida.ui.medication;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import com.example.cuida.R;
|
||||
import com.example.cuida.data.model.Medication;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.google.firebase.database.DataSnapshot;
|
||||
import com.google.firebase.database.DatabaseError;
|
||||
import com.google.firebase.database.DatabaseReference;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
import com.google.firebase.database.ValueEventListener;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import com.example.cuida.data.model.Comprimido;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import java.util.Collections;
|
||||
|
||||
public class MedicationDialog extends DialogFragment {
|
||||
|
||||
private TextInputEditText editName;
|
||||
private RecyclerView recyclerResults;
|
||||
private ComprimidoRecyclerAdapter recyclerAdapter;
|
||||
private List<Comprimido> searchResults = new ArrayList<>();
|
||||
private List<Comprimido> fullPillsList = new ArrayList<>();
|
||||
private DatabaseReference medicationRef;
|
||||
private EditText editNotes;
|
||||
private android.widget.RadioButton radioOral, radioTopical, radioInhalatory;
|
||||
private android.widget.RadioGroup radioGroupRoute;
|
||||
private ChipGroup chipGroupTimes;
|
||||
private List<String> selectedTimes = new ArrayList<>();
|
||||
private Medication medicationToEdit;
|
||||
private OnMedicationSaveListener listener;
|
||||
private OnMedicationDeleteListener deleteListener;
|
||||
|
||||
public interface OnMedicationSaveListener {
|
||||
void onSave(Medication medication);
|
||||
}
|
||||
|
||||
public interface OnMedicationDeleteListener {
|
||||
void onDelete(Medication medication);
|
||||
}
|
||||
|
||||
public void setListener(OnMedicationSaveListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setDeleteListener(OnMedicationDeleteListener listener) {
|
||||
this.deleteListener = listener;
|
||||
}
|
||||
|
||||
public void setMedicationToEdit(Medication medication) {
|
||||
this.medicationToEdit = medication;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
||||
LayoutInflater inflater = requireActivity().getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.dialog_add_medication, null);
|
||||
|
||||
editName = view.findViewById(R.id.edit_med_name);
|
||||
recyclerResults = view.findViewById(R.id.recycler_search_results);
|
||||
editNotes = view.findViewById(R.id.edit_med_notes);
|
||||
chipGroupTimes = view.findViewById(R.id.chip_group_times);
|
||||
MaterialButton btnAddTime = view.findViewById(R.id.btn_add_time);
|
||||
|
||||
radioGroupRoute = view.findViewById(R.id.radio_group_route);
|
||||
radioOral = view.findViewById(R.id.radio_oral);
|
||||
radioTopical = view.findViewById(R.id.radio_topical);
|
||||
radioInhalatory = view.findViewById(R.id.radio_inhalatory);
|
||||
|
||||
final android.content.Context currentContext = getContext();
|
||||
if (currentContext != null) {
|
||||
recyclerAdapter = new ComprimidoRecyclerAdapter(searchResults, selected -> {
|
||||
editName.setText(selected.nome);
|
||||
editName.setSelection(selected.nome.length());
|
||||
|
||||
// Adiciona a dosagem/informação ao campo de notas automaticamente
|
||||
if (selected.dosagem != null && !selected.dosagem.isEmpty()) {
|
||||
editNotes.setText(selected.dosagem);
|
||||
}
|
||||
|
||||
recyclerResults.setVisibility(View.GONE);
|
||||
searchResults.clear();
|
||||
});
|
||||
recyclerResults.setLayoutManager(new LinearLayoutManager(currentContext));
|
||||
recyclerResults.setAdapter(recyclerAdapter);
|
||||
|
||||
String dbUrl = "https://cuidamais-7b904-default-rtdb.firebaseio.com/";
|
||||
medicationRef = FirebaseDatabase.getInstance(dbUrl).getReference("medication");
|
||||
|
||||
// Carregar todos os medicamentos uma única vez para filtragem local rápida
|
||||
fetchAllMedsOnce();
|
||||
|
||||
editName.addTextChangedListener(new TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
filterMedsLocally(s.toString().trim());
|
||||
}
|
||||
@Override public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
}
|
||||
|
||||
radioOral = view.findViewById(R.id.radio_oral);
|
||||
radioTopical = view.findViewById(R.id.radio_topical);
|
||||
radioInhalatory = view.findViewById(R.id.radio_inhalatory);
|
||||
|
||||
// Set up TimePicker
|
||||
btnAddTime.setOnClickListener(v -> showTimePicker());
|
||||
|
||||
if (medicationToEdit != null) {
|
||||
editName.setText(medicationToEdit.name);
|
||||
editNotes.setText(medicationToEdit.notes);
|
||||
if (medicationToEdit.time != null && !medicationToEdit.time.isEmpty()) {
|
||||
String[] times = medicationToEdit.time.split(",\\s*");
|
||||
for (String t : times) {
|
||||
if (!t.isEmpty()) selectedTimes.add(t);
|
||||
}
|
||||
java.util.Collections.sort(selectedTimes);
|
||||
refreshTimeChips();
|
||||
}
|
||||
|
||||
String dosage = medicationToEdit.dosage;
|
||||
if (dosage != null) {
|
||||
if (dosage.contains("Oral"))
|
||||
radioOral.setChecked(true);
|
||||
else if (dosage.contains("Tópica"))
|
||||
radioTopical.setChecked(true);
|
||||
else if (dosage.contains("Inalatória"))
|
||||
radioInhalatory.setChecked(true);
|
||||
}
|
||||
|
||||
builder.setTitle("Editar Medicamento");
|
||||
} else {
|
||||
builder.setTitle("Adicionar Medicamento");
|
||||
// Default time to current time
|
||||
Calendar cal = Calendar.getInstance();
|
||||
String defaultTime = String.format(Locale.getDefault(), "%02d:%02d", cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
|
||||
selectedTimes.add(defaultTime);
|
||||
refreshTimeChips();
|
||||
}
|
||||
|
||||
builder.setView(view)
|
||||
.setPositiveButton("Guardar", (dialog, id) -> {
|
||||
String name = editName.getText().toString();
|
||||
String notes = editNotes.getText().toString();
|
||||
|
||||
// Join times with comma
|
||||
StringBuilder timeBuilder = new StringBuilder();
|
||||
for (int i = 0; i < selectedTimes.size(); i++) {
|
||||
timeBuilder.append(selectedTimes.get(i));
|
||||
if (i < selectedTimes.size() - 1) timeBuilder.append(", ");
|
||||
}
|
||||
String time = timeBuilder.toString();
|
||||
|
||||
int selectedId = radioGroupRoute.getCheckedRadioButtonId();
|
||||
String dosage = "Via não especificada";
|
||||
|
||||
if (selectedId == R.id.radio_oral) {
|
||||
dosage = "Via Oral";
|
||||
} else if (selectedId == R.id.radio_topical) {
|
||||
dosage = "Via Tópica";
|
||||
} else if (selectedId == R.id.radio_inhalatory) {
|
||||
dosage = "Via Inalatória";
|
||||
}
|
||||
|
||||
if (medicationToEdit != null) {
|
||||
medicationToEdit.name = name;
|
||||
medicationToEdit.dosage = dosage;
|
||||
medicationToEdit.notes = notes;
|
||||
medicationToEdit.time = time;
|
||||
if (listener != null)
|
||||
listener.onSave(medicationToEdit);
|
||||
} else {
|
||||
Medication newMed = new Medication(name, time, dosage, notes, null);
|
||||
if (listener != null)
|
||||
listener.onSave(newMed);
|
||||
}
|
||||
});
|
||||
|
||||
if (medicationToEdit != null) {
|
||||
builder.setNeutralButton("Eliminar", (dialog, id) -> {
|
||||
if (deleteListener != null) {
|
||||
deleteListener.onDelete(medicationToEdit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AlertDialog alertDialog = builder.create();
|
||||
|
||||
alertDialog.setOnShowListener(d -> {
|
||||
android.widget.Button btnPos = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
if (btnPos != null) {
|
||||
// Apply our custom outline drawable to "Guardar"
|
||||
btnPos.setBackgroundResource(R.drawable.btn_outline_primary);
|
||||
btnPos.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.primary_color));
|
||||
|
||||
int paddingPx = (int) (16 * getResources().getDisplayMetrics().density);
|
||||
btnPos.setPadding(paddingPx, 0, paddingPx, 0);
|
||||
}
|
||||
|
||||
android.widget.Button btnNeu = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
|
||||
if (btnNeu != null) {
|
||||
btnNeu.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.error_color));
|
||||
btnNeu.setBackgroundResource(R.drawable.btn_outline_error);
|
||||
|
||||
int paddingPx = (int) (16 * getResources().getDisplayMetrics().density);
|
||||
btnNeu.setPadding(paddingPx, 0, paddingPx, 0);
|
||||
|
||||
android.view.ViewGroup.LayoutParams lp = btnNeu.getLayoutParams();
|
||||
if (lp instanceof android.view.ViewGroup.MarginLayoutParams) {
|
||||
android.view.ViewGroup.MarginLayoutParams marginLp = (android.view.ViewGroup.MarginLayoutParams) lp;
|
||||
int marginPx = (int) (8 * getResources().getDisplayMetrics().density);
|
||||
marginLp.setMargins(marginLp.leftMargin, marginLp.topMargin, marginLp.rightMargin + marginPx, marginLp.bottomMargin);
|
||||
btnNeu.setLayoutParams(marginLp);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
private void showTimePicker() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
int hour = cal.get(Calendar.HOUR_OF_DAY);
|
||||
int minute = cal.get(Calendar.MINUTE);
|
||||
|
||||
TimePickerDialog timePickerDialog = new TimePickerDialog(getContext(),
|
||||
(view, hourOfDay, minute1) -> {
|
||||
String time = String.format(Locale.getDefault(), "%02d:%02d", hourOfDay, minute1);
|
||||
if (!selectedTimes.contains(time)) {
|
||||
selectedTimes.add(time);
|
||||
Collections.sort(selectedTimes);
|
||||
refreshTimeChips();
|
||||
}
|
||||
},
|
||||
hour, minute, true);
|
||||
timePickerDialog.show();
|
||||
}
|
||||
|
||||
private void refreshTimeChips() {
|
||||
if (chipGroupTimes == null || getContext() == null) return;
|
||||
chipGroupTimes.removeAllViews();
|
||||
for (String time : selectedTimes) {
|
||||
Chip chip = new Chip(getContext());
|
||||
chip.setText(time);
|
||||
chip.setCloseIconVisible(true);
|
||||
chip.setOnCloseIconClickListener(v -> {
|
||||
selectedTimes.remove(time);
|
||||
refreshTimeChips();
|
||||
});
|
||||
chipGroupTimes.addView(chip);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchAllMedsOnce() {
|
||||
String dbUrl = "https://cuidamais-7b904-default-rtdb.firebaseio.com/";
|
||||
DatabaseReference rootRef = FirebaseDatabase.getInstance(dbUrl).getReference();
|
||||
String[] nodes = {"medication", "medicamentos", "Medicamentos", "comprimidos"};
|
||||
|
||||
fullPillsList.clear();
|
||||
|
||||
// 1. Tentar nos nós específicos
|
||||
for (String node : nodes) {
|
||||
rootRef.child(node).addListenerForSingleValueEvent(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
if (snapshot.exists()) {
|
||||
parseSnapshot(snapshot, "Nó: " + node);
|
||||
}
|
||||
}
|
||||
@Override public void onCancelled(@NonNull DatabaseError error) {}
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Tentar também na raiz (caso os medicamentos estejam diretamente no topo)
|
||||
rootRef.limitToFirst(50).addListenerForSingleValueEvent(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
if (snapshot.exists()) {
|
||||
parseSnapshot(snapshot, "Raiz");
|
||||
}
|
||||
}
|
||||
@Override public void onCancelled(@NonNull DatabaseError error) {}
|
||||
});
|
||||
}
|
||||
|
||||
private void parseSnapshot(DataSnapshot snapshot, String source) {
|
||||
int count = 0;
|
||||
for (DataSnapshot child : snapshot.getChildren()) {
|
||||
String name = child.child("nome").getValue(String.class);
|
||||
if (name == null) name = child.child("name").getValue(String.class);
|
||||
if (name == null && !(child.getValue() instanceof java.util.Map)) {
|
||||
// Se o valor for a própria string (ex: "Paracetamol")
|
||||
name = child.getValue() instanceof String ? (String) child.getValue() : null;
|
||||
}
|
||||
if (name == null) name = child.getKey();
|
||||
|
||||
String dosage = child.child("dosagem").getValue(String.class);
|
||||
if (dosage == null) dosage = child.child("dosage").getValue(String.class);
|
||||
if (dosage == null) dosage = "";
|
||||
|
||||
if (name != null && !name.isEmpty()) {
|
||||
boolean exists = false;
|
||||
for (Comprimido p : fullPillsList) {
|
||||
if (name.equals(p.nome)) { exists = true; break; }
|
||||
}
|
||||
if (!exists) {
|
||||
fullPillsList.add(new Comprimido(name, dosage));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0 && getContext() != null) {
|
||||
Log.d("FirebaseSearch", "Carregados " + count + " de " + source);
|
||||
// Toast.makeText(getContext(), "Fonte: " + source + " (" + count + ")", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void filterMedsLocally(String query) {
|
||||
searchResults.clear();
|
||||
if (query.isEmpty()) {
|
||||
recyclerResults.setVisibility(View.GONE);
|
||||
recyclerAdapter.notifyDataSetChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
String lowerQuery = query.toLowerCase();
|
||||
for (Comprimido p : fullPillsList) {
|
||||
if (p.nome != null && p.nome.toLowerCase().contains(lowerQuery)) {
|
||||
searchResults.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
recyclerAdapter.notifyDataSetChanged();
|
||||
recyclerResults.setVisibility(searchResults.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private void handleError(DatabaseError error) {
|
||||
Log.e("FirebaseSearch", "Erro: " + error.getMessage());
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), "Erro no Firebase: " + error.getMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
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.data.model.Medication;
|
||||
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(new MedicationAdapter.OnItemClickListener() {
|
||||
@Override
|
||||
public void onCheckClick(Medication medication) {
|
||||
medicationViewModel.update(medication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(Medication medication) {
|
||||
showMedicationDialog(medication);
|
||||
}
|
||||
});
|
||||
|
||||
binding.recyclerMedication.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerMedication.setAdapter(adapter);
|
||||
|
||||
medicationViewModel.getAllMedications().observe(getViewLifecycleOwner(), medications -> {
|
||||
adapter.setMedications(medications);
|
||||
|
||||
if (medications != null && !medications.isEmpty()) {
|
||||
binding.recyclerMedication.setVisibility(View.VISIBLE);
|
||||
binding.textEmptyMedications.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.recyclerMedication.setVisibility(View.GONE);
|
||||
binding.textEmptyMedications.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
binding.fabAddMedication.setOnClickListener(v -> showMedicationDialog(null));
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void showMedicationDialog(Medication medication) {
|
||||
MedicationDialog dialog = new MedicationDialog();
|
||||
dialog.setMedicationToEdit(medication);
|
||||
dialog.setListener(medicationToSave -> {
|
||||
// If it's an edit, cancel old alarms first
|
||||
if (medication != null && medication.time != null) {
|
||||
String[] oldTimes = medication.time.split(",\\s*");
|
||||
for (String t : oldTimes) {
|
||||
if (t.isEmpty()) continue;
|
||||
try {
|
||||
int oldId = (medication.name + t).hashCode();
|
||||
com.example.cuida.utils.AlarmScheduler.cancelAlarm(requireContext(), oldId);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (medication == null) {
|
||||
medicationViewModel.insert(medicationToSave);
|
||||
} else {
|
||||
medicationViewModel.update(medicationToSave);
|
||||
}
|
||||
|
||||
String[] times = medicationToSave.time.split(",\\s*");
|
||||
for (String t : times) {
|
||||
if (t.isEmpty()) continue;
|
||||
try {
|
||||
String[] timeParts = t.split(":");
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
|
||||
java.util.Calendar calendar = java.util.Calendar.getInstance();
|
||||
calendar.set(java.util.Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(java.util.Calendar.MINUTE, minute);
|
||||
calendar.set(java.util.Calendar.SECOND, 0);
|
||||
|
||||
if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
|
||||
calendar.add(java.util.Calendar.DAY_OF_YEAR, 1);
|
||||
}
|
||||
|
||||
String title = "Hora do Medicamento";
|
||||
String msg = "É hora de tomar: " + medicationToSave.name + " (" + medicationToSave.dosage + ")";
|
||||
|
||||
int alarmId = (medicationToSave.name + t).hashCode();
|
||||
|
||||
com.example.cuida.utils.AlarmScheduler.scheduleAlarm(
|
||||
requireContext(),
|
||||
calendar.getTimeInMillis(),
|
||||
title,
|
||||
msg,
|
||||
alarmId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.setDeleteListener(medicationToDelete -> {
|
||||
medicationViewModel.delete(medicationToDelete);
|
||||
|
||||
// Cancel all alarms for this medication
|
||||
if (medicationToDelete.time != null) {
|
||||
String[] times = medicationToDelete.time.split(",\\s*");
|
||||
for (String t : times) {
|
||||
if (t.isEmpty()) continue;
|
||||
try {
|
||||
int alarmId = (medicationToDelete.name + t).hashCode();
|
||||
com.example.cuida.utils.AlarmScheduler.cancelAlarm(requireContext(), alarmId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show(getParentFragmentManager(), "MedicationDialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
package com.example.cuida.ui.medication;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.cuida.data.model.Medication;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.Query;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MedicationViewModel extends AndroidViewModel {
|
||||
|
||||
private final MutableLiveData<List<Medication>> allMedications = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Medication> nextMedication = new MutableLiveData<>(null);
|
||||
private final FirebaseFirestore db;
|
||||
private final FirebaseAuth auth;
|
||||
|
||||
public MedicationViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
db = FirebaseFirestore.getInstance();
|
||||
auth = FirebaseAuth.getInstance();
|
||||
fetchMedications();
|
||||
}
|
||||
|
||||
private void fetchMedications() {
|
||||
if (auth.getCurrentUser() == null)
|
||||
return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
db.collection("medicamentos")
|
||||
.whereEqualTo("userId", userId)
|
||||
.addSnapshotListener((value, error) -> {
|
||||
if (error != null) {
|
||||
Log.e("MedicationViewModel", "Listen failed.", error);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Medication> meds = new ArrayList<>();
|
||||
if (value != null) {
|
||||
for (QueryDocumentSnapshot doc : value) {
|
||||
Medication med = doc.toObject(Medication.class);
|
||||
med.setId(doc.getId()); // Ensure ID is set
|
||||
meds.add(med);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort locally to avoid needing a composite index in Firestore
|
||||
meds.sort((m1, m2) -> {
|
||||
if (m1.time == null && m2.time == null) return 0;
|
||||
if (m1.time == null) return 1;
|
||||
if (m2.time == null) return -1;
|
||||
return m1.time.compareTo(m2.time);
|
||||
});
|
||||
|
||||
allMedications.setValue(meds);
|
||||
|
||||
if (!meds.isEmpty()) {
|
||||
nextMedication.setValue(meds.get(0));
|
||||
} else {
|
||||
nextMedication.setValue(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<List<Medication>> getAllMedications() {
|
||||
return allMedications;
|
||||
}
|
||||
|
||||
public LiveData<Medication> getNextMedication() {
|
||||
return nextMedication;
|
||||
}
|
||||
|
||||
public void insert(Medication medication) {
|
||||
if (auth.getCurrentUser() == null)
|
||||
return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
medication.userId = userId;
|
||||
|
||||
db.collection("medicamentos")
|
||||
.add(medication)
|
||||
.addOnSuccessListener(documentReference -> Log.d("MedicationViewModel", "Medication added"))
|
||||
.addOnFailureListener(e -> Log.w("MedicationViewModel", "Error adding medication", e));
|
||||
}
|
||||
|
||||
public void update(Medication medication) {
|
||||
if (auth.getCurrentUser() == null || medication.getId() == null)
|
||||
return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
medication.userId = userId;
|
||||
|
||||
db.collection("medicamentos")
|
||||
.document(medication.getId())
|
||||
.set(medication)
|
||||
.addOnSuccessListener(aVoid -> Log.d("MedicationViewModel", "Medication updated"))
|
||||
.addOnFailureListener(e -> Log.w("MedicationViewModel", "Error updating medication", e));
|
||||
}
|
||||
|
||||
public void delete(Medication medication) {
|
||||
if (auth.getCurrentUser() == null || medication.getId() == null)
|
||||
return;
|
||||
|
||||
db.collection("medicamentos")
|
||||
.document(medication.getId())
|
||||
.delete()
|
||||
.addOnSuccessListener(aVoid -> Log.d("MedicationViewModel", "Medication deleted"))
|
||||
.addOnFailureListener(e -> Log.w("MedicationViewModel", "Error deleting medication", e));
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
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.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.example.cuida.R;
|
||||
import com.example.cuida.data.model.User;
|
||||
import com.example.cuida.databinding.FragmentProfileBinding;
|
||||
import com.example.cuida.ui.auth.LoginActivity;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
|
||||
public class ProfileFragment extends Fragment {
|
||||
|
||||
private FragmentProfileBinding binding;
|
||||
private User currentUser;
|
||||
private FirebaseFirestore db;
|
||||
private FirebaseAuth auth;
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = FragmentProfileBinding.inflate(inflater, container, false);
|
||||
|
||||
db = FirebaseFirestore.getInstance();
|
||||
auth = FirebaseAuth.getInstance();
|
||||
loadUserData();
|
||||
|
||||
binding.buttonEditProfile.setOnClickListener(v -> showEditDialog());
|
||||
|
||||
binding.buttonLogout.setOnClickListener(v -> {
|
||||
auth.signOut();
|
||||
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() {
|
||||
if (auth.getCurrentUser() == null)
|
||||
return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
db.collection("utilizadores").document(userId).get()
|
||||
.addOnSuccessListener(documentSnapshot -> {
|
||||
if (documentSnapshot.exists()) {
|
||||
currentUser = new User();
|
||||
currentUser.id = documentSnapshot.getId();
|
||||
|
||||
// Campos Padronizados
|
||||
String nome = documentSnapshot.getString("nome_completo");
|
||||
if (nome == null) nome = documentSnapshot.getString("name");
|
||||
currentUser.name = nome;
|
||||
|
||||
currentUser.email = documentSnapshot.getString("email");
|
||||
|
||||
String utente = documentSnapshot.getString("numero_utente");
|
||||
if (utente == null) utente = documentSnapshot.getString("utenteNumber");
|
||||
currentUser.utenteNumber = utente;
|
||||
|
||||
currentUser.profilePictureUri = documentSnapshot.getString("profilePictureUri");
|
||||
|
||||
// Lidar com a idade (que agora é String no registo)
|
||||
Object ageObj = documentSnapshot.get("idade");
|
||||
if (ageObj == null) ageObj = documentSnapshot.get("age");
|
||||
|
||||
if (ageObj instanceof Long) {
|
||||
currentUser.age = ((Long) ageObj).intValue();
|
||||
} else if (ageObj instanceof String) {
|
||||
try {
|
||||
currentUser.age = Integer.parseInt((String) ageObj);
|
||||
} catch (NumberFormatException e) {
|
||||
currentUser.age = 0;
|
||||
}
|
||||
} else {
|
||||
currentUser.age = 0;
|
||||
}
|
||||
|
||||
if (isAdded()) {
|
||||
binding.profileName.setText(currentUser.name != null ? currentUser.name : "N/D");
|
||||
binding.profileEmail.setText(currentUser.email != null ? currentUser.email : "N/D");
|
||||
binding.profileAge.setText(currentUser.age > 0 ? String.valueOf(currentUser.age) : "N/D");
|
||||
binding.profileUtente.setText(currentUser.utenteNumber != null ? currentUser.utenteNumber : "N/D");
|
||||
|
||||
if (currentUser.profilePictureUri != null && !currentUser.profilePictureUri.isEmpty()) {
|
||||
android.widget.ImageView profileImage = binding.getRoot().findViewById(R.id.profile_image);
|
||||
if (profileImage != null) {
|
||||
try {
|
||||
profileImage.setImageURI(android.net.Uri.parse(currentUser.profilePictureUri));
|
||||
} catch (Exception e) {
|
||||
Log.e("ProfileFragment", "Error loading pic: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(e -> {
|
||||
if (isAdded()) {
|
||||
Toast.makeText(getContext(), "Failed to load profile data", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private android.net.Uri tempProfileUri;
|
||||
private android.widget.ImageView dialogImageView;
|
||||
|
||||
private final androidx.activity.result.ActivityResultLauncher<String> pickMedia = registerForActivityResult(
|
||||
new androidx.activity.result.contract.ActivityResultContracts.GetContent(), uri -> {
|
||||
if (uri != null) {
|
||||
tempProfileUri = uri;
|
||||
try {
|
||||
requireContext().getContentResolver().takePersistableUriPermission(uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (dialogImageView != null) {
|
||||
dialogImageView.setImageURI(uri);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private void showEditDialog() {
|
||||
if (currentUser == null) {
|
||||
Toast.makeText(getContext(), "Erro: Utilizador não carregado.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
tempProfileUri = null;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
LayoutInflater inflater = requireActivity().getLayoutInflater();
|
||||
View dialogView = inflater.inflate(R.layout.dialog_edit_profile, null);
|
||||
builder.setView(dialogView);
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
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);
|
||||
dialogImageView = dialogView.findViewById(R.id.edit_profile_image);
|
||||
View btnChangePhoto = dialogView.findViewById(R.id.button_change_photo);
|
||||
View btnChangePassword = dialogView.findViewById(R.id.button_change_password);
|
||||
View btnSave = dialogView.findViewById(R.id.button_save);
|
||||
View btnCancel = dialogView.findViewById(R.id.button_cancel);
|
||||
|
||||
editName.setText(currentUser.name);
|
||||
editAge.setText(String.valueOf(currentUser.age));
|
||||
editUtente.setText(currentUser.utenteNumber);
|
||||
editEmail.setText(currentUser.email);
|
||||
|
||||
if (currentUser.profilePictureUri != null && !currentUser.profilePictureUri.isEmpty()) {
|
||||
dialogImageView.setImageURI(android.net.Uri.parse(currentUser.profilePictureUri));
|
||||
}
|
||||
|
||||
dialogImageView.setOnClickListener(v -> pickMedia.launch("image/*"));
|
||||
btnChangePhoto.setOnClickListener(v -> pickMedia.launch("image/*"));
|
||||
|
||||
btnChangePassword.setOnClickListener(v -> {
|
||||
showChangePasswordDialog();
|
||||
});
|
||||
|
||||
btnSave.setOnClickListener(v -> {
|
||||
String newName = editName.getText().toString();
|
||||
String ageStr = editAge.getText().toString();
|
||||
String newUtente = editUtente.getText().toString();
|
||||
String newEmail = editEmail.getText().toString();
|
||||
|
||||
if (newName.isEmpty() || ageStr.isEmpty() || newUtente.isEmpty() || newEmail.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Preencha todos os campos", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newUtente.length() != 9) {
|
||||
Toast.makeText(getContext(), "O Nº de Utente tem de ter 9 dígitos", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
int newAge = 0;
|
||||
try {
|
||||
newAge = Integer.parseInt(ageStr);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
|
||||
boolean emailChanged = !newEmail.equals(currentUser.email);
|
||||
|
||||
currentUser.name = newName;
|
||||
currentUser.age = newAge;
|
||||
currentUser.utenteNumber = newUtente;
|
||||
|
||||
if (tempProfileUri != null) {
|
||||
currentUser.profilePictureUri = tempProfileUri.toString();
|
||||
}
|
||||
|
||||
Runnable saveToFirestore = () -> {
|
||||
currentUser.email = newEmail;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
db.collection("utilizadores").document(userId)
|
||||
.set(currentUser)
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
if (emailChanged) {
|
||||
getContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString("user_email", newEmail)
|
||||
.apply();
|
||||
}
|
||||
|
||||
if (isAdded()) {
|
||||
loadUserData(); // Reload to show new image and data
|
||||
Toast.makeText(getContext(), "Dados atualizados com sucesso!", Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
dialog.dismiss();
|
||||
dialogImageView = null;
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(e -> {
|
||||
if (isAdded()) {
|
||||
Toast.makeText(getContext(), "Erro a guardar perfil.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (emailChanged && auth.getCurrentUser() != null) {
|
||||
auth.getCurrentUser().updateEmail(newEmail)
|
||||
.addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
saveToFirestore.run();
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Erro no email: " + task.getException().getMessage(),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
saveToFirestore.run();
|
||||
}
|
||||
});
|
||||
|
||||
btnCancel.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
dialogImageView = null;
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void showChangePasswordDialog() {
|
||||
if (currentUser == null)
|
||||
return;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
LayoutInflater inflater = requireActivity().getLayoutInflater();
|
||||
View dialogView = inflater.inflate(R.layout.dialog_change_password, null);
|
||||
builder.setView(dialogView);
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
EditText editNewPassword = dialogView.findViewById(R.id.new_password);
|
||||
View btnSave = dialogView.findViewById(R.id.button_save_password);
|
||||
View btnCancel = dialogView.findViewById(R.id.button_cancel_password);
|
||||
|
||||
btnSave.setOnClickListener(v -> {
|
||||
String newPass = editNewPassword.getText().toString();
|
||||
if (newPass.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Insira a nova palavra-passe", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (auth.getCurrentUser() != null) {
|
||||
auth.getCurrentUser().updatePassword(newPass).addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
Toast.makeText(getContext(), "Palavra-passe alterada!", Toast.LENGTH_SHORT).show();
|
||||
dialog.dismiss();
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Erro a alterar: " + task.getException().getMessage(),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
btnCancel.setOnClickListener(v -> dialog.dismiss());
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
package com.example.cuida.ui.schedule;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
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 AutoCompleteTextView spinnerDoctor;
|
||||
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);
|
||||
spinnerDoctor = root.findViewById(R.id.spinner_doctor);
|
||||
recyclerTimeSlots = root.findViewById(R.id.recycler_time_slots);
|
||||
btnConfirm = root.findViewById(R.id.btn_confirm_appointment);
|
||||
|
||||
setupDoctorSpinner();
|
||||
setupDatePicker();
|
||||
setupRecyclerView();
|
||||
setupObservers();
|
||||
|
||||
btnConfirm.setOnClickListener(v -> {
|
||||
com.google.android.material.textfield.TextInputEditText editReason = getView()
|
||||
.findViewById(R.id.edit_reason);
|
||||
String reason = editReason.getText().toString();
|
||||
|
||||
if (scheduleViewModel.getSelectedTime().getValue() == null) {
|
||||
Toast.makeText(getContext(), "Por favor selecione um horário", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reason.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Por favor indique o motivo", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String selectedDoctor = spinnerDoctor.getText().toString();
|
||||
if (selectedDoctor.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Por favor selecione um médico", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isUrgentSymptom(reason)) {
|
||||
showUrgencyAlert(selectedDoctor, reason);
|
||||
} else {
|
||||
scheduleViewModel.confirmAppointment(selectedDoctor, reason);
|
||||
}
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private boolean isUrgentSymptom(String reason) {
|
||||
String lowerReason = reason.toLowerCase();
|
||||
String[] urgentKeywords = {
|
||||
"dor no peito", "falta de ar", "desmaio", "sangramento",
|
||||
"paralisia", "perda de vis", "dormência", "confusão",
|
||||
"aperto no peito", "convulsão", "hemorragia", "asfixia"
|
||||
};
|
||||
|
||||
for (String keyword : urgentKeywords) {
|
||||
if (lowerReason.contains(keyword)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void showUrgencyAlert(String selectedDoctor, String reason) {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Aviso de Urgência Médica")
|
||||
.setMessage("O motivo indicado parece necessitar de atendimento urgente. Para situações graves, dirija-se ao Hospital mais próximo ou ligue 112.\n\nPretende continuar com o agendamento normal?")
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton("Agendar na mesma", (dialog, which) -> {
|
||||
scheduleViewModel.confirmAppointment(selectedDoctor, reason);
|
||||
})
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void setupDoctorSpinner() {
|
||||
scheduleViewModel.getDoctorsList().observe(getViewLifecycleOwner(), doctors -> {
|
||||
if (doctors != null) {
|
||||
java.util.List<String> shuffledDoctors = new java.util.ArrayList<>(doctors);
|
||||
java.util.Collections.shuffle(shuffledDoctors); // Randomize the names as requested
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_dropdown_item_1line, shuffledDoctors);
|
||||
spinnerDoctor.setAdapter(adapter);
|
||||
}
|
||||
});
|
||||
|
||||
spinnerDoctor.setOnItemClickListener((parent, view, position, id) -> {
|
||||
String selectedDoctor = (String) parent.getItemAtPosition(position);
|
||||
scheduleViewModel.setSelectedDoctor(selectedDoctor);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Hide the year component
|
||||
int yearSpinnerId = android.content.res.Resources.getSystem().getIdentifier("year", "id", "android");
|
||||
if (yearSpinnerId != 0) {
|
||||
View yearSpinner = datePicker.findViewById(yearSpinnerId);
|
||||
if (yearSpinner != null) {
|
||||
yearSpinner.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
// Put day on left, month on right
|
||||
int daySpinnerId = android.content.res.Resources.getSystem().getIdentifier("day", "id", "android");
|
||||
int monthSpinnerId = android.content.res.Resources.getSystem().getIdentifier("month", "id", "android");
|
||||
if (daySpinnerId != 0 && monthSpinnerId != 0) {
|
||||
View daySpinner = datePicker.findViewById(daySpinnerId);
|
||||
View monthSpinner = datePicker.findViewById(monthSpinnerId);
|
||||
if (daySpinner != null && monthSpinner != null) {
|
||||
ViewGroup parent = (ViewGroup) daySpinner.getParent();
|
||||
if (parent != null && parent.equals(monthSpinner.getParent())) {
|
||||
int dIndex = parent.indexOfChild(daySpinner);
|
||||
int mIndex = parent.indexOfChild(monthSpinner);
|
||||
// We want Day to be before Month (Day on Left, Month on Right)
|
||||
if (dIndex > mIndex) {
|
||||
parent.removeView(daySpinner);
|
||||
parent.addView(daySpinner, mIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
scheduleViewModel.getSaveError().observe(getViewLifecycleOwner(), errorMsg -> {
|
||||
if (errorMsg != null && !errorMsg.isEmpty()) {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Horário Indisponível")
|
||||
.setMessage(errorMsg)
|
||||
.setPositiveButton("OK", null)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
|
||||
scheduleViewModel.getSelectedDoctorSchedule().observe(getViewLifecycleOwner(), schedule -> {
|
||||
android.widget.TextView textSchedule = getView().findViewById(R.id.text_doctor_schedule);
|
||||
if (textSchedule != null) {
|
||||
textSchedule.setText(schedule);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,334 +0,0 @@
|
||||
package com.example.cuida.ui.schedule;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.cuida.data.model.Appointment;
|
||||
import com.example.cuida.utils.AlarmScheduler;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
import com.google.firebase.firestore.ListenerRegistration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
public class ScheduleViewModel extends AndroidViewModel {
|
||||
|
||||
private final FirebaseFirestore db;
|
||||
private final FirebaseAuth auth;
|
||||
|
||||
private final MutableLiveData<String> selectedDate = new MutableLiveData<>();
|
||||
private final MutableLiveData<String> selectedTime = new MutableLiveData<>();
|
||||
private final MutableLiveData<String> selectedDoctor = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<TimeSlot>> timeSlots = new MutableLiveData<>();
|
||||
private final MutableLiveData<String> selectedDoctorSchedule = new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> saveSuccess = new MutableLiveData<>();
|
||||
private final MutableLiveData<String> saveError = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<String>> doctorsList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final java.util.Map<String, String> doctorSchedules = new java.util.HashMap<>();
|
||||
|
||||
private ListenerRegistration snapshotListener;
|
||||
|
||||
public ScheduleViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
db = FirebaseFirestore.getInstance();
|
||||
auth = FirebaseAuth.getInstance();
|
||||
fetchDoctors();
|
||||
}
|
||||
|
||||
private void fetchDoctors() {
|
||||
db.collection("medicos")
|
||||
.get()
|
||||
.addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful() && task.getResult() != null) {
|
||||
List<String> docs = new ArrayList<>();
|
||||
doctorSchedules.clear();
|
||||
for (QueryDocumentSnapshot document : task.getResult()) {
|
||||
String name = document.getString("nome");
|
||||
if (name == null) name = document.getString("nome_completo");
|
||||
if (name == null) name = document.getString("name");
|
||||
|
||||
String specialty = document.getString("especialidade");
|
||||
String gender = document.getString("genero");
|
||||
String horario = document.getString("horario");
|
||||
|
||||
if (name != null && !name.trim().isEmpty()) {
|
||||
String displayName = name;
|
||||
if (specialty != null && !specialty.trim().isEmpty()) {
|
||||
displayName += " - " + specialty;
|
||||
}
|
||||
if (!displayName.startsWith("Dr.") && !displayName.startsWith("Dra.")) {
|
||||
if ("Feminino".equalsIgnoreCase(gender) || "Feminino".equals(gender)) {
|
||||
displayName = "Dra. " + displayName;
|
||||
} else {
|
||||
displayName = "Dr. " + displayName;
|
||||
}
|
||||
}
|
||||
docs.add(displayName);
|
||||
if (horario != null) {
|
||||
doctorSchedules.put(displayName, horario);
|
||||
}
|
||||
}
|
||||
}
|
||||
doctorsList.postValue(docs);
|
||||
} else {
|
||||
Log.e("ScheduleViewModel", "Error getting doctors", task.getException());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setDate(int year, int month, int dayOfMonth) {
|
||||
String date = String.format("%02d/%02d/%04d", dayOfMonth, month + 1, year);
|
||||
selectedDate.setValue(date);
|
||||
loadTimeSlots(date);
|
||||
}
|
||||
|
||||
public void setSelectedDoctor(String doctor) {
|
||||
selectedDoctor.setValue(doctor);
|
||||
String schedule = doctorSchedules.get(doctor);
|
||||
selectedDoctorSchedule.setValue(schedule != null ? "Horário: " + schedule : "Horário: 08:00 - 19:00");
|
||||
String date = selectedDate.getValue();
|
||||
if (date != null) {
|
||||
loadTimeSlots(date);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<String> getSelectedDoctorSchedule() {
|
||||
return selectedDoctorSchedule;
|
||||
}
|
||||
|
||||
public LiveData<String> getSelectedDate() {
|
||||
return selectedDate;
|
||||
}
|
||||
|
||||
public void setTime(String time) {
|
||||
selectedTime.setValue(time);
|
||||
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;
|
||||
}
|
||||
|
||||
public LiveData<String> getSaveError() {
|
||||
return saveError;
|
||||
}
|
||||
|
||||
public LiveData<List<String>> getDoctorsList() {
|
||||
return doctorsList;
|
||||
}
|
||||
|
||||
private void loadTimeSlots(String date) {
|
||||
if (snapshotListener != null) {
|
||||
snapshotListener.remove();
|
||||
snapshotListener = null;
|
||||
}
|
||||
|
||||
// Init slots immediately to prevent "disappearing" hours while waiting for network.
|
||||
timeSlots.setValue(generateTimeSlots(new ArrayList<>(), date));
|
||||
|
||||
if (auth.getCurrentUser() == null) return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
String doctor = selectedDoctor.getValue();
|
||||
|
||||
// Listen in REAL-TIME for all appointments on the selected date
|
||||
snapshotListener = db.collection("consultas")
|
||||
.whereEqualTo("date", date)
|
||||
.addSnapshotListener((queryDocumentSnapshots, e) -> {
|
||||
if (e != null) {
|
||||
Log.e("ScheduleViewModel", "Listen failed.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> bookedTimes = new ArrayList<>();
|
||||
if (queryDocumentSnapshots != null) {
|
||||
for (QueryDocumentSnapshot document : queryDocumentSnapshots) {
|
||||
Appointment appt = document.toObject(Appointment.class);
|
||||
|
||||
boolean isDoctorAppointment = doctor != null && doctor.equals(appt.type);
|
||||
|
||||
if (isDoctorAppointment && appt.time != null) {
|
||||
if (!bookedTimes.contains(appt.time)) {
|
||||
bookedTimes.add(appt.time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<TimeSlot> slots = generateTimeSlots(bookedTimes, date);
|
||||
timeSlots.setValue(slots);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
if (snapshotListener != null) {
|
||||
snapshotListener.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private List<TimeSlot> generateTimeSlots(List<String> bookedTimes, String selectedDateStr) {
|
||||
List<TimeSlot> slots = new ArrayList<>();
|
||||
int startHour = 8;
|
||||
int endHour = 19;
|
||||
int startMinute = 0;
|
||||
int endMinute = 0;
|
||||
|
||||
String doctor = selectedDoctor.getValue();
|
||||
String schedule = doctorSchedules.get(doctor);
|
||||
|
||||
if (schedule != null && schedule.contains(" - ")) {
|
||||
try {
|
||||
String[] parts = schedule.split(" - ");
|
||||
String[] startParts = parts[0].split(":");
|
||||
String[] endParts = parts[1].split(":");
|
||||
startHour = Integer.parseInt(startParts[0]);
|
||||
startMinute = Integer.parseInt(startParts[1]);
|
||||
endHour = Integer.parseInt(endParts[0]);
|
||||
endMinute = Integer.parseInt(endParts[1]);
|
||||
} catch (Exception e) {
|
||||
Log.e("ScheduleViewModel", "Error parsing schedule: " + schedule);
|
||||
}
|
||||
}
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
boolean isToday = false;
|
||||
|
||||
if (selectedDateStr != null) {
|
||||
String todayStr = String.format("%02d/%02d/%04d",
|
||||
now.get(Calendar.DAY_OF_MONTH),
|
||||
now.get(Calendar.MONTH) + 1,
|
||||
now.get(Calendar.YEAR));
|
||||
if (todayStr.equals(selectedDateStr)) {
|
||||
isToday = true;
|
||||
}
|
||||
}
|
||||
|
||||
int currentHour = now.get(Calendar.HOUR_OF_DAY);
|
||||
int currentMinute = now.get(Calendar.MINUTE);
|
||||
|
||||
Calendar cursor = Calendar.getInstance();
|
||||
cursor.set(Calendar.HOUR_OF_DAY, startHour);
|
||||
cursor.set(Calendar.MINUTE, startMinute);
|
||||
cursor.set(Calendar.SECOND, 0);
|
||||
cursor.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Calendar endLimit = Calendar.getInstance();
|
||||
endLimit.set(Calendar.HOUR_OF_DAY, endHour);
|
||||
endLimit.set(Calendar.MINUTE, endMinute);
|
||||
endLimit.set(Calendar.SECOND, 0);
|
||||
endLimit.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
while (cursor.before(endLimit)) {
|
||||
int h = cursor.get(Calendar.HOUR_OF_DAY);
|
||||
int m = cursor.get(Calendar.MINUTE);
|
||||
String timeStr = String.format("%02d:%02d", h, m);
|
||||
|
||||
if (!isToday || h > currentHour || (h == currentHour && m > currentMinute)) {
|
||||
addSlot(slots, timeStr, bookedTimes);
|
||||
}
|
||||
|
||||
cursor.add(Calendar.MINUTE, 20);
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
private void addSlot(List<TimeSlot> slots, String time, List<String> bookedTimes) {
|
||||
boolean isBooked = bookedTimes.contains(time);
|
||||
boolean isSelected = time.equals(selectedTime.getValue());
|
||||
slots.add(new TimeSlot(time, isBooked, isSelected));
|
||||
}
|
||||
|
||||
public void confirmAppointment(String type, String reason) {
|
||||
String date = selectedDate.getValue();
|
||||
String time = selectedTime.getValue();
|
||||
|
||||
if (auth.getCurrentUser() == null)
|
||||
return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
if (date != null && time != null) {
|
||||
saveError.setValue(null); // Resetar erro antes de validar
|
||||
|
||||
// Validar no servidor se o horário já está ocupado por este médico
|
||||
db.collection("consultas")
|
||||
.whereEqualTo("type", type)
|
||||
.whereEqualTo("date", date)
|
||||
.whereEqualTo("time", time)
|
||||
.get()
|
||||
.addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
if (!queryDocumentSnapshots.isEmpty()) {
|
||||
// Já existe uma consulta!
|
||||
saveError.postValue("Este horário já foi marcado por outro paciente. Por favor, escolha outro.");
|
||||
} else {
|
||||
// O horário está livre, prosseguir com a marcação
|
||||
Appointment appointment = new Appointment(type, date, time, reason, false, userId, "Pendente");
|
||||
|
||||
db.collection("consultas")
|
||||
.add(appointment)
|
||||
.addOnSuccessListener(documentReference -> {
|
||||
try {
|
||||
String[] dateParts = date.split("/");
|
||||
int day = Integer.parseInt(dateParts[0]);
|
||||
int month = Integer.parseInt(dateParts[1]) - 1; // 0-based
|
||||
int year = Integer.parseInt(dateParts[2]);
|
||||
|
||||
String[] timeParts = time.split(":");
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
|
||||
Calendar baseCal = Calendar.getInstance();
|
||||
baseCal.set(year, month, day, hour, minute, 0);
|
||||
|
||||
// Schedule 24 hours before
|
||||
Calendar cal24h = (Calendar) baseCal.clone();
|
||||
cal24h.add(Calendar.DAY_OF_YEAR, -1);
|
||||
if (cal24h.getTimeInMillis() > System.currentTimeMillis()) {
|
||||
AlarmScheduler.scheduleAlarm(getApplication(), cal24h.getTimeInMillis(),
|
||||
"Lembrete de Consulta", "A sua consulta é amanhã às " + time,
|
||||
(date + time + "24h").hashCode());
|
||||
}
|
||||
|
||||
// Schedule 30 minutes before
|
||||
Calendar cal30m = (Calendar) baseCal.clone();
|
||||
cal30m.add(Calendar.MINUTE, -30);
|
||||
if (cal30m.getTimeInMillis() > System.currentTimeMillis()) {
|
||||
AlarmScheduler.scheduleAlarm(getApplication(), cal30m.getTimeInMillis(),
|
||||
"Lembrete de Consulta", "A sua consulta é daqui a 30 minutos (" + time + ")",
|
||||
(date + time + "30m").hashCode());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
saveSuccess.postValue(true);
|
||||
})
|
||||
.addOnFailureListener(e -> Log.e("ScheduleViewModel", "Failed to confirm appt", e));
|
||||
}
|
||||
}).addOnFailureListener(e -> {
|
||||
saveError.postValue("Erro ao verificar a disponibilidade do horário. Tente novamente.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
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 android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.example.cuida.databinding.FragmentSns24Binding;
|
||||
import com.example.cuida.services.Gemini;
|
||||
|
||||
public class Sns24Fragment extends Fragment {
|
||||
|
||||
private FragmentSns24Binding binding;
|
||||
private Gemini gemini;
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
|
||||
binding = FragmentSns24Binding.inflate(inflater, container, false);
|
||||
View root = binding.getRoot();
|
||||
|
||||
gemini = new Gemini();
|
||||
|
||||
// 1. Botão de Chamada SNS 24
|
||||
binding.buttonCallSns.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||
intent.setData(Uri.parse("tel:808242424"));
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
// 2. Esconder o botão de hospital inicialmente
|
||||
binding.buttonFindHospital.setVisibility(View.GONE);
|
||||
|
||||
// 3. Botão Triagem IA
|
||||
binding.buttonAiTriage.setOnClickListener(v -> {
|
||||
String symptoms = binding.inputSymptoms.getText().toString().trim();
|
||||
if (!symptoms.isEmpty()) {
|
||||
hideKeyboard();
|
||||
analyzeSymptomsWithGemini(symptoms);
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Por favor, descreva os seus sintomas.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private void hideKeyboard() {
|
||||
View view = getActivity() != null ? getActivity().getCurrentFocus() : null;
|
||||
if (view != null) {
|
||||
android.view.inputmethod.InputMethodManager imm = (android.view.inputmethod.InputMethodManager)
|
||||
getActivity().getSystemService(android.content.Context.INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void analyzeSymptomsWithGemini(String symptoms) {
|
||||
binding.buttonAiTriage.setEnabled(false);
|
||||
binding.textAiResult.setVisibility(View.VISIBLE);
|
||||
binding.textAiResult.setText("A analisar sintomas...");
|
||||
binding.buttonFindHospital.setVisibility(View.GONE);
|
||||
|
||||
String prompt = "Atua como triagem médica de urgência (estilo SNS 24). " +
|
||||
"Sê extremamente direto, objetivo e conciso. Não uses introduções ou saudações. " +
|
||||
"Responde apenas com: 1) Causa provável, 2) Ação imediata recomendada. " +
|
||||
"Se os sintomas indicarem perigo de vida ou necessidade de observação urgente, OBRIGATORIAMENTE começa a tua primeira linha com a palavra [GRAVE]. " +
|
||||
"Sintomas do paciente: " + symptoms;
|
||||
|
||||
gemini.fazerPergunta(prompt, new Gemini.GeminiCallback() {
|
||||
@Override
|
||||
public void onSuccess(String result) {
|
||||
if (getActivity() != null && binding != null) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
String displayResult = result.replace("[GRAVE]", "").trim();
|
||||
binding.textAiResult.setText(displayResult);
|
||||
binding.buttonAiTriage.setEnabled(true);
|
||||
|
||||
if (result.contains("[GRAVE]")) {
|
||||
binding.buttonFindHospital.setVisibility(View.VISIBLE);
|
||||
binding.buttonFindHospital.setOnClickListener(v -> {
|
||||
Uri gmmIntentUri = Uri.parse("geo:0,0?q=hospital+mais+proximo");
|
||||
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
|
||||
mapIntent.setPackage("com.google.android.apps.maps");
|
||||
startActivity(mapIntent);
|
||||
});
|
||||
}
|
||||
saveTriageToHistory(symptoms, displayResult);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
if (getActivity() != null && binding != null) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
binding.textAiResult.setText("Erro na ligação: " + t.getMessage());
|
||||
binding.buttonAiTriage.setEnabled(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveTriageToHistory(String symptoms, String result) {
|
||||
if (getActivity() == null) return;
|
||||
com.google.firebase.auth.FirebaseUser user = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser();
|
||||
if (user == null) return;
|
||||
|
||||
java.util.Map<String, Object> triage = new java.util.HashMap<>();
|
||||
triage.put("userId", user.getUid());
|
||||
triage.put("sintomas", symptoms);
|
||||
triage.put("resultado", result);
|
||||
triage.put("data", new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm", java.util.Locale.getDefault()).format(new java.util.Date()));
|
||||
triage.put("timestamp", com.google.firebase.firestore.FieldValue.serverTimestamp());
|
||||
|
||||
com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||
.collection("triagens").add(triage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.example.cuida.utils;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import com.example.cuida.services.AlarmReceiver;
|
||||
|
||||
public class AlarmScheduler {
|
||||
public static void scheduleAlarm(Context context, long timeInMillis, String title, String message,
|
||||
int requestCode) {
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
if (alarmManager == null)
|
||||
return;
|
||||
|
||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||
intent.putExtra("EXTRA_TITLE", title);
|
||||
intent.putExtra("EXTRA_MESSAGE", message);
|
||||
intent.putExtra("EXTRA_NOTIFICATION_ID", requestCode);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
requestCode,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (alarmManager.canScheduleExactAlarms()) {
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
|
||||
} else {
|
||||
// Fallback to inexact alarm if exact permission is revoked
|
||||
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
|
||||
} else {
|
||||
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
// Android 14+ requires explicit consent for SCHEDULE_EXACT_ALARM except for
|
||||
// clocks/calendars
|
||||
// Fallback when security exception is raised
|
||||
alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelAlarm(Context context, int requestCode) {
|
||||
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
if (alarmManager == null)
|
||||
return;
|
||||
|
||||
Intent intent = new Intent(context, AlarmReceiver.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
requestCode,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
alarmManager.cancel(pendingIntent);
|
||||
pendingIntent.cancel();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.example.cuida.utils;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import com.example.cuida.MainActivity;
|
||||
import com.example.cuida.R;
|
||||
|
||||
public class NotificationHelper {
|
||||
|
||||
private final Context context;
|
||||
public static final String MEDICATION_CHANNEL_ID = "MEDICATION_CHANNEL_ID";
|
||||
public static final String APPOINTMENT_CHANNEL_ID = "APPOINTMENT_CHANNEL_ID";
|
||||
|
||||
public NotificationHelper(Context context) {
|
||||
this.context = context;
|
||||
createChannels();
|
||||
}
|
||||
|
||||
private void createChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManager manager = context.getSystemService(NotificationManager.class);
|
||||
if (manager != null) {
|
||||
// Medication Channel
|
||||
NotificationChannel medChannel = new NotificationChannel(
|
||||
MEDICATION_CHANNEL_ID,
|
||||
"Lembretes de Medicação",
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
medChannel.setDescription("Notificações para tomar a medicação a horas");
|
||||
medChannel.enableLights(true);
|
||||
medChannel.setLightColor(Color.BLUE);
|
||||
manager.createNotificationChannel(medChannel);
|
||||
|
||||
// Appointment Channel
|
||||
NotificationChannel apptChannel = new NotificationChannel(
|
||||
APPOINTMENT_CHANNEL_ID,
|
||||
"Lembretes de Consultas",
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
apptChannel.setDescription("Notificações antes das consultas");
|
||||
apptChannel.enableLights(true);
|
||||
apptChannel.setLightColor(Color.GREEN);
|
||||
manager.createNotificationChannel(apptChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendNotification(String title, String message, int notificationId, String channelId) {
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
|
||||
.setSmallIcon(R.drawable.ic_launcher_final) // Using app icon
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent);
|
||||
|
||||
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (manager != null) {
|
||||
manager.notify(notificationId, builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user