Compare commits

..

2 Commits

17 changed files with 815 additions and 401 deletions

View File

@@ -3,7 +3,14 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<application
android:allowBackup="true"
@@ -25,9 +32,9 @@
<activity android:name=".HomeActivity" android:exported="false" />
<activity android:name=".RegisterActivity" android:exported="false" />
<activity android:name=".MainActivity" android:exported="false" />
<!-- AQUI ESTÁ A ACTIVITY QUE FALTAVA! -->
<activity android:name=".VerificacaoActivity" android:exported="false" />
<activity android:name=".MudarEmailActivity" android:exported="false" />
<activity android:name=".MudarPasswordActivity" android:exported="false" />
<activity
android:name=".LoginActivity"

View File

@@ -1,237 +1,66 @@
package com.example.pap;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.InputType;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.SwitchCompat;
import java.util.HashMap;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class DefinicoesActivity extends AppCompatActivity {
private SwitchCompat switchDarkMode;
private Button btnEditarNome, btnMudarEmail, btnMudarPass, btnVoltarDef;
private SharedPreferences sharedPreferences;
private EditText etNovoNome;
private Button btnGuardarNome, btnSairConta, btnIrMudarEmail, btnIrMudarPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_definicoes);
// Iniciar a memória local do telemóvel
sharedPreferences = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
etNovoNome = findViewById(R.id.etNovoNome);
btnGuardarNome = findViewById(R.id.btnGuardarNome);
btnSairConta = findViewById(R.id.btnSairConta);
btnIrMudarEmail = findViewById(R.id.btnIrMudarEmail);
btnIrMudarPassword = findViewById(R.id.btnIrMudarPassword);
// Ligar os elementos do design ao código
switchDarkMode = findViewById(R.id.switchDarkMode);
btnEditarNome = findViewById(R.id.btnEditarNome);
btnMudarEmail = findViewById(R.id.btnMudarEmail);
btnMudarPass = findViewById(R.id.btnMudarPass);
btnVoltarDef = findViewById(R.id.btnVoltarDef);
findViewById(R.id.btnVoltarDefinicoes).setOnClickListener(v -> finish());
// 1. Lógica do Modo Escuro
boolean isDarkMode = sharedPreferences.getBoolean("dark_mode", false);
switchDarkMode.setChecked(isDarkMode);
SharedPreferences dadosPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
etNovoNome.setText(dadosPrefs.getString("nome", ""));
switchDarkMode.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
sharedPreferences.edit().putBoolean("dark_mode", isChecked).apply();
// Ir para Mudar Email
btnIrMudarEmail.setOnClickListener(v -> {
startActivity(new Intent(DefinicoesActivity.this, MudarEmailActivity.class));
});
// Configurar os cliques nos botões
btnEditarNome.setOnClickListener(v -> mostrarPopupNome());
btnMudarEmail.setOnClickListener(v -> mostrarPopupEmail());
btnMudarPass.setOnClickListener(v -> mostrarPopupPassword());
btnVoltarDef.setOnClickListener(v -> finish());
}
// Ir para Mudar Password
btnIrMudarPassword.setOnClickListener(v -> {
startActivity(new Intent(DefinicoesActivity.this, MudarPasswordActivity.class));
});
// --- FUNÇÕES DOS POP-UPS ---
private void mostrarPopupNome() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Mudar Nome");
final EditText input = new EditText(this);
input.setHint("Novo nome");
builder.setView(input);
builder.setPositiveButton("Guardar", (dialog, which) -> {
String novoNome = input.getText().toString().trim();
// 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.edit().putString("nome", novoNome).apply();
Toast.makeText(this, "Nome atualizado com sucesso!", Toast.LENGTH_SHORT).show();
SharedPreferences.Editor editor = dadosPrefs.edit();
editor.putString("nome", novoNome);
editor.apply();
Toast.makeText(this, "Nome atualizado!", Toast.LENGTH_SHORT).show();
finish();
}
});
builder.show();
}
private void mostrarPopupEmail() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Alterar Email");
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(50, 20, 50, 0);
final EditText etPassAtual = new EditText(this);
etPassAtual.setHint("A tua Password atual");
etPassAtual.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(etPassAtual);
final EditText etNovoEmail = new EditText(this);
etNovoEmail.setHint("Novo Email");
layout.addView(etNovoEmail);
builder.setView(layout);
builder.setPositiveButton("Alterar", (dialog, which) -> {
String passAtual = etPassAtual.getText().toString().trim();
String novoEmail = etNovoEmail.getText().toString().trim();
String emailAtual = sharedPreferences.getString("email", "");
if (passAtual.isEmpty() || novoEmail.isEmpty()) {
Toast.makeText(this, "Preenche todos os campos!", Toast.LENGTH_SHORT).show();
return;
}
SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
UserCredentials creds = new UserCredentials(emailAtual, passAtual);
// Confirma a identidade do utilizador (Mini-login)
api.login(SupabaseConfig.SUPABASE_KEY, creds).enqueue(new Callback<SupabaseResponse>() {
@Override
public void onResponse(Call<SupabaseResponse> call, Response<SupabaseResponse> response) {
if (response.isSuccessful() && response.body() != null) {
String freshToken = response.body().access_token;
// Envia o pedido de alteração de email para o Supabase
Map<String, String> updates = new HashMap<>();
updates.put("email", novoEmail);
api.updateUserData(SupabaseConfig.SUPABASE_KEY, "Bearer " + freshToken, updates).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
// Apenas avisa o utilizador. O email só muda após clicar no link!
Toast.makeText(DefinicoesActivity.this, "Link enviado! Verifica a tua nova caixa de correio para confirmar.", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(DefinicoesActivity.this, "Erro ao tentar enviar o email de confirmação.", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
Toast.makeText(DefinicoesActivity.this, "Verifica a tua ligação à internet!", Toast.LENGTH_SHORT).show();
}
// Terminar Sessão
btnSairConta.setOnClickListener(v -> {
SharedPreferences.Editor editor = dadosPrefs.edit();
editor.clear();
editor.apply();
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();
});
} else {
Toast.makeText(DefinicoesActivity.this, "A password atual está incorreta!", Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<SupabaseResponse> call, Throwable t) {
Toast.makeText(DefinicoesActivity.this, "Verifica a tua ligação à internet!", Toast.LENGTH_SHORT).show();
}
});
});
builder.show();
}
private void mostrarPopupPassword() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Alterar Password");
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(50, 20, 50, 0);
final EditText etAntiga = new EditText(this);
etAntiga.setHint("Password Antiga");
etAntiga.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(etAntiga);
final EditText etNova = new EditText(this);
etNova.setHint("Nova Password (min. 6 caracteres)");
etNova.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(etNova);
final EditText etConfirma = new EditText(this);
etConfirma.setHint("Confirma a Nova Password");
etConfirma.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(etConfirma);
builder.setView(layout);
builder.setPositiveButton("Atualizar", (dialog, which) -> {
String pAntiga = etAntiga.getText().toString().trim();
String pNova = etNova.getText().toString().trim();
String pConfirma = etConfirma.getText().toString().trim();
String emailAtual = sharedPreferences.getString("email", "");
if (pAntiga.isEmpty() || pNova.isEmpty() || pConfirma.isEmpty()) {
Toast.makeText(this, "Preenche todos os campos!", Toast.LENGTH_SHORT).show();
return;
}
if (!pNova.equals(pConfirma) || pNova.length() < 6) {
Toast.makeText(this, "As novas passwords não coincidem ou são muito curtas!", Toast.LENGTH_LONG).show();
return;
}
SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
UserCredentials creds = new UserCredentials(emailAtual, pAntiga);
// Confirma a identidade do utilizador (Mini-login)
api.login(SupabaseConfig.SUPABASE_KEY, creds).enqueue(new Callback<SupabaseResponse>() {
@Override
public void onResponse(Call<SupabaseResponse> call, Response<SupabaseResponse> response) {
if (response.isSuccessful() && response.body() != null) {
String freshToken = response.body().access_token;
// Atualiza a palavra-passe no Supabase
Map<String, String> updates = new HashMap<>();
updates.put("password", pNova);
api.updateUserData(SupabaseConfig.SUPABASE_KEY, "Bearer " + freshToken, updates).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
Toast.makeText(DefinicoesActivity.this, "Password alterada com sucesso!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(DefinicoesActivity.this, "Erro no servidor ao mudar password.", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
Toast.makeText(DefinicoesActivity.this, "Verifica a tua ligação à internet!", Toast.LENGTH_SHORT).show();
}
});
} else {
Toast.makeText(DefinicoesActivity.this, "A password antiga está incorreta!", Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<SupabaseResponse> call, Throwable t) {
Toast.makeText(DefinicoesActivity.this, "Verifica a tua ligação à internet!", Toast.LENGTH_SHORT).show();
}
});
});
builder.show();
}
}

