From 05f30539ad5227d9d99591e393dfec1030870ad9 Mon Sep 17 00:00:00 2001
From: 230412 <230412@epvc.pt>
Date: Tue, 16 Jun 2026 17:14:00 +0100
Subject: [PATCH] =?UTF-8?q?corrigir=20os=20novos=20erros=20amnh=C3=A3=20e?=
=?UTF-8?q?=20adicionar=20qual=20tipo=20de=20sexo=20a=20pessoa=20=C3=A9=20?=
=?UTF-8?q?no=20register?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/src/main/AndroidManifest.xml | 4 +-
.../com/example/pap/DefinicoesActivity.java | 78 +++++-------
.../com/example/pap/EstatisticasActivity.java | 81 +++++++++++-
.../java/com/example/pap/FotoActivity.java | 120 +++++++++++++++---
.../com/example/pap/MudarEmailActivity.java | 88 +++++++++++++
.../example/pap/MudarPasswordActivity.java | 93 ++++++++++++++
.../java/com/example/pap/PerfilActivity.java | 87 +++++++++----
.../main/res/layout/activity_definicoes.xml | 78 +++++-------
app/src/main/res/layout/activity_foto.xml | 14 +-
app/src/main/res/layout/activity_home.xml | 4 +-
app/src/main/res/layout/activity_login.xml | 2 +-
.../main/res/layout/activity_mudar_email.xml | 90 +++++++++++++
.../res/layout/activity_mudar_password.xml | 90 +++++++++++++
app/src/main/res/layout/activity_perfil.xml | 54 ++++----
app/src/main/res/layout/activity_register.xml | 2 +-
15 files changed, 718 insertions(+), 167 deletions(-)
create mode 100644 app/src/main/java/com/example/pap/MudarEmailActivity.java
create mode 100644 app/src/main/java/com/example/pap/MudarPasswordActivity.java
create mode 100644 app/src/main/res/layout/activity_mudar_email.xml
create mode 100644 app/src/main/res/layout/activity_mudar_password.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 293fccd..bc2240d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -32,9 +32,9 @@
-
-
+
+
finish());
- // 1. CARREGAR E CONFIGURAR O MODO ESCURO
- SharedPreferences themePrefs = getSharedPreferences("Definicoes", MODE_PRIVATE);
- boolean isDark = themePrefs.getBoolean("dark_mode", false);
- switchModoEscuro.setChecked(isDark);
+ SharedPreferences dadosPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+ etNovoNome.setText(dadosPrefs.getString("nome", ""));
- switchModoEscuro.setOnCheckedChangeListener((buttonView, isChecked) -> {
- SharedPreferences.Editor editor = themePrefs.edit();
- editor.putBoolean("dark_mode", isChecked);
- editor.apply();
+ // Ir para Mudar Email
+ btnIrMudarEmail.setOnClickListener(v -> {
+ startActivity(new Intent(DefinicoesActivity.this, MudarEmailActivity.class));
+ });
- if (isChecked) {
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
- } else {
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
+ // Ir para Mudar Password
+ btnIrMudarPassword.setOnClickListener(v -> {
+ startActivity(new Intent(DefinicoesActivity.this, MudarPasswordActivity.class));
+ });
+
+ // Guardar Apenas o Nome (Porque Email e Password têm ecrãs próprios)
+ btnGuardarNome.setOnClickListener(v -> {
+ String novoNome = etNovoNome.getText().toString().trim();
+ if (!novoNome.isEmpty()) {
+ SharedPreferences.Editor editor = dadosPrefs.edit();
+ editor.putString("nome", novoNome);
+ editor.apply();
+ Toast.makeText(this, "Nome atualizado!", Toast.LENGTH_SHORT).show();
+ finish();
}
});
- // 2. CARREGAR O NOME E EMAIL ATUAIS PARA O CAMPO DE TEXTO
- SharedPreferences dadosPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
- etNovoNome.setText(dadosPrefs.getString("nome", ""));
- etNovoEmail.setText(dadosPrefs.getString("email", ""));
-
- // 3. GUARDAR AS ALTERAÇÕES
- btnGuardarAlteracoes.setOnClickListener(v -> {
- String novoNome = etNovoNome.getText().toString().trim();
- String novoEmail = etNovoEmail.getText().toString().trim();
- String novaPassword = etNovaPassword.getText().toString().trim();
-
+ // Terminar Sessão
+ btnSairConta.setOnClickListener(v -> {
SharedPreferences.Editor editor = dadosPrefs.edit();
-
- if (!novoNome.isEmpty()) {
- editor.putString("nome", novoNome);
- }
- if (!novoEmail.isEmpty()) {
- editor.putString("email", novoEmail);
- }
-
+ editor.clear();
editor.apply();
-
- Toast.makeText(this, "Alterações guardadas com sucesso!", Toast.LENGTH_SHORT).show();
- finish(); // Volta ao ecrã anterior
+ Toast.makeText(this, "Sessão terminada", Toast.LENGTH_SHORT).show();
+ Intent intent = new Intent(DefinicoesActivity.this, LoginActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ finish();
});
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/EstatisticasActivity.java b/app/src/main/java/com/example/pap/EstatisticasActivity.java
index d269cdf..f9eb020 100644
--- a/app/src/main/java/com/example/pap/EstatisticasActivity.java
+++ b/app/src/main/java/com/example/pap/EstatisticasActivity.java
@@ -7,8 +7,13 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
+import java.util.Collections;
import java.util.Locale;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
public class EstatisticasActivity extends AppCompatActivity {
private TextView tvValorIMC, tvStatusIMC, tvCaloriasMeta, tvDicaIA, tvCaloriasQueimadas;
@@ -16,6 +21,9 @@ public class EstatisticasActivity extends AppCompatActivity {
private TextView tvAguaStats;
private ProgressBar progressProt, progressHidr, progressGord;
+ // COLOCA A TUA API KEY AQUI
+ private final String MINHA_API_KEY = "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";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -41,11 +49,15 @@ public class EstatisticasActivity extends AppCompatActivity {
findViewById(R.id.btnVoltarStats).setOnClickListener(v -> finish());
+ // Carrega os painéis locais
calcularIMC();
calcularTMB();
carregarMacrosDaIA();
carregarAgua();
carregarCaloriasQueimadas();
+
+ // CHAMA A IA PARA AVALIAR O ESTADO GERAL DO UTILIZADOR
+ gerarInsightInteligente();
}
private void calcularIMC() {
@@ -80,10 +92,10 @@ public class EstatisticasActivity extends AppCompatActivity {
SharedPreferences prefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
float peso = prefs.getFloat("peso", 0);
float alturaMetros = prefs.getFloat("altura", 0);
- String sexo = prefs.getString("sexo", "");
+ String sexo = prefs.getString("sexo", "Masculino"); // Assumido Masculino como padrão
int idade = prefs.getInt("idade", 20);
- if (peso > 0 && alturaMetros > 0 && !sexo.isEmpty()) {
+ if (peso > 0 && alturaMetros > 0) {
float alturaCm = alturaMetros * 100;
double tmb;
@@ -108,10 +120,6 @@ public class EstatisticasActivity extends AppCompatActivity {
int gord = prefs.getInt("gord_hoje", 0);
String ultimaComida = prefs.getString("ultimo_prato", "Ainda não leste nada hoje.");
- // Puxa a dica extraída da foto!
- String dicaIa = prefs.getString("ultima_dica_ia", "Continua a registar as tuas refeições para ver dicas.");
- tvDicaIA.setText(dicaIa);
-
tvProtGramas.setText("Proteína: " + prot + "g");
tvHidrGramas.setText("Hidratos: " + hidr + "g");
tvGordGramas.setText("Gordura: " + gord + "g");
@@ -135,4 +143,65 @@ public class EstatisticasActivity extends AppCompatActivity {
int kcalQueimadas = prefs.getInt("calorias_desafios", 0);
tvCaloriasQueimadas.setText(kcalQueimadas + " kcal");
}
+
+ // ==========================================
+ // NOVA FUNÇÃO: O CÉREBRO DA IA NAS ESTATÍSTICAS
+ // ==========================================
+ private void gerarInsightInteligente() {
+ tvDicaIA.setText("A analisar os teus dados diários... ⏳");
+
+ // 1. Recolher a informação toda das gavetas (SharedPreferences)
+ SharedPreferences prefsDados = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+ SharedPreferences prefsSaude = getSharedPreferences("DadosSaude", MODE_PRIVATE);
+ SharedPreferences prefsGam = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
+
+ float peso = prefsDados.getFloat("peso", 0);
+ float altura = prefsDados.getFloat("altura", 0);
+ float imc = (peso > 0 && altura > 0) ? (peso / (altura * altura)) : 0;
+
+ int prot = prefsSaude.getInt("prot_hoje", 0);
+ int hidr = prefsSaude.getInt("hidr_hoje", 0);
+ int gord = prefsSaude.getInt("gord_hoje", 0);
+
+ float litrosAgua = prefsGam.getInt("agua_hoje", 0) * 0.25f;
+ int kcalQueimadas = prefsGam.getInt("calorias_desafios", 0);
+
+ // 2. Criar o Prompt a explicar o cenário à IA
+ String promptDaIA = "És o treinador e nutricionista pessoal do utilizador. " +
+ "Os dados dele hoje são: IMC = " + String.format(Locale.getDefault(), "%.1f", imc) + ", " +
+ "Água bebida = " + litrosAgua + " Litros, Calorias Queimadas = " + kcalQueimadas + " kcal. " +
+ "Macros consumidos: Proteína=" + prot + "g, Hidratos=" + hidr + "g, Gordura=" + gord + "g. " +
+ "Avalia estes números de forma realista. Se ele bebeu pouca água, avisa-o. Se tem pouca proteína, alerta-o. " +
+ "Regra de Ouro: Escreve apenas 2 frases curtas, diretas e motivadoras. NÃO USES ASTERISCOS.";
+
+ // 3. Enviar o pedido usando a classe AiApi que já tens (enviamos só texto, sem imagem)
+ AiRequest request = new AiRequest(Collections.singletonList(
+ new Message("user", Collections.singletonList(new ContentPart("text", promptDaIA)))
+ ));
+
+ AiConfig.getRetrofit().create(AiApi.class)
+ .analisarImagem("Bearer " + MINHA_API_KEY, request) // Usamos o mesmo método mas sem enviar a foto
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful() && response.body() != null) {
+ try {
+ String resposta = response.body().choices.get(0).message.content;
+ // Limpa qualquer formatação extra que a IA tente mandar
+ String dicaLimpa = resposta.replace("**", "").replace("*", "");
+ tvDicaIA.setText(dicaLimpa);
+ } catch (Exception e) {
+ tvDicaIA.setText("Continua o bom trabalho! Mantém o foco na água e macros.");
+ }
+ } else {
+ tvDicaIA.setText("Servidor indisponível no momento. Foca-te em beber água!");
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ tvDicaIA.setText("Estás offline. Regista os dados localmente!");
+ }
+ });
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/FotoActivity.java b/app/src/main/java/com/example/pap/FotoActivity.java
index a09a32c..8ae6bd4 100644
--- a/app/src/main/java/com/example/pap/FotoActivity.java
+++ b/app/src/main/java/com/example/pap/FotoActivity.java
@@ -11,10 +11,15 @@ import android.provider.MediaStore;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
+import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
+import android.widget.Toast;
+
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.io.ByteArrayOutputStream;
@@ -28,12 +33,12 @@ import retrofit2.Response;
public class FotoActivity extends AppCompatActivity {
private ImageView ivFotoComida;
- private Button btnTirarFoto, btnGaleria, btnAnalisarIA, btnIrParaChat;
+ private Button btnTirarFoto, btnGaleria, btnAnalisarIA, btnIrParaChat, btnCorrigir;
private TextView tvResultadoIA;
private Bitmap imagemCapturada;
private String textoAnalise = "";
- // MANTÉM A TUA CHAVE (cuidado para não partilhar no futuro)
+ // MANTÉM A TUA CHAVE AQUI
private final String MINHA_API_KEY = "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";
@Override
@@ -46,6 +51,7 @@ public class FotoActivity extends AppCompatActivity {
btnGaleria = findViewById(R.id.btnGaleria);
btnAnalisarIA = findViewById(R.id.btnAnalisarIA);
btnIrParaChat = findViewById(R.id.btnIrParaChat);
+ btnCorrigir = findViewById(R.id.btnCorrigir); // Ligado ao XML
tvResultadoIA = findViewById(R.id.tvResultadoIA);
ActivityResultLauncher camLauncher = registerForActivityResult(
@@ -85,7 +91,8 @@ public class FotoActivity extends AppCompatActivity {
galLauncher.launch(intent);
});
- btnAnalisarIA.setOnClickListener(v -> enviarParaIA());
+ // Clique para analisar pela primeira vez
+ btnAnalisarIA.setOnClickListener(v -> enviarParaIA(null));
btnIrParaChat.setOnClickListener(v -> {
Intent intent = new Intent(FotoActivity.this, ChatActivity.class);
@@ -93,6 +100,9 @@ public class FotoActivity extends AppCompatActivity {
startActivity(intent);
});
+ // NOVO: Clique para corrigir erro
+ btnCorrigir.setOnClickListener(v -> mostrarPopupCorrecao());
+
findViewById(R.id.btnVoltarFoto).setOnClickListener(v -> finish());
}
@@ -103,26 +113,42 @@ public class FotoActivity extends AppCompatActivity {
ivFotoComida.setImageBitmap(imagemCapturada);
btnAnalisarIA.setVisibility(View.VISIBLE);
btnIrParaChat.setVisibility(View.GONE);
+ btnCorrigir.setVisibility(View.GONE); // Esconde a correção até analisar
tvResultadoIA.setText("Pronto para analisar.");
}
}
- private void enviarParaIA() {
+ // Função melhorada que aceita a comida certa se o utilizador corrigir
+ private void enviarParaIA(String comidaCerta) {
tvResultadoIA.setText("A processar... ⏳");
btnAnalisarIA.setEnabled(false);
btnIrParaChat.setVisibility(View.GONE);
+ btnCorrigir.setVisibility(View.GONE);
ByteArrayOutputStream os = new ByteArrayOutputStream();
imagemCapturada.compress(Bitmap.CompressFormat.JPEG, 50, os);
String base64 = Base64.encodeToString(os.toByteArray(), Base64.NO_WRAP);
- String ordemParaIA = "És um nutricionista prático. Identifica a comida e dá os valores de forma SUPER RESUMIDA. " +
- "REGRAS: 1. Português de Portugal. 2. SEM asteriscos. 3. Máximo 4 linhas. " +
- "Formato exato: \n" +
- "Prato: [Nome]\n" +
- "Calorias: [Valor] kcal\n" +
- "Macros: [X]g Proteína, [X]g Hidratos, [X]g Gordura\n" +
- "Dica: [Uma frase curta].";
+ String ordemParaIA;
+ if (comidaCerta == null) {
+ // Análise normal
+ ordemParaIA = "És um nutricionista prático. Identifica a comida e dá os valores de forma SUPER RESUMIDA. " +
+ "REGRAS: 1. Português de Portugal. 2. SEM asteriscos. 3. Máximo 4 linhas. " +
+ "Formato exato: \n" +
+ "Prato: [Nome]\n" +
+ "Calorias: [Valor] kcal\n" +
+ "Macros: [X]g Proteína, [X]g Hidratos, [X]g Gordura\n" +
+ "Dica: [Uma frase curta].";
+ } else {
+ // Análise forçada com a correção do utilizador
+ ordemParaIA = "Atenção: A tua análise anterior falhou. O prato na imagem é na verdade: '" + comidaCerta + "'. " +
+ "Esquece tudo o resto e foca-te em dar os valores reais APENAS para '" + comidaCerta + "'. " +
+ "Usa este formato exato: \n" +
+ "Prato: " + comidaCerta + "\n" +
+ "Calorias: [Valor] kcal\n" +
+ "Macros: [X]g Proteína, [X]g Hidratos, [X]g Gordura\n" +
+ "Dica: [Frase de saúde curta e sem asteriscos].";
+ }
AiRequest request = new AiRequest(Collections.singletonList(
new Message("user", java.util.Arrays.asList(
@@ -142,9 +168,17 @@ public class FotoActivity extends AppCompatActivity {
String resposta = response.body().choices.get(0).message.content;
textoAnalise = resposta.replace("**", "").replace("*", "");
tvResultadoIA.setText(textoAnalise);
- btnIrParaChat.setVisibility(View.VISIBLE);
- // A MAGIA ACONTECE AQUI!
+ // Mostra os botões
+ btnIrParaChat.setVisibility(View.VISIBLE);
+ btnCorrigir.setVisibility(View.VISIBLE);
+
+ // Se ele estiver a corrigir, apagamos o erro passado!
+ if (comidaCerta != null) {
+ desfazerUltimoErro();
+ }
+
+ // Guarda a nova resposta
extrairEGuardarDados(textoAnalise);
} catch (Exception e) { tvResultadoIA.setText("Erro na resposta."); }
@@ -158,7 +192,34 @@ public class FotoActivity extends AppCompatActivity {
});
}
- // Função que "lê" a resposta da IA e guarda os números E A DICA
+ // --- POPUP PARA CORREÇÃO MANUAL ---
+ private void mostrarPopupCorrecao() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Corrigir a IA 🤔");
+ builder.setMessage("A IA enganou-se? O que estava realmente no teu prato?");
+
+ final EditText input = new EditText(this);
+ input.setHint("Ex: Hambúrguer de Frango");
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setPadding(50, 20, 50, 0);
+ layout.addView(input);
+ builder.setView(layout);
+
+ builder.setPositiveButton("Re-Analisar", (dialog, which) -> {
+ String comidaCerta = input.getText().toString().trim();
+ if (!comidaCerta.isEmpty()) {
+ enviarParaIA(comidaCerta); // Manda o texto escrito pelo user
+ } else {
+ Toast.makeText(FotoActivity.this, "Tens de escrever a comida!", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ builder.setNegativeButton("Cancelar", null);
+ builder.show();
+ }
+
+ // --- FUNÇÃO PARA SALVAR OS DADOS E A DICA ---
private void extrairEGuardarDados(String texto) {
try {
int indexNomeStart = texto.indexOf("Prato: ") + 7;
@@ -170,7 +231,6 @@ public class FotoActivity extends AppCompatActivity {
int hidratos = extrairNumero(texto, "Proteína, ", "g Hidratos");
int gordura = extrairNumero(texto, "Hidratos, ", "g Gordura");
- // EXTRAIR A DICA DA IA
String dicaIA = "Continua a registar refeições para ver dicas.";
if (texto.contains("Dica: ")) {
int indexDica = texto.indexOf("Dica: ") + 6;
@@ -181,17 +241,39 @@ public class FotoActivity extends AppCompatActivity {
SharedPreferences.Editor editor = prefs.edit();
editor.putString("ultimo_prato", nomePrato);
- editor.putString("ultima_dica_ia", dicaIA); // Guarda a dica
+ editor.putString("ultima_dica_ia", dicaIA);
editor.putInt("cal_hoje", prefs.getInt("cal_hoje", 0) + calorias);
editor.putInt("prot_hoje", prefs.getInt("prot_hoje", 0) + proteina);
editor.putInt("hidr_hoje", prefs.getInt("hidr_hoje", 0) + hidratos);
editor.putInt("gord_hoje", prefs.getInt("gord_hoje", 0) + gordura);
+ // GUAAAAARDA O ERRO PARA PODERMOS APAGAR SE O GAJO CLICAR EM "CORRIGIR"
+ editor.putInt("ultimo_erro_cal", calorias);
+ editor.putInt("ultimo_erro_prot", proteina);
+ editor.putInt("ultimo_erro_hidr", hidratos);
+ editor.putInt("ultimo_erro_gord", gordura);
+
editor.apply();
- } catch (Exception e) {
- // Ignora se a IA responder noutro formato para não crashar
- }
+ } catch (Exception e) {}
+ }
+
+ // --- FUNÇÃO PARA REMOVER O ÚLTIMO PRATO QUE FOI MAL LIDO ---
+ private void desfazerUltimoErro() {
+ SharedPreferences prefs = getSharedPreferences("DadosSaude", MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+
+ int calAntiga = prefs.getInt("ultimo_erro_cal", 0);
+ int protAntiga = prefs.getInt("ultimo_erro_prot", 0);
+ int hidrAntiga = prefs.getInt("ultimo_erro_hidr", 0);
+ int gordAntiga = prefs.getInt("ultimo_erro_gord", 0);
+
+ editor.putInt("cal_hoje", prefs.getInt("cal_hoje", 0) - calAntiga);
+ editor.putInt("prot_hoje", prefs.getInt("prot_hoje", 0) - protAntiga);
+ editor.putInt("hidr_hoje", prefs.getInt("hidr_hoje", 0) - hidrAntiga);
+ editor.putInt("gord_hoje", prefs.getInt("gord_hoje", 0) - gordAntiga);
+
+ editor.apply();
}
private int extrairNumero(String texto, String inicio, String fim) {
diff --git a/app/src/main/java/com/example/pap/MudarEmailActivity.java b/app/src/main/java/com/example/pap/MudarEmailActivity.java
new file mode 100644
index 0000000..bffcfde
--- /dev/null
+++ b/app/src/main/java/com/example/pap/MudarEmailActivity.java
@@ -0,0 +1,88 @@
+package com.example.pap;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class MudarEmailActivity extends AppCompatActivity {
+
+ private EditText etAlterarEmail, etAlterarPassword1, etAlterarPassword2;
+ private Button btnConfirmarEmailNovo;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_mudar_email);
+
+ etAlterarEmail = findViewById(R.id.etAlterarEmail);
+ etAlterarPassword1 = findViewById(R.id.etAlterarPassword1);
+ etAlterarPassword2 = findViewById(R.id.etAlterarPassword2);
+ btnConfirmarEmailNovo = findViewById(R.id.btnConfirmarEmailNovo);
+
+ findViewById(R.id.btnVoltarMudarEmail).setOnClickListener(v -> finish());
+
+ SharedPreferences dadosPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+
+ btnConfirmarEmailNovo.setOnClickListener(v -> {
+ String novoEmail = etAlterarEmail.getText().toString().trim();
+ String pass1 = etAlterarPassword1.getText().toString().trim();
+ String pass2 = etAlterarPassword2.getText().toString().trim();
+
+ if (novoEmail.isEmpty() || pass1.isEmpty() || pass2.isEmpty()) {
+ Toast.makeText(this, "Preenche todos os campos!", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // 1. Validação: Verifica se os dois campos de password antiga coincidem
+ if (!pass1.equals(pass2)) {
+ Toast.makeText(this, "As confirmações de password não coincidem!", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // 2. Validação: Verifica se a password escrita é mesmo a senha correta da conta
+ String passwordReal = dadosPrefs.getString("password", "");
+ if (!passwordReal.isEmpty() && !pass1.equals(passwordReal)) {
+ Toast.makeText(this, "A password atual introduzida está errada!", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ String token = dadosPrefs.getString("access_token", "");
+ if (token.isEmpty()) {
+ Toast.makeText(this, "Erro: Faz login novamente.", Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ // Envia as alterações para o Supabase
+ Map updates = new HashMap<>();
+ updates.put("email", novoEmail);
+
+ SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
+ api.updateUserData(SupabaseConfig.SUPABASE_KEY, "Bearer " + token, updates).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ Toast.makeText(MudarEmailActivity.this, "Vai à caixa do novo email confirmar!", Toast.LENGTH_LONG).show();
+ finish();
+ } else {
+ Toast.makeText(MudarEmailActivity.this, "Erro ao mudar o email no Supabase.", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Toast.makeText(MudarEmailActivity.this, "Falha na internet.", Toast.LENGTH_SHORT).show();
+ }
+ });
+ });
+ }
+}
diff --git a/app/src/main/java/com/example/pap/MudarPasswordActivity.java b/app/src/main/java/com/example/pap/MudarPasswordActivity.java
new file mode 100644
index 0000000..1751e8f
--- /dev/null
+++ b/app/src/main/java/com/example/pap/MudarPasswordActivity.java
@@ -0,0 +1,93 @@
+package com.example.pap;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class MudarPasswordActivity extends AppCompatActivity {
+
+ private EditText etPasswordAntiga, etNovaPassword1, etNovaPassword2;
+ private Button btnConfirmarPassword;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_mudar_password);
+
+ etPasswordAntiga = findViewById(R.id.etPasswordAntiga);
+ etNovaPassword1 = findViewById(R.id.etNovaPassword1);
+ etNovaPassword2 = findViewById(R.id.etNovaPassword2);
+ btnConfirmarPassword = findViewById(R.id.btnConfirmarPassword);
+
+ findViewById(R.id.btnVoltarMudarPassword).setOnClickListener(v -> finish());
+
+ SharedPreferences dadosPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+
+ btnConfirmarPassword.setOnClickListener(v -> {
+ String passAntiga = etPasswordAntiga.getText().toString().trim();
+ String novaPass1 = etNovaPassword1.getText().toString().trim();
+ String novaPass2 = etNovaPassword2.getText().toString().trim();
+
+ if (passAntiga.isEmpty() || novaPass1.isEmpty() || novaPass2.isEmpty()) {
+ Toast.makeText(this, "Preenche todos os campos!", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // Confirma se as duas passwords novas são iguais
+ if (!novaPass1.equals(novaPass2)) {
+ Toast.makeText(this, "As passwords novas não coincidem!", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // Confirma se a password antiga escrita bate certo com a guardada no telemóvel
+ String passwordReal = dadosPrefs.getString("password", "");
+ if (!passwordReal.isEmpty() && !passAntiga.equals(passwordReal)) {
+ Toast.makeText(this, "A password antiga está errada!", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ String token = dadosPrefs.getString("access_token", "");
+ if (token.isEmpty()) {
+ Toast.makeText(this, "Erro: Faz login novamente.", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // Atualizar na nuvem
+ Map updates = new HashMap<>();
+ updates.put("password", novaPass1);
+
+ SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
+ api.updateUserData(SupabaseConfig.SUPABASE_KEY, "Bearer " + token, updates).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ // Guardar a nova password na memória para os próximos testes
+ SharedPreferences.Editor editor = dadosPrefs.edit();
+ editor.putString("password", novaPass1);
+ editor.apply();
+
+ Toast.makeText(MudarPasswordActivity.this, "Password alterada com sucesso!", Toast.LENGTH_SHORT).show();
+ finish();
+ } else {
+ Toast.makeText(MudarPasswordActivity.this, "Erro ao mudar password.", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Toast.makeText(MudarPasswordActivity.this, "Falha na internet.", Toast.LENGTH_SHORT).show();
+ }
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/PerfilActivity.java b/app/src/main/java/com/example/pap/PerfilActivity.java
index 3e74624..a83b29f 100644
--- a/app/src/main/java/com/example/pap/PerfilActivity.java
+++ b/app/src/main/java/com/example/pap/PerfilActivity.java
@@ -10,44 +10,81 @@ import androidx.appcompat.app.AppCompatActivity;
public class PerfilActivity extends AppCompatActivity {
- private TextView tvPerfilPontos, tvPerfilDesafios, tvPerfilSequencia;
+ private TextView tvPerfilNome, tvPerfilPontos, tvPerfilDesafios, tvPerfilSequencia;
+ private TextView tvPerfilEmail, tvPerfilPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_perfil);
- tvPerfilPontos = findViewById(R.id.tvPerfilPontos);
- tvPerfilDesafios = findViewById(R.id.tvPerfilDesafios);
- tvPerfilSequencia = findViewById(R.id.tvPerfilSequencia);
+ try {
+ tvPerfilNome = findViewById(R.id.tvPerfilNome);
+ tvPerfilPontos = findViewById(R.id.tvPerfilPontos);
+ tvPerfilDesafios = findViewById(R.id.tvPerfilDesafios);
+ tvPerfilSequencia = findViewById(R.id.tvPerfilSequencia);
- Button btnDefinicoes = findViewById(R.id.btnDefinicoes);
- TextView btnVoltar = findViewById(R.id.btnVoltarPerfil);
+ // Ligar os novos textos do Email e Password
+ tvPerfilEmail = findViewById(R.id.tvPerfilEmail);
+ tvPerfilPassword = findViewById(R.id.tvPerfilPassword);
- // Ler a pontuação guardada (vamos ligar isto mais à frente)
+ Button btnDefinicoes = findViewById(R.id.btnDefinicoes);
+ TextView btnVoltar = findViewById(R.id.btnVoltarPerfil);
+
+ if (btnDefinicoes != null) {
+ btnDefinicoes.setOnClickListener(v -> {
+ Intent intent = new Intent(PerfilActivity.this, DefinicoesActivity.class);
+ startActivity(intent);
+ });
+ }
+
+ if (btnVoltar != null) {
+ btnVoltar.setOnClickListener(v -> finish());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(this, "Erro visual. Verifica o layout.", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
carregarProgresso();
-
- // Botão Definições
- btnDefinicoes.setOnClickListener(v -> {
- // Quando tiveres o ecrã de definições criado, trocas o Toast por isto:
- // Intent intent = new Intent(PerfilActivity.this, DefinicoesActivity.class);
- // startActivity(intent);
- Toast.makeText(this, "A abrir Definições...", Toast.LENGTH_SHORT).show();
- });
-
- // Botão Voltar (manda-te de volta para o Home/Menu)
- btnVoltar.setOnClickListener(v -> finish());
}
private void carregarProgresso() {
- SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
+ try {
+ // Puxa o nome, email e password
+ SharedPreferences perfilPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
- int pontos = prefs.getInt("pontos", 0);
- int desafios = prefs.getInt("desafios", 0);
- int sequencia = prefs.getInt("sequencia", 1);
+ if (tvPerfilNome != null) tvPerfilNome.setText(perfilPrefs.getString("nome", "Utilizador"));
+ if (tvPerfilEmail != null) tvPerfilEmail.setText(perfilPrefs.getString("email", "Sem Email"));
+ if (tvPerfilPassword != null) tvPerfilPassword.setText(perfilPrefs.getString("password", "********"));
- tvPerfilPontos.setText(String.valueOf(pontos));
- tvPerfilDesafios.setText(String.valueOf(desafios));
- tvPerfilSequencia.setText(String.valueOf(sequencia));
+ // Puxa pontos e desafios
+ int pontos = 0;
+ int desafios = 0;
+ try {
+ pontos = perfilPrefs.getInt("pontos", 0);
+ desafios = perfilPrefs.getInt("desafios_concluidos", 0);
+ } catch (Exception e) {
+ try {
+ String pts = perfilPrefs.getString("pontos", "0");
+ pontos = Integer.parseInt(pts.replaceAll("[^0-9]", ""));
+ } catch (Exception ignored) {}
+ }
+
+ // Puxa a sequência
+ SharedPreferences gamificacaoPrefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
+ int sequencia = gamificacaoPrefs.getInt("sequencia", 1);
+
+ if (tvPerfilPontos != null) tvPerfilPontos.setText(String.valueOf(pontos));
+ if (tvPerfilDesafios != null) tvPerfilDesafios.setText(String.valueOf(desafios));
+ if (tvPerfilSequencia != null) tvPerfilSequencia.setText(String.valueOf(sequencia));
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_definicoes.xml b/app/src/main/res/layout/activity_definicoes.xml
index 71f4f3f..1ab1f8c 100644
--- a/app/src/main/res/layout/activity_definicoes.xml
+++ b/app/src/main/res/layout/activity_definicoes.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="?android:attr/colorBackground"
+ android:background="#FFFFFF"
android:padding="24dp">
@@ -40,34 +40,6 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
-
-
-
-
-
-
-
+ android:text="Mudar Email"
+ android:textColor="#1C1C1E"
+ android:backgroundTint="#E5E5EA"
+ android:layout_marginBottom="8dp"
+ app:cornerRadius="8dp"
+ android:elevation="0dp"
+ style="?android:attr/borderlessButtonStyle"/>
-
+ android:text="Mudar Password"
+ android:textColor="#1C1C1E"
+ android:backgroundTint="#E5E5EA"
+ app:cornerRadius="8dp"
+ android:elevation="0dp"
+ style="?android:attr/borderlessButtonStyle"/>
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_foto.xml b/app/src/main/res/layout/activity_foto.xml
index d645e03..ed4bea8 100644
--- a/app/src/main/res/layout/activity_foto.xml
+++ b/app/src/main/res/layout/activity_foto.xml
@@ -114,7 +114,19 @@
android:backgroundTint="#F2F2F7"
android:textColor="#1C1C1E"
app:cornerRadius="16dp"
- android:visibility="gone" />
+ android:visibility="gone"
+ android:layout_marginBottom="12dp"/>
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml
index 3ad846f..0658da5 100644
--- a/app/src/main/res/layout/activity_home.xml
+++ b/app/src/main/res/layout/activity_home.xml
@@ -198,7 +198,7 @@
android:layout_marginStart="20dp">
-
+
@@ -238,7 +238,7 @@
android:layout_marginStart="20dp">
-
+
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 3eb75e8..408e337 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -56,7 +56,7 @@
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@android:color/transparent"
- android:hint="A tua Password"
+ android:hint="A tua Palavra-passe"
android:inputType="textPassword"
android:paddingStart="16dp"
android:paddingEnd="16dp"
diff --git a/app/src/main/res/layout/activity_mudar_email.xml b/app/src/main/res/layout/activity_mudar_email.xml
new file mode 100644
index 0000000..c67447f
--- /dev/null
+++ b/app/src/main/res/layout/activity_mudar_email.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_mudar_password.xml b/app/src/main/res/layout/activity_mudar_password.xml
new file mode 100644
index 0000000..bdc689d
--- /dev/null
+++ b/app/src/main/res/layout/activity_mudar_password.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_perfil.xml b/app/src/main/res/layout/activity_perfil.xml
index 8b7ba62..740d8a5 100644
--- a/app/src/main/res/layout/activity_perfil.xml
+++ b/app/src/main/res/layout/activity_perfil.xml
@@ -47,32 +47,15 @@
android:orientation="vertical"
android:gravity="center_horizontal">
-
-
-
-
+ android:layout_marginBottom="24dp">
-
+
@@ -110,7 +93,7 @@
android:orientation="vertical"
android:gravity="center">
-
+
@@ -123,17 +106,40 @@
android:orientation="vertical"
android:gravity="center">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+