a ia esta a dar eero

This commit is contained in:
2026-04-21 17:02:35 +01:00
parent a032c6ca08
commit 97cb615f69
632 changed files with 18868 additions and 49861 deletions

View File

@@ -1,21 +1,27 @@
package com.example.cuida.data.model;
import com.google.firebase.firestore.DocumentId;
import com.google.firebase.firestore.PropertyName;
public class Medication {
private String id;
@DocumentId
public String id;
@PropertyName("nome")
public String name;
public String time; // HH:mm
public String dosage; // e.g. "1 compprimido"
@PropertyName("hora")
public String time;
@PropertyName("dosagem")
public String dosage;
@PropertyName("notas")
public String notes;
public boolean isTaken;
public String userId;
// Required empty constructor for Firestore deserialization
public Medication() {
// Obrigatório para o Firestore
}
public Medication(String name, String time, String dosage, String notes, String userId) {
@@ -26,4 +32,45 @@ public class Medication {
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; }
}

View File

@@ -5,15 +5,30 @@ 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() {
}
@@ -25,4 +40,25 @@ public class User {
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; }
}

View File

@@ -1,29 +1,33 @@
package com.example.cuida.services;
// Imports básicos do SDK do Google AI
import com.google.ai.client.generativeai.GenerativeModel;
import com.google.ai.client.generativeai.java.GenerativeModelFutures;
// Imports de tipos e configurações
import com.google.ai.client.generativeai.type.BlockThreshold;
import com.google.ai.client.generativeai.type.Content;
import com.google.ai.client.generativeai.type.GenerateContentResponse;
import com.google.ai.client.generativeai.type.GenerationConfig;
import com.google.ai.client.generativeai.type.HarmCategory;
import com.google.ai.client.generativeai.type.SafetySetting;
// Imports do Guava para processamento assíncrono
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import com.google.ai.client.generativeai.type.BlockThreshold;
import com.google.ai.client.generativeai.type.HarmCategory;
import com.google.ai.client.generativeai.type.SafetySetting;
// Imports standard de Java
import java.util.Arrays;
import java.util.List;
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class Gemini {
private final GenerativeModelFutures modelo;
public Gemini() {
// 1. Configurar os SafetySettings para permitir termos médicos e partes do corpo
// 1. Configurar Segurança
List<SafetySetting> safetySettings = Arrays.asList(
new SafetySetting(HarmCategory.HARASSMENT, BlockThreshold.NONE),
new SafetySetting(HarmCategory.HATE_SPEECH, BlockThreshold.NONE),
@@ -31,39 +35,43 @@ public class Gemini {
new SafetySetting(HarmCategory.DANGEROUS_CONTENT, BlockThreshold.NONE)
);
// 2. Configurar o modelo (usa a tua API Key do Google AI Studio)
// 2. Configuração de Geração
GenerationConfig config = new GenerationConfig.Builder()
.setTemperature(0.7f)
.build();
// 3. Inicialização do Modelo (Gemini 1.5 Flash)
GenerativeModel generativeModel = new GenerativeModel(
"gemini-2.5-flash",
"gemini-1.5-flash",
"AIzaSyBmLgn-SHaTDvAeDWsw2iTZRR9gahhOu7k",
null, // generationConfig
config,
safetySettings
);
this.modelo = GenerativeModelFutures.from(generativeModel);
}
public interface GeminiCallback {
void onSuccess(String result);
void onError(Throwable t);
}
public void fazerPergunta(String promptUtilizador, GeminiCallback callback) {
// 2. Preparar o conteúdo da pergunta
Content conteudo = new Content.Builder()
.addText(promptUtilizador)
.build();
// 3. Chamar a IA de forma assíncrona
ListenableFuture<GenerateContentResponse> respostaFuture = modelo.generateContent(conteudo);
Executor executor = Executors.newSingleThreadExecutor();
Futures.addCallback(respostaFuture, new FutureCallback<GenerateContentResponse>() {
@Override
public void onSuccess(GenerateContentResponse resultado) {
// Aqui recebes o texto da IA
String textoResposta = resultado.getText();
callback.onSuccess(textoResposta);
if (resultado != null && resultado.getText() != null) {
callback.onSuccess(resultado.getText());
} else {
callback.onError(new Exception("IA não devolveu texto."));
}
}
@Override

View File

@@ -59,56 +59,60 @@ public class RegisterActivity extends AppCompatActivity {
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) {
String userId = firebaseUser.getUid();
java.util.Map<String, Object> userMap = new java.util.HashMap<>();
userMap.put("id", userId);
userMap.put("name", name);
userMap.put("email", email);
userMap.put("age", age);
userMap.put("utenteNumber", utenteStr);
userMap.put("sexo", gender);
userMap.put("profilePictureUri", ""); // Init empty
db.collection("utilizadores").document(userId)
.set(userMap)
.addOnSuccessListener(aVoid -> {
Toast.makeText(RegisterActivity.this, "Conta criada 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 salvar dados: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
});
saveUserData(firebaseUser.getUid(), name, email, ageStr, utenteStr, gender);
}
} else {
binding.registerButton.setEnabled(true);
binding.registerButton.setText("Registar");
String errorMsg = task.getException() != null ? task.getException().getMessage()
: "Erro desconhecido";
if (errorMsg != null) {
if (errorMsg.contains("email address is already in use")) {
errorMsg = "Este email já está registado!";
} else if (errorMsg.contains("email address is badly formatted")) {
errorMsg = "O formato do email é inválido!";
} else if (errorMsg.contains("Password should be at least")) {
errorMsg = "A palavra-passe deve ter pelo menos 6 caracteres.";
}
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();
}
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();
});
}
}

View File

@@ -34,7 +34,10 @@ public class HomeFragment extends Fragment {
.get()
.addOnSuccessListener(documentSnapshot -> {
if (documentSnapshot.exists() && isAdded()) {
String name = documentSnapshot.getString("name");
// 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];

View File

@@ -74,12 +74,24 @@ public class ProfileFragment extends Fragment {
if (documentSnapshot.exists()) {
currentUser = new User();
currentUser.id = documentSnapshot.getId();
currentUser.name = documentSnapshot.getString("name");
// Campos Padronizados
String nome = documentSnapshot.getString("nome_completo");
if (nome == null) nome = documentSnapshot.getString("name");
currentUser.name = nome;
currentUser.email = documentSnapshot.getString("email");
currentUser.utenteNumber = documentSnapshot.getString("utenteNumber");
String utente = documentSnapshot.getString("numero_utente");
if (utente == null) utente = documentSnapshot.getString("utenteNumber");
currentUser.utenteNumber = utente;
currentUser.profilePictureUri = documentSnapshot.getString("profilePictureUri");
Object ageObj = documentSnapshot.get("age");
// 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) {
@@ -91,21 +103,20 @@ public class ProfileFragment extends Fragment {
} else {
currentUser.age = 0;
}
if (currentUser != null && isAdded()) {
binding.profileName.setText(currentUser.name);
binding.profileEmail.setText(currentUser.email);
binding.profileAge.setText(String.valueOf(currentUser.age));
binding.profileUtente
.setText(currentUser.utenteNumber != null ? currentUser.utenteNumber : "N/A");
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);
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 profile pic view: " + e.getMessage());
Log.e("ProfileFragment", "Error loading pic: " + e.getMessage());
}
}
}

View File

@@ -9,79 +9,80 @@ import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
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;
import com.example.cuida.services.Gemini;
public class Sns24Fragment extends Fragment {
private FragmentSns24Binding binding;
private Gemini gemini;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(com.example.cuida.R.layout.fragment_sns24, container, false);
binding = FragmentSns24Binding.bind(view);
return binding.getRoot();
}
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding = FragmentSns24Binding.inflate(inflater, container, false);
View root = binding.getRoot();
// 2. Botão Ligar SNS 24
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) {
// Feedback visual de carregamento
binding.buttonAiTriage.setEnabled(false);
binding.textAiResult.setVisibility(View.VISIBLE);
binding.textAiResult.setText("A contactar a Inteligência Artificial...");
binding.textAiResult.setText("A analisar sintomas...");
binding.buttonFindHospital.setVisibility(View.GONE);
com.example.cuida.services.Gemini gemini = new com.example.cuida.services.Gemini();
String prompt = "Atua como um assistente de triagem médica. " +
"Analisa os seguintes sintomas de um paciente e dá uma resposta MUITO CURTA e direta (máximo 2 a 3 frases). " +
"Tem bom senso e calma na avaliação: não assumas automaticamente que os sintomas são severos sem analisar todo o contexto. Evita ser alarmista desnecessariamente. " +
"Apenas se os sintomas indicarem uma situação de verdadeira emergência ou necessidade indiscutível de observação médica urgente, a tua resposta DEVE conter obrigatoriamente a palavra [GRAVE]. " +
"Recomenda sempre qual o próximo passo adequado (ex: repouso, farmácia, médico, SNS 24, ou urgências). " +
String prompt = "Atua como um assistente de triagem médica do SNS 24. " +
"Analisa os seguintes sintomas de forma curta e direta. " +
"Se os sintomas parecerem graves, começa a resposta com [GRAVE]. " +
"Sintomas: " + symptoms;
gemini.fazerPergunta(prompt, new com.example.cuida.services.Gemini.GeminiCallback() {
gemini.fazerPergunta(prompt, new Gemini.GeminiCallback() {
@Override
public void onSuccess(String result) {
if (getActivity() != null && binding != null) {
getActivity().runOnUiThread(() -> {
if (binding == null) return;
// Limpar a marcação da palavra [GRAVE] para não aparecer ao utilizador
String displayResult = result.replace("[GRAVE]", "").trim();
binding.textAiResult.setText(displayResult);
binding.buttonAiTriage.setEnabled(true);
String resultLower = result.toLowerCase();
if (result.contains("[GRAVE]") || resultLower.contains("grave") || resultLower.contains("urgência") || resultLower.contains("hospital")
|| resultLower.contains("112")) {
if (result.contains("[GRAVE]")) {
binding.buttonFindHospital.setVisibility(View.VISIBLE);
binding.buttonFindHospital.setOnClickListener(v -> {
Uri gmmIntentUri = Uri.parse("geo:0,0?q=hospital+mais+proximo");
@@ -90,8 +91,6 @@ public class Sns24Fragment extends Fragment {
startActivity(mapIntent);
});
}
// Guardar no Histórico do Firestore
saveTriageToHistory(symptoms, displayResult);
});
}
@@ -101,7 +100,7 @@ public class Sns24Fragment extends Fragment {
public void onError(Throwable t) {
if (getActivity() != null && binding != null) {
getActivity().runOnUiThread(() -> {
binding.textAiResult.setText("Falha na comunicação com a IA: " + t.getMessage());
binding.textAiResult.setText("Erro na ligação: " + t.getMessage());
binding.buttonAiTriage.setEnabled(true);
});
}
@@ -110,23 +109,19 @@ public class Sns24Fragment extends Fragment {
}
private void saveTriageToHistory(String symptoms, String result) {
FirebaseAuth auth = FirebaseAuth.getInstance();
if (auth.getCurrentUser() == null) return;
if (getActivity() == null) return;
com.google.firebase.auth.FirebaseUser user = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser();
if (user == 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());
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());
FirebaseFirestore.getInstance().collection("triagens")
.add(triage)
.addOnSuccessListener(documentReference -> {
// Histórico guardado com sucesso
})
.addOnFailureListener(e -> {
// Falha ao guardar histórico
});
com.google.firebase.firestore.FirebaseFirestore.getInstance()
.collection("triagens").add(triage);
}
@Override