View File

@@ -58,7 +58,6 @@ public class DesafiosActivity extends AppCompatActivity {
verificarResetMeiaNoite();
// Launcher da Câmara
ActivityResultLauncher<Intent> videoLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
@@ -70,7 +69,6 @@ public class DesafiosActivity extends AppCompatActivity {
}
});
// Configurar Botões
btnVideoAgua.setOnClickListener(v -> { desafioAtualSendoGravado = 0; abrirCamera(videoLauncher); });
btnVideoD1.setOnClickListener(v -> { desafioAtualSendoGravado = 1; abrirCamera(videoLauncher); });
btnVideoD2.setOnClickListener(v -> { desafioAtualSendoGravado = 2; abrirCamera(videoLauncher); });
@@ -91,7 +89,6 @@ public class DesafiosActivity extends AppCompatActivity {
String dataGuardada = prefs.getString("data_5_desafios", "");
if (!dataHoje.equals(dataGuardada)) {
// Limpar tudo
SharedPreferences.Editor editor = prefs.edit();
editor.putString("data_5_desafios", dataHoje);
editor.putFloat("agua_litros", 0.0f);
@@ -99,8 +96,10 @@ public class DesafiosActivity extends AppCompatActivity {
editor.putBoolean("d2_concluido", false);
editor.putBoolean("d3_concluido", false);
editor.putBoolean("d4_concluido", false);
// Zera a água para as estatísticas
// Zera a água e as calorias dos desafios diários
editor.putInt("agua_hoje", 0);
editor.putInt("calorias_desafios", 0);
editor.apply();
}
@@ -122,10 +121,10 @@ public class DesafiosActivity extends AppCompatActivity {
private void atualizarTextoDesafio(TextView tv, boolean concluido) {
if (concluido) {
tv.setText("Estado: Concluído ✅");
tv.setTextColor(Color.parseColor("#10B981")); // Verde
tv.setTextColor(Color.parseColor("#10B981"));
} else {
tv.setText("Estado: Pendente");
tv.setTextColor(Color.parseColor("#EF4444")); // Vermelho
tv.setTextColor(Color.parseColor("#EF4444"));
}
}
@@ -178,7 +177,6 @@ public class DesafiosActivity extends AppCompatActivity {
SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
// Dar pontos no perfil
SharedPreferences perfilPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
SharedPreferences.Editor perfilEditor = perfilPrefs.edit();
@@ -191,7 +189,7 @@ public class DesafiosActivity extends AppCompatActivity {
litrosAgua += lido;
editor.putFloat("agua_litros", litrosAgua);
editor.putInt("agua_hoje", (int) (litrosAgua / 0.25f)); // Atualiza Estatísticas
editor.putInt("agua_hoje", (int) (litrosAgua / 0.25f));
tvStatusGeralIA.setText("IA leu: +" + lido + " Litros!");
} catch (Exception e) {
@@ -200,12 +198,18 @@ public class DesafiosActivity extends AppCompatActivity {
}
} else {
if (texto.contains("Status: Concluido")) {
tvStatusGeralIA.setText("IA: Desafio Validado! ✅ +50 Pontos");
if (desafioAtualSendoGravado == 1) editor.putBoolean("d1_concluido", true);
if (desafioAtualSendoGravado == 2) editor.putBoolean("d2_concluido", true);
if (desafioAtualSendoGravado == 3) editor.putBoolean("d3_concluido", true);
if (desafioAtualSendoGravado == 4) editor.putBoolean("d4_concluido", true);
int caloriasAQueimar = 0;
if (desafioAtualSendoGravado == 1) { editor.putBoolean("d1_concluido", true); caloriasAQueimar = 2; }
if (desafioAtualSendoGravado == 2) { editor.putBoolean("d2_concluido", true); caloriasAQueimar = 2; }
if (desafioAtualSendoGravado == 3) { editor.putBoolean("d3_concluido", true); caloriasAQueimar = 2; }
if (desafioAtualSendoGravado == 4) { editor.putBoolean("d4_concluido", true); caloriasAQueimar = 3; }
// Soma as calorias queimadas e atualiza a pontuação
int caloriasTotaisQueimadas = prefs.getInt("calorias_desafios", 0) + caloriasAQueimar;
editor.putInt("calorias_desafios", caloriasTotaisQueimadas);
tvStatusGeralIA.setText("IA: Desafio Validado! ✅ +50 Pontos | 🔥 +" + caloriasAQueimar + " kcal");
perfilEditor.putInt("pontos", perfilPrefs.getInt("pontos", 0) + 50);
perfilEditor.putInt("desafios_concluidos", perfilPrefs.getInt("desafios_concluidos", 0) + 1);

View File

@@ -7,25 +7,35 @@ 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;
private TextView tvValorIMC, tvStatusIMC, tvCaloriasMeta, tvDicaIA, tvCaloriasQueimadas;
private TextView tvProtGramas, tvHidrGramas, tvGordGramas, tvComida1;
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);
setContentView(R.layout.activity_estatisticas);
// Ligar o layout (XML) às variáveis do Java
tvValorIMC = findViewById(R.id.tvValorIMC);
tvStatusIMC = findViewById(R.id.tvStatusIMC);
tvCaloriasMeta = findViewById(R.id.tvCaloriasMeta);
tvDicaIA = findViewById(R.id.tvDicaIA);
tvCaloriasQueimadas = findViewById(R.id.tvCaloriasQueimadas);
tvProtGramas = findViewById(R.id.tvProtGramas);
tvHidrGramas = findViewById(R.id.tvHidrGramas);
tvGordGramas = findViewById(R.id.tvGordGramas);
@@ -37,14 +47,17 @@ public class EstatisticasActivity extends AppCompatActivity {
progressHidr = findViewById(R.id.progressHidr);
progressGord = findViewById(R.id.progressGord);
// Botão voltar
findViewById(R.id.btnVoltarStats).setOnClickListener(v -> finish());
// Chamar as funções que preenchem os dados
// Carrega os painéis locais
calcularIMC();
calcularTMB();
carregarMacrosDaIA();
carregarAgua();
carregarCaloriasQueimadas();
// CHAMA A IA PARA AVALIAR O ESTADO GERAL DO UTILIZADOR
gerarInsightInteligente();
}
private void calcularIMC() {
@@ -79,25 +92,20 @@ 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", "");
// Puxa a idade real. Se der erro ou não existir, assume 20.
String sexo = prefs.getString("sexo", "Masculino"); // Assumido Masculino como padrão
int idade = prefs.getInt("idade", 20);
if (peso > 0 && alturaMetros > 0 && !sexo.isEmpty()) {
float alturaCm = alturaMetros * 100; // Passar para centímetros
if (peso > 0 && alturaMetros > 0) {
float alturaCm = alturaMetros * 100;
double tmb;
// Fórmula de Harris-Benedict
if (sexo.equals("Masculino")) {
tmb = (10 * peso) + (6.25 * alturaCm) - (5 * idade) + 5;
} else {
tmb = (10 * peso) + (6.25 * alturaCm) - (5 * idade) - 161;
}
// Fator de atividade leve para dar uma meta real (1.2)
double caloriasTotais = tmb * 1.2;
tvCaloriasMeta.setText(String.valueOf((int) caloriasTotais));
} else {
tvCaloriasMeta.setText("--");
@@ -126,9 +134,74 @@ public class EstatisticasActivity extends AppCompatActivity {
private void carregarAgua() {
SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
int coposBebidos = prefs.getInt("agua_hoje", 0);
float litrosTotais = coposBebidos * 0.25f;
tvAguaStats.setText(String.format(Locale.getDefault(), "💧 %.2f L", litrosTotais));
}
private void carregarCaloriasQueimadas() {
SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
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<AiResponse>() {
@Override
public void onResponse(Call<AiResponse> call, Response<AiResponse> 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<AiResponse> call, Throwable t) {
tvDicaIA.setText("Estás offline. Regista os dados localmente!");
}
});
}
}

View File

@@ -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<Intent> 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. " +
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,50 +192,99 @@ public class FotoActivity extends AppCompatActivity {
});
}
// Função que "lê" a resposta da IA e guarda os números
// --- 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 {
// Apanhar o Nome do prato (tudo o que está a seguir a "Prato: " até à próxima linha)
int indexNomeStart = texto.indexOf("Prato: ") + 7;
int indexNomeEnd = texto.indexOf("\n", indexNomeStart);
String nomePrato = texto.substring(indexNomeStart, indexNomeEnd).trim();
// Apanhar os números
int calorias = extrairNumero(texto, "Calorias: ", " kcal");
int proteina = extrairNumero(texto, "Macros: ", "g Proteína");
int hidratos = extrairNumero(texto, "Proteína, ", "g Hidratos");
int gordura = extrairNumero(texto, "Hidratos, ", "g Gordura");
// Abrir a memória
String dicaIA = "Continua a registar refeições para ver dicas.";
if (texto.contains("Dica: ")) {
int indexDica = texto.indexOf("Dica: ") + 6;
dicaIA = texto.substring(indexDica).trim();
}
SharedPreferences prefs = getSharedPreferences("DadosSaude", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
// Guardar nome do último prato
editor.putString("ultimo_prato", nomePrato);
editor.putString("ultima_dica_ia", dicaIA);
// Somar aos valores do dia
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) {}
}
// --- 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();
} catch (Exception e) {
// Ignora se a IA responder noutro formato para não crashar
}
}
// Ferramenta que corta a fatia certa de texto para tirar os números
private int extrairNumero(String texto, String inicio, String fim) {
try {
int start = texto.indexOf(inicio) + inicio.length();
int end = texto.indexOf(fim, start);
String valorString = texto.substring(start, end).trim();
// Limpa tudo o que não for número (caso a IA escreva mal)
valorString = valorString.replaceAll("[^0-9]", "");
return Integer.parseInt(valorString);
} catch (Exception e) {
return 0; // Se não encontrar, assume 0
return 0;
}
}
}

View File

@@ -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<String, String> 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<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> 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<Void> call, Throwable t) {
Toast.makeText(MudarEmailActivity.this, "Falha na internet.", Toast.LENGTH_SHORT).show();
}
});
});
}
}

