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"/>