ja meti o droplist nos medicamentos
This commit is contained in:
@@ -45,6 +45,7 @@ dependencies {
|
||||
implementation 'androidx.navigation:navigation-ui:2.7.7'
|
||||
|
||||
// Adiciona a biblioteca para Auth se for do Google ID (credentials)
|
||||
implementation 'androidx.biometric:biometric:1.1.0'
|
||||
implementation 'androidx.credentials:credentials:1.5.0'
|
||||
implementation 'androidx.credentials:credentials-play-services-auth:1.5.0'
|
||||
//noinspection UseIdentifyId
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
|
||||
|
||||
<application
|
||||
android:name=".CuidaApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
@@ -58,6 +59,16 @@
|
||||
|
||||
<receiver android:name=".services.AlarmReceiver" android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -13,8 +13,10 @@ 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 {
|
||||
|
||||
@@ -12,6 +12,10 @@ 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
|
||||
@@ -56,6 +60,69 @@ public class LoginActivity extends AppCompatActivity {
|
||||
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.biometric_button.setVisibility(android.view.View.VISIBLE);
|
||||
binding.biometric_button.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()) {
|
||||
startActivity(new Intent(LoginActivity.this, MainActivity.class));
|
||||
finish();
|
||||
} 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() {
|
||||
@@ -108,6 +175,12 @@ public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
prefs.edit().putBoolean("is_logged_in", true).apply();
|
||||
prefs.edit().putBoolean("remember_me", rememberMe).apply();
|
||||
|
||||
// Save for biometrics if remember me is on
|
||||
if (rememberMe) {
|
||||
prefs.edit().putString("saved_email", email).apply();
|
||||
prefs.edit().putString("saved_pass", password).apply();
|
||||
}
|
||||
|
||||
if (fetchTask.isSuccessful() && fetchTask.getResult() != null
|
||||
&& fetchTask.getResult().exists()) {
|
||||
|
||||
@@ -31,6 +31,10 @@ 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 {
|
||||
|
||||
@@ -43,7 +47,8 @@ public class MedicationDialog extends DialogFragment {
|
||||
private EditText editNotes;
|
||||
private android.widget.RadioButton radioOral, radioTopical, radioInhalatory;
|
||||
private android.widget.RadioGroup radioGroupRoute;
|
||||
private TextView textTime;
|
||||
private ChipGroup chipGroupTimes;
|
||||
private List<String> selectedTimes = new ArrayList<>();
|
||||
private Medication medicationToEdit;
|
||||
private OnMedicationSaveListener listener;
|
||||
private OnMedicationDeleteListener deleteListener;
|
||||
@@ -78,7 +83,8 @@ public class MedicationDialog extends DialogFragment {
|
||||
editName = view.findViewById(R.id.edit_med_name);
|
||||
recyclerResults = view.findViewById(R.id.recycler_search_results);
|
||||
editNotes = view.findViewById(R.id.edit_med_notes);
|
||||
textTime = view.findViewById(R.id.text_med_time);
|
||||
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);
|
||||
@@ -124,12 +130,18 @@ public class MedicationDialog extends DialogFragment {
|
||||
radioInhalatory = view.findViewById(R.id.radio_inhalatory);
|
||||
|
||||
// Set up TimePicker
|
||||
textTime.setOnClickListener(v -> showTimePicker());
|
||||
btnAddTime.setOnClickListener(v -> showTimePicker());
|
||||
|
||||
if (medicationToEdit != null) {
|
||||
editName.setText(medicationToEdit.name);
|
||||
editNotes.setText(medicationToEdit.notes);
|
||||
textTime.setText(medicationToEdit.time);
|
||||
if (medicationToEdit.time != null && !medicationToEdit.time.isEmpty()) {
|
||||
String[] times = medicationToEdit.time.split(",\\s*");
|
||||
for (String t : times) {
|
||||
if (!t.isEmpty()) selectedTimes.add(t);
|
||||
}
|
||||
refreshTimeChips();
|
||||
}
|
||||
|
||||
String dosage = medicationToEdit.dosage;
|
||||
if (dosage != null) {
|
||||
@@ -146,14 +158,23 @@ public class MedicationDialog extends DialogFragment {
|
||||
builder.setTitle("Adicionar Medicamento");
|
||||
// Default time to current time
|
||||
Calendar cal = Calendar.getInstance();
|
||||
updateTimeLabel(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
|
||||
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();
|
||||
String time = textTime.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";
|
||||
@@ -227,25 +248,32 @@ public class MedicationDialog extends DialogFragment {
|
||||
int hour = cal.get(Calendar.HOUR_OF_DAY);
|
||||
int minute = cal.get(Calendar.MINUTE);
|
||||
|
||||
if (medicationToEdit != null) {
|
||||
try {
|
||||
String[] parts = medicationToEdit.time.split(":");
|
||||
hour = Integer.parseInt(parts[0]);
|
||||
minute = Integer.parseInt(parts[1]);
|
||||
} catch (Exception e) {
|
||||
// Use current time if parsing fails
|
||||
}
|
||||
}
|
||||
|
||||
TimePickerDialog timePickerDialog = new TimePickerDialog(getContext(),
|
||||
(view, hourOfDay, minute1) -> updateTimeLabel(hourOfDay, minute1),
|
||||
(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 updateTimeLabel(int hourOfDay, int minute) {
|
||||
String time = String.format(Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||
textTime.setText(time);
|
||||
private void refreshTimeChips() {
|
||||
if (chipGroupTimes == null) return;
|
||||
chipGroupTimes.removeAllViews();
|
||||
for (String time : selectedTimes) {
|
||||
Chip chip = new Chip(requireContext());
|
||||
chip.setText(time);
|
||||
chip.setCloseIconVisible(true);
|
||||
chip.setOnCloseIconClickListener(v -> {
|
||||
selectedTimes.remove(time);
|
||||
refreshTimeChips();
|
||||
});
|
||||
chipGroupTimes.addView(chip);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchAllMedsOnce() {
|
||||
|
||||
@@ -58,36 +58,55 @@ public class MedicationFragment extends Fragment {
|
||||
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);
|
||||
}
|
||||
|
||||
try {
|
||||
String[] timeParts = medicationToSave.time.split(":");
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
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);
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
String title = "Hora do Medicamento";
|
||||
String msg = "É hora de tomar: " + medicationToSave.name + " (" + medicationToSave.dosage + ")";
|
||||
com.example.cuida.utils.AlarmScheduler.scheduleAlarm(
|
||||
requireContext(),
|
||||
calendar.getTimeInMillis(),
|
||||
title,
|
||||
msg,
|
||||
medicationToSave.name.hashCode());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
@@ -96,13 +115,18 @@ public class MedicationFragment extends Fragment {
|
||||
dialog.setDeleteListener(medicationToDelete -> {
|
||||
medicationViewModel.delete(medicationToDelete);
|
||||
|
||||
// Cancel alarm if configured
|
||||
try {
|
||||
com.example.cuida.utils.AlarmScheduler.cancelAlarm(
|
||||
requireContext(),
|
||||
medicationToDelete.name.hashCode());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,18 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.pdf.PdfDocument;
|
||||
import androidx.core.content.FileProvider;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.example.cuida.data.model.Medication;
|
||||
import com.example.cuida.data.model.Appointment;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -47,6 +59,8 @@ public class ProfileFragment extends Fragment {
|
||||
getActivity().finish();
|
||||
});
|
||||
|
||||
binding.buttonExportReport.setOnClickListener(v -> exportMonthlyReport());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@@ -285,9 +299,119 @@ public class ProfileFragment extends Fragment {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
private void exportMonthlyReport() {
|
||||
if (currentUser == null || auth.getCurrentUser() == null) return;
|
||||
String userId = auth.getCurrentUser().getUid();
|
||||
|
||||
Toast.makeText(getContext(), "A gerar relatório...", Toast.LENGTH_SHORT).show();
|
||||
|
||||
db.collection("medicamentos").whereEqualTo("userId", userId).get()
|
||||
.addOnSuccessListener(medSnapshots -> {
|
||||
List<Medication> meds = new ArrayList<>();
|
||||
for (com.google.firebase.firestore.DocumentSnapshot doc : medSnapshots) {
|
||||
meds.add(doc.toObject(Medication.class));
|
||||
}
|
||||
|
||||
db.collection("consultas").whereEqualTo("userId", userId).get()
|
||||
.addOnSuccessListener(apptSnapshots -> {
|
||||
List<Appointment> appts = new ArrayList<>();
|
||||
for (com.google.firebase.firestore.DocumentSnapshot doc : apptSnapshots) {
|
||||
appts.add(doc.toObject(Appointment.class));
|
||||
}
|
||||
|
||||
generateAndSharePDF(meds, appts);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void generateAndSharePDF(List<Medication> meds, List<Appointment> appts) {
|
||||
PdfDocument document = new PdfDocument();
|
||||
PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(595, 842, 1).create(); // A4 size
|
||||
PdfDocument.Page page = document.startPage(pageInfo);
|
||||
Canvas canvas = page.getCanvas();
|
||||
Paint paint = new Paint();
|
||||
|
||||
int y = 50;
|
||||
paint.setTextSize(24);
|
||||
paint.setFakeBoldText(true);
|
||||
canvas.drawText("Relatório Mensal de Saúde - Cuida+", 50, y, paint);
|
||||
|
||||
y += 40;
|
||||
paint.setTextSize(14);
|
||||
paint.setFakeBoldText(false);
|
||||
canvas.drawText("Utilizador: " + currentUser.name, 50, y, paint);
|
||||
y += 20;
|
||||
canvas.drawText("Data: " + java.text.DateFormat.getDateTimeInstance().format(new java.util.Date()), 50, y, paint);
|
||||
|
||||
y += 40;
|
||||
paint.setFakeBoldText(true);
|
||||
paint.setTextSize(18);
|
||||
canvas.drawText("Medicação Atual:", 50, y, paint);
|
||||
y += 25;
|
||||
paint.setFakeBoldText(false);
|
||||
paint.setTextSize(12);
|
||||
|
||||
if (meds.isEmpty()) {
|
||||
canvas.drawText("Nenhuma medicação registada.", 70, y, paint);
|
||||
y += 20;
|
||||
} else {
|
||||
for (Medication m : meds) {
|
||||
canvas.drawText("- " + m.name + " (" + m.dosage + ") - Horários: " + m.time, 70, y, paint);
|
||||
y += 20;
|
||||
if (m.notes != null && !m.notes.isEmpty()) {
|
||||
paint.setColor(Color.GRAY);
|
||||
canvas.drawText(" Notas: " + m.notes, 70, y, paint);
|
||||
paint.setColor(Color.BLACK);
|
||||
y += 20;
|
||||
}
|
||||
if (y > 750) break; // Simple page break check
|
||||
}
|
||||
}
|
||||
|
||||
y += 20;
|
||||
paint.setFakeBoldText(true);
|
||||
paint.setTextSize(18);
|
||||
canvas.drawText("Consultas Agendadas:", 50, y, paint);
|
||||
y += 25;
|
||||
paint.setFakeBoldText(false);
|
||||
paint.setTextSize(12);
|
||||
|
||||
if (appts.isEmpty()) {
|
||||
canvas.drawText("Nenhuma consulta registada.", 70, y, paint);
|
||||
} else {
|
||||
for (Appointment a : appts) {
|
||||
canvas.drawText("- " + a.type + " em " + a.date + " às " + a.time, 70, y, paint);
|
||||
y += 20;
|
||||
canvas.drawText(" Motivo: " + a.reason, 70, y, paint);
|
||||
y += 25;
|
||||
if (y > 800) break;
|
||||
}
|
||||
}
|
||||
|
||||
document.finishPage(page);
|
||||
|
||||
File file = new File(requireContext().getCacheDir(), "Relatorio_Saude_Cuida.pdf");
|
||||
try {
|
||||
document.writeTo(new FileOutputStream(file));
|
||||
document.close();
|
||||
shareFile(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getContext(), "Erro ao gerar PDF", Toast.LENGTH_SHORT).show();
|
||||
document.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void shareFile(File file) {
|
||||
android.net.Uri uri = FileProvider.getUriForFile(requireContext(),
|
||||
requireContext().getPackageName() + ".fileprovider", file);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("application/pdf");
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, "Relatório de Saúde Cuida+");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
startActivity(Intent.createChooser(intent, "Partilhar Relatório"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,20 +199,25 @@ public class ScheduleViewModel extends AndroidViewModel {
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(year, month, day, hour, minute, 0);
|
||||
// 1 hour before
|
||||
calendar.add(Calendar.HOUR_OF_DAY, -1);
|
||||
Calendar baseCal = Calendar.getInstance();
|
||||
baseCal.set(year, month, day, hour, minute, 0);
|
||||
|
||||
if (calendar.getTimeInMillis() > System.currentTimeMillis()) {
|
||||
String title = "Lembrete de Consulta";
|
||||
String msg = "A sua consulta é daqui a 1 hora (" + time + ").";
|
||||
AlarmScheduler.scheduleAlarm(
|
||||
getApplication(),
|
||||
calendar.getTimeInMillis(),
|
||||
title,
|
||||
msg,
|
||||
(date + time).hashCode());
|
||||
// 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();
|
||||
|
||||
@@ -13,6 +13,10 @@ import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.example.cuida.databinding.FragmentSns24Binding;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Sns24Fragment extends Fragment {
|
||||
|
||||
@@ -85,6 +89,9 @@ public class Sns24Fragment extends Fragment {
|
||||
startActivity(mapIntent);
|
||||
});
|
||||
}
|
||||
|
||||
// Guardar no Histórico do Firestore
|
||||
saveTriageToHistory(symptoms, displayResult);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -101,6 +108,26 @@ public class Sns24Fragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
private void saveTriageToHistory(String symptoms, String result) {
|
||||
FirebaseAuth auth = FirebaseAuth.getInstance();
|
||||
if (auth.getCurrentUser() == null) return;
|
||||
|
||||
Map<String, Object> triage = new HashMap<>();
|
||||
triage.put("userId", auth.getCurrentUser().getUid());
|
||||
triage.put("symptoms", symptoms);
|
||||
triage.put("result", result);
|
||||
triage.put("timestamp", com.google.firebase.Timestamp.now());
|
||||
|
||||
FirebaseFirestore.getInstance().collection("triagens")
|
||||
.add(triage)
|
||||
.addOnSuccessListener(documentReference -> {
|
||||
// Histórico guardado com sucesso
|
||||
})
|
||||
.addOnFailureListener(e -> {
|
||||
// Falha ao guardar histórico
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -86,7 +86,18 @@
|
||||
android:text="@string/login_button"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/biometric_button"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Login com Biometria"
|
||||
android:icon="@android:drawable/ic_dialog_info"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/forgot_password_link"
|
||||
|
||||
@@ -29,36 +29,38 @@
|
||||
android:inputType="textCapWords" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:text="Horário"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<!-- O resto do conteúdo do diálogo continua aqui em baixo -->
|
||||
</LinearLayout>
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:text="Horários"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_search_results"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:visibility="gone"
|
||||
android:elevation="8dp"
|
||||
android:background="@drawable/bg_search_results" />
|
||||
</FrameLayout>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_add_time"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Adicionar"
|
||||
android:padding="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chip_group_times"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:padding="4dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_med_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="08:00"
|
||||
android:textSize="18sp"
|
||||
android:padding="12dp"
|
||||
android:background="#E0E0E0"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="Via de Administração"
|
||||
|
||||
@@ -115,6 +115,17 @@
|
||||
android:layout_marginBottom="16dp"
|
||||
android:backgroundTint="@color/secondary_color"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_export_report"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:text="Exportar Relatório Mensal"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:strokeColor="@color/secondary_color"
|
||||
android:textColor="@color/secondary_color"/>
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_logout"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
|
||||
5
app/src/main/res/xml/file_paths.xml
Normal file
5
app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="my_cache" path="." />
|
||||
<external-path name="my_external" path="." />
|
||||
</paths>
|
||||
Reference in New Issue
Block a user