View File

@@ -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<String, String> 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<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> 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<Void> call, Throwable t) {
Toast.makeText(MudarPasswordActivity.this, "Falha na internet.", Toast.LENGTH_SHORT).show();
}
});
});
}
}

View File

@@ -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);
try {
tvPerfilNome = findViewById(R.id.tvPerfilNome);
tvPerfilPontos = findViewById(R.id.tvPerfilPontos);
tvPerfilDesafios = findViewById(R.id.tvPerfilDesafios);
tvPerfilSequencia = findViewById(R.id.tvPerfilSequencia);
// Ligar os novos textos do Email e Password
tvPerfilEmail = findViewById(R.id.tvPerfilEmail);
tvPerfilPassword = findViewById(R.id.tvPerfilPassword);
Button btnDefinicoes = findViewById(R.id.btnDefinicoes);
TextView btnVoltar = findViewById(R.id.btnVoltarPerfil);
// Ler a pontuação guardada (vamos ligar isto mais à frente)
carregarProgresso();
// Botão Definições
if (btnDefinicoes != null) {
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();
Intent intent = new Intent(PerfilActivity.this, DefinicoesActivity.class);
startActivity(intent);
});
}
// Botão Voltar (manda-te de volta para o Home/Menu)
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();
}
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();
}
}
}

View File

@@ -4,27 +4,36 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#F8FAFC">
android:background="#FFFFFF"
android:padding="24dp">
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:background="#FFFFFF"
android:elevation="4dp">
android:layout_marginBottom="24dp">
<TextView
android:id="@+id/btnVoltarDefinicoes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Voltar"
android:textSize="16sp"
android:textColor="#8E8E93"
android:layout_centerVertical="true"
android:padding="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Definições"
android:textSize="22sp"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#0F172A"/>
</LinearLayout>
android:textColor="#1C1C1E"
android:layout_centerInParent="true" />
</RelativeLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
@@ -34,92 +43,85 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="APARÊNCIA"
android:textSize="12sp"
android:text="Dados da Conta"
android:textSize="14sp"
android:textColor="#8E8E93"
android:textStyle="bold"
android:textColor="#64748B"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"/>
android:layout_marginBottom="12dp"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
android:layout_marginBottom="24dp">
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:gravity="center_vertical">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Modo Escuro"
android:textSize="16sp"
android:textColor="#0F172A"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switchDarkMode"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:orientation="vertical"
android:padding="20dp">
<EditText
android:id="@+id/etNovoNome"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Novo Nome"
android:textColor="#1C1C1E"
android:background="@android:color/transparent"
android:inputType="textPersonName"
android:layout_marginBottom="8dp"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#E5E5EA" android:layout_marginBottom="8dp"/>
<Button
android:id="@+id/btnIrMudarEmail"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Mudar Email"
android:textColor="#1C1C1E"
android:backgroundTint="#E5E5EA"
android:layout_marginBottom="8dp"
app:cornerRadius="8dp"
android:elevation="0dp"
style="?android:attr/borderlessButtonStyle"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#E5E5EA" android:layout_marginBottom="8dp"/>
<Button
android:id="@+id/btnIrMudarPassword"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Mudar Password"
android:textColor="#1C1C1E"
android:backgroundTint="#E5E5EA"
app:cornerRadius="8dp"
android:elevation="0dp"
style="?android:attr/borderlessButtonStyle"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CONTA E SEGURANÇA"
android:textSize="12sp"
<Button
android:id="@+id/btnGuardarNome"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Guardar Nome"
android:backgroundTint="#03A9F4"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#64748B"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"/>
app:cornerRadius="16dp"/>
<Button
android:id="@+id/btnEditarNome"
android:id="@+id/btnSairConta"
android:layout_width="match_parent"
android:layout_height="55dp"
android:text="Mudar Nome"
android:backgroundTint="#FFFFFF"
android:textColor="#0F172A"
android:layout_marginBottom="8dp"
app:cornerRadius="12dp"
android:gravity="start|center_vertical"
android:paddingStart="16dp"/>
<Button
android:id="@+id/btnMudarEmail"
android:layout_width="match_parent"
android:layout_height="55dp"
android:text="Alterar Email"
android:backgroundTint="#FFFFFF"
android:textColor="#0F172A"
android:layout_marginBottom="8dp"
app:cornerRadius="12dp"
android:gravity="start|center_vertical"
android:paddingStart="16dp"/>
<Button
android:id="@+id/btnMudarPass"
android:layout_width="match_parent"
android:layout_height="55dp"
android:text="Alterar Palavra-Passe"
android:backgroundTint="#FFFFFF"
android:textColor="#0F172A"
android:layout_marginBottom="24dp"
app:cornerRadius="12dp"
android:gravity="start|center_vertical"
android:paddingStart="16dp"/>
<Button
android:id="@+id/btnVoltarDef"
android:layout_width="match_parent"
android:layout_height="55dp"
android:text="Voltar"
android:backgroundTint="#E2E8F0"
android:textColor="#0F172A"
app:cornerRadius="12dp"/>
android:layout_height="60dp"
android:layout_marginTop="16dp"
android:text="Terminar Sessão"
android:backgroundTint="#FF3B30"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold"
app:cornerRadius="16dp"
android:layout_marginBottom="24dp"/>
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -56,7 +56,7 @@
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="✨" android:textSize="24sp" android:layout_marginEnd="12dp"/>
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Insight da IA" android:textSize="12sp" android:textStyle="bold" android:textColor="#0284C7"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Continua a registar as tuas refeições para ver dicas." android:textSize="14sp" android:textColor="#334155" android:layout_marginTop="2dp"/>
<TextView android:id="@+id/tvDicaIA" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Continua a registar as tuas refeições para ver dicas." android:textSize="14sp" android:textColor="#334155" android:layout_marginTop="2dp"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
@@ -147,11 +147,11 @@
android:layout_marginStart="8dp"
app:cardCornerRadius="20dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
app:cardBackgroundColor="#FEF2F2">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp" android:gravity="center">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="👟" android:textSize="20sp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:textSize="18sp" android:textStyle="bold" android:textColor="#1C1C1E" android:layout_marginTop="4dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Passos" android:textSize="12sp" android:textColor="#8E8E93"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="🔥" android:textSize="20sp"/>
<TextView android:id="@+id/tvCaloriasQueimadas" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0 kcal" android:textSize="18sp" android:textStyle="bold" android:textColor="#1C1C1E" android:layout_marginTop="4dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafios" android:textSize="12sp" android:textColor="#8E8E93"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -114,7 +114,19 @@
android:backgroundTint="#F2F2F7"
android:textColor="#1C1C1E"
app:cornerRadius="16dp"
android:visibility="gone" />
android:visibility="gone"
android:layout_marginBottom="12dp"/>
<Button
android:id="@+id/btnCorrigir"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="A IA errou? Corrigir"
android:textColor="#EF4444"
android:backgroundTint="#FEF2F2"
app:cornerRadius="16dp"
android:visibility="gone"/>
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -43,7 +43,7 @@
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#1C1C1E">
app:cardBackgroundColor="#000000">
<LinearLayout
android:layout_width="match_parent"
@@ -198,7 +198,7 @@
android:layout_marginStart="20dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafios Diários" android:textSize="17sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Ganha pontos de recompensa" android:textSize="13sp" android:textColor="#8E8E93" android:layout_marginTop="2dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Faça desafios para melhor o seu desempenho" android:textSize="13sp" android:textColor="#8E8E93" android:layout_marginTop="2dp"/>
</LinearLayout>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:textSize="28sp" android:textColor="#C7C7CC" android:layout_marginStart="8dp"/>
@@ -238,7 +238,7 @@
android:layout_marginStart="20dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="O Meu Perfil" android:textSize="17sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Dados, pontuação e definições" android:textSize="13sp" android:textColor="#8E8E93" android:layout_marginTop="2dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Dados e definições" android:textSize="13sp" android:textColor="#8E8E93" android:layout_marginTop="2dp"/>
</LinearLayout>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:textSize="28sp" android:textColor="#C7C7CC" android:layout_marginStart="8dp"/>

View File

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

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFFFF"
android:padding="24dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp">
<TextView
android:id="@+id/btnVoltarMudarEmail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancelar"
android:textSize="16sp"
android:textColor="#FF3B30"
android:layout_centerVertical="true"
android:padding="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Mudar Email"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#1C1C1E"
android:layout_centerInParent="true" />
</RelativeLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<EditText
android:id="@+id/etAlterarEmail"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Novo Email"
android:textColor="#1C1C1E"
android:background="@android:color/transparent"
android:inputType="textEmailAddress"
android:layout_marginBottom="8dp"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#E5E5EA" android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/etAlterarPassword1"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Escreve a tua Palavra-passe Atual"
android:textColor="#1C1C1E"
android:background="@android:color/transparent"
android:inputType="textPassword"
android:layout_marginBottom="8dp"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#E5E5EA" android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/etAlterarPassword2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Confirma a tua Palavra-passe Atual"
android:textColor="#1C1C1E"
android:background="@android:color/transparent"
android:inputType="textPassword"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/btnConfirmarEmailNovo"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Confirmar Alteração"
android:backgroundTint="#03A9F4"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold"
app:cornerRadius="16dp"/>
</LinearLayout>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFFFF"
android:padding="24dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp">
<TextView
android:id="@+id/btnVoltarMudarPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancelar"
android:textSize="16sp"
android:textColor="#FF3B30"
android:layout_centerVertical="true"
android:padding="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Mudar Password"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#1C1C1E"
android:layout_centerInParent="true" />
</RelativeLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<EditText
android:id="@+id/etPasswordAntiga"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Palavra-passe Antiga"
android:textColor="#1C1C1E"
android:background="@android:color/transparent"
android:inputType="textPassword"
android:layout_marginBottom="8dp"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#E5E5EA" android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/etNovaPassword1"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Nova Palavra-passe"
android:textColor="#1C1C1E"
android:background="@android:color/transparent"
android:inputType="textPassword"
android:layout_marginBottom="8dp"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#E5E5EA" android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/etNovaPassword2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Confirma a Nova Palavra-passe"
android:textColor="#1C1C1E"
android:background="@android:color/transparent"
android:inputType="textPassword"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/btnConfirmarPassword"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Guardar Password"
android:backgroundTint="#03A9F4"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold"
app:cornerRadius="16dp"/>
</LinearLayout>

View File

@@ -47,32 +47,15 @@
android:orientation="vertical"
android:gravity="center_horizontal">
<androidx.cardview.widget.CardView
android:layout_width="120dp"
android:layout_height="120dp"
app:cardCornerRadius="60dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7"
android:layout_marginTop="10dp">
<ImageView
android:id="@+id/ivFotoPerfil"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@android:drawable/ic_menu_gallery"
android:scaleType="centerCrop"
android:padding="30dp"
app:tint="#C7C7CC"/>
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/tvPerfilNome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nome do Utilizador"
android:textSize="22sp"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#1C1C1E"
android:layout_marginTop="16dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="32dp"/>
<androidx.cardview.widget.CardView
@@ -81,7 +64,7 @@
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7"
android:layout_marginBottom="32dp">
android:layout_marginBottom="24dp">
<LinearLayout
android:layout_width="match_parent"
@@ -97,7 +80,7 @@
android:orientation="vertical"
android:gravity="center">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="⭐" android:textSize="24sp" android:layout_marginBottom="4dp"/>
<TextView android:id="@+id/tvPerfilPontos" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="1450" android:textSize="20sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:id="@+id/tvPerfilPontos" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:textSize="20sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Pontos" android:textSize="12sp" android:textColor="#8E8E93"/>
</LinearLayout>
@@ -110,7 +93,7 @@
android:orientation="vertical"
android:gravity="center">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="🏆" android:textSize="24sp" android:layout_marginBottom="4dp"/>
<TextView android:id="@+id/tvPerfilDesafios" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="28" android:textSize="20sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:id="@+id/tvPerfilDesafios" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:textSize="20sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafios" android:textSize="12sp" android:textColor="#8E8E93"/>
</LinearLayout>
@@ -123,17 +106,40 @@
android:orientation="vertical"
android:gravity="center">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="🔥" android:textSize="24sp" android:layout_marginBottom="4dp"/>
<TextView android:id="@+id/tvPerfilSequencia" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="5" android:textSize="20sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:id="@+id/tvPerfilSequencia" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="1" android:textSize="20sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Sequência" android:textSize="12sp" android:textColor="#8E8E93"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F8FAFC"
android:layout_marginBottom="24dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Email Registado" android:textSize="12sp" android:textColor="#8E8E93" android:textStyle="bold"/>
<TextView android:id="@+id/tvPerfilEmail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="email@exemplo.com" android:textSize="16sp" android:textColor="#1C1C1E" android:layout_marginBottom="12dp" android:layout_marginTop="2dp"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#E2E8F0" android:layout_marginBottom="12dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Palavra-passe" android:textSize="12sp" android:textColor="#8E8E93" android:textStyle="bold"/>
<TextView android:id="@+id/tvPerfilPassword" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="********" android:textSize="16sp" android:textColor="#1C1C1E" android:layout_marginTop="2dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/btnDefinicoes"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Definições da Conta"
android:text="Editar Conta"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#FFFFFF"

View File

@@ -77,7 +77,7 @@
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@android:color/transparent"
android:hint="Password (mín. 6 letras)"
android:hint="Palavra-passe (mín. 6 letras)"
android:inputType="textPassword"
android:paddingStart="16dp"
android:paddingEnd="16dp"/>