diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 68b0ad3..235163c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -43,4 +43,9 @@ dependencies {
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
+ implementation("androidx.camera:camera-core:1.3.1")
+ implementation("androidx.camera:camera-camera2:1.3.1")
+ implementation("androidx.camera:camera-lifecycle:1.3.1")
+ implementation("androidx.camera:camera-view:1.3.1")
+ implementation("com.squareup.okhttp3:okhttp:4.12.0")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6f76c78..1f486f2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
+
+
analisarImagem(
+ @Header("Authorization") String authHeader,
+ @Body AiRequest request
+ );
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/AiConfig.java b/app/src/main/java/com/example/pap/AiConfig.java
new file mode 100644
index 0000000..69c4c08
--- /dev/null
+++ b/app/src/main/java/com/example/pap/AiConfig.java
@@ -0,0 +1,19 @@
+package com.example.pap;
+
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+public class AiConfig {
+ private static Retrofit retrofit;
+ private static final String BASE_URL = "https://openrouter.ai/api/v1/";
+
+ public static Retrofit getRetrofit() {
+ if (retrofit == null) {
+ retrofit = new Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build();
+ }
+ return retrofit;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/AiModels.java b/app/src/main/java/com/example/pap/AiModels.java
new file mode 100644
index 0000000..0d901f8
--- /dev/null
+++ b/app/src/main/java/com/example/pap/AiModels.java
@@ -0,0 +1,54 @@
+package com.example.pap;
+
+import java.util.List;
+
+class AiRequest {
+ // A MAGIA ESTÁ AQUI: Isto escolhe automaticamente a IA visual que estiver a funcionar hoje!
+ String model = "openrouter/free";
+ List messages;
+
+ AiRequest(List messages) { this.messages = messages; }
+}
+
+class Message {
+ String role;
+ List content;
+
+ Message(String role, List content) {
+ this.role = role;
+ this.content = content;
+ }
+}
+
+class ContentPart {
+ String type;
+ String text;
+ ImageUrl image_url;
+
+ ContentPart(String type, String text) {
+ this.type = type;
+ this.text = text;
+ }
+
+ ContentPart(String type, ImageUrl image_url) {
+ this.type = type;
+ this.image_url = image_url;
+ }
+}
+
+class ImageUrl {
+ String url;
+ ImageUrl(String url) { this.url = url; }
+}
+
+class AiResponse {
+ List choices;
+
+ class Choice {
+ MessageResponse message;
+ }
+
+ class MessageResponse {
+ String content;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/ChatActivity.java b/app/src/main/java/com/example/pap/ChatActivity.java
index 3df565b..2e57238 100644
--- a/app/src/main/java/com/example/pap/ChatActivity.java
+++ b/app/src/main/java/com/example/pap/ChatActivity.java
@@ -1,138 +1,168 @@
package com.example.pap;
import android.graphics.Color;
-import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.view.Gravity;
-import android.view.View;
-import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
+import android.widget.Toast;
+
import androidx.appcompat.app.AppCompatActivity;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
public class ChatActivity extends AppCompatActivity {
+ private EditText etMensagem;
+ private ImageButton btnEnviar;
private LinearLayout chatLayout;
private ScrollView chatScrollView;
- private EditText etNovaMensagem;
- private Button btnEnviarMensagem;
- private Button btnVoltarChat;
+
+ // A TUA CHAVE DA GOOGLE VEM PARA AQUI!
+ private static final String GEMINI_API_KEY = "AQ.Ab8RN6IfhsFBO3UOpK3vYd7BrR2nfFb-mVE--nvqRnR46hB36A";
+
+ // O motor do Gemini 1.5 Flash
+ private static final String GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=" + GEMINI_API_KEY;
+
+ private OkHttpClient client;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
- // 1. Associar os componentes do layout
+ etMensagem = findViewById(R.id.etMensagem);
+ btnEnviar = findViewById(R.id.btnEnviar);
chatLayout = findViewById(R.id.chatLayout);
chatScrollView = findViewById(R.id.chatScrollView);
- etNovaMensagem = findViewById(R.id.etNovaMensagem);
- btnEnviarMensagem = findViewById(R.id.btnEnviarMensagem);
- btnVoltarChat = findViewById(R.id.btnVoltarChat);
- // Mensagem inicial da IA ao abrir o ecrã
- adicionarMensagemBalao("Olá! Sou o teu IA Coach de Saúde. Como te posso ajudar hoje na tua jornada de emagrecimento?", false);
+ client = new OkHttpClient();
- // 2. Clique no botão de Enviar
- btnEnviarMensagem.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- String mensagemUser = etNovaMensagem.getText().toString().trim();
+ // Dá as boas vindas
+ adicionarBalaoNoEcra("Olá! Sou o NutriChat AI 🤖. O que comeste hoje ou que dúvidas tens sobre a tua dieta?", false);
- if (!mensagemUser.isEmpty()) {
- // Adiciona a mensagem do utilizador no ecrã
- adicionarMensagemBalao(mensagemUser, true);
+ btnEnviar.setOnClickListener(v -> {
+ String pergunta = etMensagem.getText().toString().trim();
+ if (pergunta.isEmpty()) {
+ Toast.makeText(this, "Escreve alguma merda primeiro!", Toast.LENGTH_SHORT).show();
+ return;
+ }
- // Limpa a caixa de texto
- etNovaMensagem.setText("");
+ // 1. Mostrar a mensagem do utilizador no ecrã
+ adicionarBalaoNoEcra(pergunta, true);
+ etMensagem.setText(""); // Limpa a caixa de texto
- // Simula a IA a processar e a responder
- simularRespostaDaIA(mensagemUser);
+ // 2. Chamar a Inteligência Artificial
+ chamarGemini(pergunta);
+ });
+ }
+
+ // Função que desenha os balões de conversa no ecrã
+ private void adicionarBalaoNoEcra(String texto, boolean isUser) {
+ runOnUiThread(() -> {
+ TextView tv = new TextView(this);
+ tv.setText(texto);
+ tv.setTextSize(16f);
+ tv.setPadding(30, 20, 30, 20);
+ tv.setTextColor(isUser ? Color.WHITE : Color.parseColor("#1E293B"));
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ );
+ params.setMargins(0, 10, 0, 10);
+
+ // Se for o utilizador, o balão vai para a direita e fica azul
+ if (isUser) {
+ params.gravity = Gravity.END;
+ tv.setBackgroundResource(R.drawable.balao_user); // Já vamos criar isto!
+ } else {
+ // Se for a IA, o balão vai para a esquerda e fica branco/cinza
+ params.gravity = Gravity.START;
+ tv.setBackgroundResource(R.drawable.balao_ai); // Já vamos criar isto!
+ }
+
+ tv.setLayoutParams(params);
+ chatLayout.addView(tv);
+
+ // Faz scroll automático para o fundo para ver a mensagem nova
+ chatScrollView.post(() -> chatScrollView.fullScroll(ScrollView.FOCUS_DOWN));
+ });
+ }
+
+ // Função que vai à internet falar com o cérebro da Google
+ private void chamarGemini(String pergunta) {
+ try {
+ // Instrução secreta para ele agir como Nutricionista
+ String promptCompleto = "Aja como um nutricionista simpático e profissional. Responda de forma curta e direta à seguinte mensagem do utilizador: " + pergunta;
+
+ // Construir o JSON que o Gemini exige
+ JSONObject part = new JSONObject();
+ part.put("text", promptCompleto);
+ JSONArray parts = new JSONArray();
+ parts.put(part);
+ JSONObject content = new JSONObject();
+ content.put("parts", parts);
+ JSONArray contents = new JSONArray();
+ contents.put(content);
+ JSONObject jsonBody = new JSONObject();
+ jsonBody.put("contents", contents);
+
+ RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.get("application/json; charset=utf-8"));
+ Request request = new Request.Builder()
+ .url(GEMINI_URL)
+ .post(body)
+ .build();
+
+ // Fazer a chamada fora da Thread principal para a app não encravar
+ client.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ adicionarBalaoNoEcra("Oops! Fiquei sem internet ou deu merda na ligação. 🔌", false);
}
- }
- });
- // 3. Clique no botão de Voltar
- btnVoltarChat.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- }
- });
- }
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (response.isSuccessful()) {
+ try {
+ String respostaJSON = response.body().string();
+ JSONObject jsonObject = new JSONObject(respostaJSON);
+ // Escarafunchar o JSON para tirar só o texto da resposta
+ String respostaIA = jsonObject.getJSONArray("candidates")
+ .getJSONObject(0)
+ .getJSONObject("content")
+ .getJSONArray("parts")
+ .getJSONObject(0)
+ .getString("text");
- // Passo 3: Função para desenhar os balões de conversa no ecrã
- private void adicionarMensagemBalao(String texto, boolean isUser) {
- TextView textView = new TextView(this);
- textView.setText(texto);
- textView.setTextSize(16f);
- textView.setPadding(32, 24, 32, 24);
+ // Mostrar a resposta no ecrã
+ adicionarBalaoNoEcra(respostaIA, false);
- // Configurar as margens e o alinhamento (Direita = Utilizador, Esquerda = IA)
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- params.setMargins(0, 16, 0, 16);
+ } catch (Exception e) {
+ adicionarBalaoNoEcra("Deu um nó no meu cérebro ao ler os dados. 🤯", false);
+ }
+ } else {
+ adicionarBalaoNoEcra("Erro da Google: " + response.code(), false);
+ }
+ }
+ });
- // Configurar o design arredondado do balão
- GradientDrawable background = new GradientDrawable();
- background.setCornerRadius(32f);
-
- if (isUser) {
- // Estilo do Utilizador (Fundo Verde, Texto Branco, Alinhado à direita)
- params.gravity = Gravity.END;
- background.setColor(Color.parseColor("#4CAF50"));
- textView.setTextColor(Color.WHITE);
- } else {
- // Estilo da IA (Fundo Cinza/Branco, Texto Escuro, Alinhado à esquerda)
- params.gravity = Gravity.START;
- background.setColor(Color.parseColor("#FFFFFF"));
- textView.setTextColor(Color.parseColor("#2E3D32"));
- textView.setElevation(4f); // Dá uma pequena sombra ao balão da IA
+ } catch (Exception e) {
+ e.printStackTrace();
}
-
- textView.setLayoutParams(params);
- textView.setBackground(background);
-
- // Adicionar o balão ao ecrã
- chatLayout.addView(textView);
-
- // Fazer scroll automático para baixo para ver a nova mensagem
- chatScrollView.post(new Runnable() {
- @Override
- public void run() {
- chatScrollView.fullScroll(ScrollView.FOCUS_DOWN);
- }
- });
- }
-
- // Passo 4: Função para simular a inteligência da IA
- private void simularRespostaDaIA(String perguntaUtilizador) {
- perguntaUtilizador = perguntaUtilizador.toLowerCase();
- final String resposta;
-
- // Regras simples para fingir que a IA está a entender a pergunta
- if (perguntaUtilizador.contains("água") || perguntaUtilizador.contains("agua")) {
- resposta = "A hidratação é fundamental! O ideal é beberes pelo menos 2 a 3 litros de água por dia. Já bebeste algum copo hoje?";
- } else if (perguntaUtilizador.contains("comer") || perguntaUtilizador.contains("fome") || perguntaUtilizador.contains("dieta")) {
- resposta = "Se sentires fome entre as refeições, opta por snacks saudáveis como uma peça de fruta ou um punhado de frutos secos. Evita os doces!";
- } else if (perguntaUtilizador.contains("treino") || perguntaUtilizador.contains("exercício") || perguntaUtilizador.contains("correr")) {
- resposta = "Excelente! O exercício acelera o metabolismo. Lembra-te que a consistência é mais importante do que a intensidade. 30 minutos de caminhada já fazem a diferença.";
- } else {
- resposta = "Essa é uma ótima questão! O segredo do emagrecimento é o défice calórico aliado a bons hábitos. Continua focado, estou aqui para te apoiar em cada passo!";
- }
-
- // Simular um tempo de "pensamento" da IA (espera 1.5 segundos antes de mostrar a resposta)
- new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
- @Override
- public void run() {
- adicionarMensagemBalao(resposta, false);
- }
- }, 1500);
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/DefinicoesActivity.java b/app/src/main/java/com/example/pap/DefinicoesActivity.java
new file mode 100644
index 0000000..bb59432
--- /dev/null
+++ b/app/src/main/java/com/example/pap/DefinicoesActivity.java
@@ -0,0 +1,237 @@
+package com.example.pap;
+
+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;
+
+ @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);
+
+ // 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);
+
+ // 1. Lógica do Modo Escuro
+ boolean isDarkMode = sharedPreferences.getBoolean("dark_mode", false);
+ switchDarkMode.setChecked(isDarkMode);
+
+ 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();
+ });
+
+ // Configurar os cliques nos botões
+ btnEditarNome.setOnClickListener(v -> mostrarPopupNome());
+ btnMudarEmail.setOnClickListener(v -> mostrarPopupEmail());
+ btnMudarPass.setOnClickListener(v -> mostrarPopupPassword());
+ btnVoltarDef.setOnClickListener(v -> finish());
+ }
+
+ // --- 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();
+ if (!novoNome.isEmpty()) {
+ sharedPreferences.edit().putString("nome", novoNome).apply();
+ Toast.makeText(this, "Nome atualizado com sucesso!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ 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() {
+ @Override
+ public void onResponse(Call call, Response 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 updates = new HashMap<>();
+ updates.put("email", novoEmail);
+
+ api.updateUserData(SupabaseConfig.SUPABASE_KEY, "Bearer " + freshToken, updates).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response 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 call, Throwable t) {
+ Toast.makeText(DefinicoesActivity.this, "Verifica a tua ligação à internet!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ } else {
+ Toast.makeText(DefinicoesActivity.this, "A password atual está incorreta!", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void onFailure(Call 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() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful() && response.body() != null) {
+ String freshToken = response.body().access_token;
+
+ // Atualiza a palavra-passe no Supabase
+ Map updates = new HashMap<>();
+ updates.put("password", pNova);
+
+ api.updateUserData(SupabaseConfig.SUPABASE_KEY, "Bearer " + freshToken, updates).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response 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 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 call, Throwable t) {
+ Toast.makeText(DefinicoesActivity.this, "Verifica a tua ligação à internet!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ });
+ builder.show();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/EditarPerfilActivity.java b/app/src/main/java/com/example/pap/EditarPerfilActivity.java
new file mode 100644
index 0000000..580445b
--- /dev/null
+++ b/app/src/main/java/com/example/pap/EditarPerfilActivity.java
@@ -0,0 +1,30 @@
+package com.example.pap;
+
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+
+public class EditarPerfilActivity extends AppCompatActivity {
+
+ private EditText etEditNome, etEditEmail, etEditPassword;
+ private Button btnGuardarPerfil;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_editar_perfil);
+
+ etEditNome = findViewById(R.id.etEditNome);
+ etEditEmail = findViewById(R.id.etEditEmail);
+ etEditPassword = findViewById(R.id.etEditPassword);
+ btnGuardarPerfil = findViewById(R.id.btnGuardarPerfil);
+
+ btnGuardarPerfil.setOnClickListener(v -> {
+ // Mais tarde ligamos esta merda ao Supabase!
+ Toast.makeText(this, "Dados atualizados com sucesso, caralho! ✅", Toast.LENGTH_SHORT).show();
+ finish(); // Fecha e volta atrás
+ });
+ }
+}
\ 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 49c8148..cfdaddb 100644
--- a/app/src/main/java/com/example/pap/FotoActivity.java
+++ b/app/src/main/java/com/example/pap/FotoActivity.java
@@ -1,146 +1,108 @@
package com.example.pap;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.provider.MediaStore;
+import android.util.Base64;
import android.view.View;
import android.widget.Button;
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.AppCompatActivity;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import java.util.ArrayList;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Collections;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
public class FotoActivity extends AppCompatActivity {
private ImageView ivFotoComida;
- private Button btnTirarFoto, btnVoltar, btnAddManual, btnConfirmar;
- private LinearLayout layoutResultadosIA;
- private TextView tvTotalCalorias, tvNomePrato;
- private RecyclerView recyclerView;
- private IngredientesAdapter adapter;
- private ArrayList listaIngredientes;
+ private Button btnTirarFoto, btnAnalisarIA, btnVoltarFoto;
+ private TextView tvResultadoIA;
+ private Bitmap imagemCapturada;
- // Lançador da Câmara
- private final ActivityResultLauncher cameraLauncher = registerForActivityResult(
- new ActivityResultContracts.StartActivityForResult(),
- result -> {
- if (result.getResultCode() == RESULT_OK && result.getData() != null) {
- // Recebe a miniatura da foto
- Bundle extras = result.getData().getExtras();
- Bitmap imageBitmap = (Bitmap) extras.get("data");
- ivFotoComida.setImageBitmap(imageBitmap);
-
- // Inicia a simulação da IA
- Toast.makeText(this, "A analisar a imagem com IA...", Toast.LENGTH_SHORT).show();
- simularAnaliseIA();
- }
- }
- );
+ // COLA A TUA CHAVE DO OPENROUTER AQUI (Aquela que tiraste sem telemóvel e sem VPN)
+ private final String MINHA_API_KEY = "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";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_foto);
- // Inicializar vistas
ivFotoComida = findViewById(R.id.ivFotoComida);
- btnTirarFoto = findViewById(R.id.btnTirarFotoComida);
- btnVoltar = findViewById(R.id.btnVoltarFoto);
- layoutResultadosIA = findViewById(R.id.layoutResultadosIA);
- tvTotalCalorias = findViewById(R.id.tvTotalCalorias);
- tvNomePrato = findViewById(R.id.tvNomePratoDetectado);
- recyclerView = findViewById(R.id.recyclerViewIngredientes);
- btnAddManual = findViewById(R.id.btnAddIngredienteManual);
- btnConfirmar = findViewById(R.id.btnConfirmarRefeicao);
+ btnTirarFoto = findViewById(R.id.btnTirarFoto);
+ btnAnalisarIA = findViewById(R.id.btnAnalisarIA);
+ tvResultadoIA = findViewById(R.id.tvResultadoIA);
+ btnVoltarFoto = findViewById(R.id.btnVoltarFoto);
- // Configurar a RecyclerView
- listaIngredientes = new ArrayList<>();
- recyclerView.setLayoutManager(new LinearLayoutManager(this));
- adapter = new IngredientesAdapter(listaIngredientes);
- recyclerView.setAdapter(adapter);
+ ActivityResultLauncher cameraLauncher = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ if (result.getResultCode() == RESULT_OK && result.getData() != null) {
+ Bundle extras = result.getData().getExtras();
+ imagemCapturada = (Bitmap) extras.get("data");
- // Configurar o clique no botão de apagar (lixo) na lista
- adapter.setOnItemClickListener(new IngredientesAdapter.OnItemClickListener() {
+ ivFotoComida.setPadding(0, 0, 0, 0);
+ ivFotoComida.setImageBitmap(imagemCapturada);
+
+ btnTirarFoto.setVisibility(View.GONE);
+ btnAnalisarIA.setVisibility(View.VISIBLE);
+ tvResultadoIA.setText("Prato detetado! Clica em Analisar.");
+ }
+ }
+ );
+
+ btnTirarFoto.setOnClickListener(v -> cameraLauncher.launch(new Intent(MediaStore.ACTION_IMAGE_CAPTURE)));
+
+ btnAnalisarIA.setOnClickListener(v -> { if (imagemCapturada != null) enviarParaIA(); });
+ btnVoltarFoto.setOnClickListener(v -> finish());
+ }
+
+ private void enviarParaIA() {
+ tvResultadoIA.setText("A procurar servidor livre e a analisar... ⏳");
+ btnAnalisarIA.setEnabled(false);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ imagemCapturada.compress(Bitmap.CompressFormat.JPEG, 60, outputStream);
+ String base64Image = Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP);
+
+ String dataUrl = "data:image/jpeg;base64," + base64Image;
+ String prompt = "Identifica a comida nesta imagem e diz-me as calorias e macronutrientes num pequeno parágrafo.";
+
+ ContentPart textPart = new ContentPart("text", prompt);
+ ContentPart imagePart = new ContentPart("image_url", new ImageUrl(dataUrl));
+ Message message = new Message("user", java.util.Arrays.asList(textPart, imagePart));
+ AiRequest request = new AiRequest(Collections.singletonList(message));
+
+ AiApi api = AiConfig.getRetrofit().create(AiApi.class);
+ api.analisarImagem("Bearer " + MINHA_API_KEY, request).enqueue(new Callback() {
@Override
- public void onDeleteClick(int position) {
- removerIngrediente(position);
+ public void onResponse(Call call, Response response) {
+ btnAnalisarIA.setEnabled(true);
+ if (response.isSuccessful() && response.body() != null) {
+ try {
+ String resultado = response.body().choices.get(0).message.content;
+ tvResultadoIA.setText(resultado);
+ } catch (Exception e) { tvResultadoIA.setText("Erro ao ler texto da IA."); }
+ } else {
+ try {
+ String erroReal = response.errorBody() != null ? response.errorBody().string() : "Vazio";
+ tvResultadoIA.setText("ERRO: " + response.code() + "\n" + erroReal);
+ } catch (Exception e) { tvResultadoIA.setText("Erro desconhecido."); }
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ btnAnalisarIA.setEnabled(true);
+ tvResultadoIA.setText("Falha na ligação à Internet.");
}
});
-
- btnTirarFoto.setOnClickListener(v -> abrirCamera());
- btnVoltar.setOnClickListener(v -> finish());
-
- // Botão para simular adicionar um ingrediente extra manualmente
- btnAddManual.setOnClickListener(v -> {
- listaIngredientes.add(new Ingrediente("Azeite Extra (1 c.sopa)", 120));
- adapter.notifyItemInserted(listaIngredientes.size() - 1);
- calcularTotal();
- Toast.makeText(this, "Ingrediente adicionado!", Toast.LENGTH_SHORT).show();
- });
-
- btnConfirmar.setOnClickListener(v -> {
- Toast.makeText(this, "Refeição registada no diário!", Toast.LENGTH_LONG).show();
- finish(); // Fecha o ecrã após confirmar
- });
- }
-
- private void abrirCamera() {
- Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- try {
- cameraLauncher.launch(takePictureIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, "Erro ao abrir a câmara.", Toast.LENGTH_SHORT).show();
- }
- }
-
- // Simula a resposta da IA após 2 segundos
- private void simularAnaliseIA() {
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- // 1. Mostra a área de resultados
- layoutResultadosIA.setVisibility(View.VISIBLE);
- btnTirarFoto.setText("Tirar Outra Foto");
-
- // 2. Simula os dados detetados
- tvNomePrato.setText("Prato detetado: Salada César com Frango");
- listaIngredientes.clear();
- listaIngredientes.add(new Ingrediente("Peito de Frango Grelhado (150g)", 240));
- listaIngredientes.add(new Ingrediente("Alface Romana (200g)", 30));
- listaIngredientes.add(new Ingrediente("Molho César (2 c.sopa)", 160));
- listaIngredientes.add(new Ingrediente("Croutons (30g)", 120));
- listaIngredientes.add(new Ingrediente("Queijo Parmesão (20g)", 80));
-
- // 3. Atualiza a lista e o total
- adapter.notifyDataSetChanged();
- calcularTotal();
-
- }, 2000); // Espera 2 segundos
- }
-
- // Remove um item da lista e atualiza o total
- private void removerIngrediente(int position) {
- Ingrediente removido = listaIngredientes.get(position);
- listaIngredientes.remove(position);
- adapter.notifyItemRemoved(position);
- calcularTotal();
- Toast.makeText(this, "Removido: " + removido.getNome(), Toast.LENGTH_SHORT).show();
- }
-
- // Percorre a lista e soma as calorias
- private void calcularTotal() {
- int total = 0;
- for (Ingrediente ing : listaIngredientes) {
- total += ing.getCalorias();
- }
- tvTotalCalorias.setText(total + " kcal");
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/HomeActivity.java b/app/src/main/java/com/example/pap/HomeActivity.java
index 5dafab2..a3cf471 100644
--- a/app/src/main/java/com/example/pap/HomeActivity.java
+++ b/app/src/main/java/com/example/pap/HomeActivity.java
@@ -6,6 +6,7 @@ import android.os.Bundle;
import android.text.InputType;
import android.widget.EditText;
import android.widget.LinearLayout;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -15,58 +16,46 @@ public class HomeActivity extends AppCompatActivity {
private SharedPreferences sharedPreferences;
- // Agora são 6 cartões!
- private CardView cardPerfil, cardEstatisticas, cardTirarFoto, cardChat, cardDesafios, cardDefinicoes;
+ // Os 5 cartões principais do teu novo design
+ private CardView cardTirarFoto, cardChat, cardEstatisticas, cardDesafios, cardPerfil;
+ private TextView tvSaudacaoHome;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
- // 1. Iniciar a memória do telemóvel
- sharedPreferences = getSharedPreferences("AppEmagrecimento", MODE_PRIVATE);
+ // 1. Iniciar a memória do telemóvel (Onde guardámos os dados no Registo)
+ sharedPreferences = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
- // 2. Ligar os cartões do XML ao código
- cardPerfil = findViewById(R.id.cardPerfil);
- cardEstatisticas = findViewById(R.id.cardEstatisticas);
+ // 2. Ligar ao XML
cardTirarFoto = findViewById(R.id.cardTirarFoto);
cardChat = findViewById(R.id.cardChat);
+ cardEstatisticas = findViewById(R.id.cardEstatisticas);
cardDesafios = findViewById(R.id.cardDesafios);
- cardDefinicoes = findViewById(R.id.cardDefinicoes);
+ cardPerfil = findViewById(R.id.cardPerfil);
+ tvSaudacaoHome = findViewById(R.id.tvSaudacaoHome);
- // 3. Configurar os cliques para abrir os outros ecrãs
- cardPerfil.setOnClickListener(v -> {
- Toast.makeText(this, "A abrir O Meu Perfil...", Toast.LENGTH_SHORT).show();
- // startActivity(new Intent(HomeActivity.this, PerfilActivity.class));
- });
+ // 3. Preencher o nome do utilizador no cabeçalho
+ String nome = sharedPreferences.getString("nome", "Guerreiro");
+ tvSaudacaoHome.setText("Olá, " + nome + "! 👋");
- cardEstatisticas.setOnClickListener(v -> {
- Toast.makeText(this, "A abrir Estatísticas...", Toast.LENGTH_SHORT).show();
- // startActivity(new Intent(HomeActivity.this, EstatisticasActivity.class));
- });
+ // 4. ATIVAR OS CLIQUES (Os Intents para abrir os outros ecrãs)
+ cardTirarFoto.setOnClickListener(v -> startActivity(new Intent(HomeActivity.this, FotoActivity.class)));
- cardTirarFoto.setOnClickListener(v -> {
- startActivity(new Intent(HomeActivity.this, FotoActivity.class));
- });
+ cardChat.setOnClickListener(v -> startActivity(new Intent(HomeActivity.this, ChatActivity.class)));
- cardChat.setOnClickListener(v -> {
- startActivity(new Intent(HomeActivity.this, ChatActivity.class));
- });
+ cardEstatisticas.setOnClickListener(v -> startActivity(new Intent(HomeActivity.this, EstatisticasActivity.class)));
- cardDesafios.setOnClickListener(v -> {
- startActivity(new Intent(HomeActivity.this, DesafiosActivity.class));
- });
+ cardDesafios.setOnClickListener(v -> startActivity(new Intent(HomeActivity.this, DesafiosActivity.class)));
- cardDefinicoes.setOnClickListener(v -> {
- Toast.makeText(this, "A abrir Definições...", Toast.LENGTH_SHORT).show();
- // startActivity(new Intent(HomeActivity.this, DefinicoesActivity.class));
- });
+ cardPerfil.setOnClickListener(v -> startActivity(new Intent(HomeActivity.this, PerfilActivity.class)));
- // 4. Verifica se já passou o tempo para pedir o novo peso
+ // 5. Verifica se já passou o tempo para pedir o novo peso
verificarAtualizacaoSemanal();
}
- // Função que calcula se já passou o tempo (1 semana ou 10 segundos)
+ // Função que calcula o tempo passado
private void verificarAtualizacaoSemanal() {
long dataUltimaAtualizacao = sharedPreferences.getLong("data_ultima_atualizacao", 0);
long dataAtual = System.currentTimeMillis();
@@ -75,7 +64,7 @@ public class HomeActivity extends AppCompatActivity {
sharedPreferences.edit().putLong("data_ultima_atualizacao", dataAtual).apply();
} else {
// TEMPO CONFIGURADO PARA TESTES: 10 segundos!
- // Para a PAP final usa: long tempoNecessario = 7L * 24 * 60 * 60 * 1000;
+ // Quando fores apresentar a PAP, podes mudar isto para 1 semana.
long tempoNecessario = 10 * 1000;
if (dataAtual - dataUltimaAtualizacao >= tempoNecessario) {
@@ -84,11 +73,11 @@ public class HomeActivity extends AppCompatActivity {
}
}
- // Função que cria o pop-up para atualizar o peso
+ // Função que cria o pop-up no ecrã para atualizar peso e altura
private void mostrarPopupAtualizacao() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Hora da pesagem! ⚖️");
- builder.setMessage("Já passou 1 semana! Atualiza o teu peso e altura para acompanharmos a tua evolução.");
+ builder.setMessage("Já passou algum tempo! Atualiza o teu peso e altura para acompanharmos a tua evolução.");
builder.setCancelable(false);
LinearLayout layout = new LinearLayout(this);
@@ -119,16 +108,17 @@ public class HomeActivity extends AppCompatActivity {
float novoPeso = Float.parseFloat(pesoStr);
float novaAltura = Float.parseFloat(alturaStr);
+ // Grava os novos dados com as chaves corretas no bloco de notas
SharedPreferences.Editor editor = sharedPreferences.edit();
- editor.putFloat("pesoAtual", novoPeso);
- editor.putFloat("alturaAtual", novaAltura);
+ editor.putFloat("peso", novoPeso);
+ editor.putFloat("altura", novaAltura);
editor.putLong("data_ultima_atualizacao", System.currentTimeMillis());
editor.apply();
Toast.makeText(HomeActivity.this, "Dados atualizados com sucesso! 💪", Toast.LENGTH_SHORT).show();
dialog.dismiss();
} else {
- Toast.makeText(HomeActivity.this, "Tens de preencher os dois campos!", Toast.LENGTH_SHORT).show();
+ Toast.makeText(HomeActivity.this, "Tens de preencher os dois campos, caralho!", Toast.LENGTH_SHORT).show();
}
});
}
diff --git a/app/src/main/java/com/example/pap/LoginActivity.java b/app/src/main/java/com/example/pap/LoginActivity.java
index aa1441e..3827f07 100644
--- a/app/src/main/java/com/example/pap/LoginActivity.java
+++ b/app/src/main/java/com/example/pap/LoginActivity.java
@@ -1,8 +1,9 @@
package com.example.pap;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
-import android.view.View;
+import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@@ -12,82 +13,74 @@ import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
-import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
public class LoginActivity extends AppCompatActivity {
private EditText etLoginEmail, etLoginPassword;
private Button btnLogin;
private TextView tvGoToRegister;
- private SupabaseApi api;
+ private SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
- // 1. Inicializar os componentes
+ // Ligar o código aos IDs do ecrã
etLoginEmail = findViewById(R.id.etLoginEmail);
etLoginPassword = findViewById(R.id.etLoginPassword);
btnLogin = findViewById(R.id.btnLogin);
tvGoToRegister = findViewById(R.id.tvGoToRegister);
- // 2. Iniciar a comunicação com a API do Supabase
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl(SupabaseConfig.URL)
- .addConverterFactory(GsonConverterFactory.create())
- .build();
- api = retrofit.create(SupabaseApi.class);
-
- // 3. Configurar os cliques
- btnLogin.setOnClickListener(v -> efetuarLogin());
+ // Iniciar a memória do telemóvel
+ sharedPreferences = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+ // Enviar para o Registo
tvGoToRegister.setOnClickListener(v -> {
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
});
- }
- private void efetuarLogin() {
- String email = etLoginEmail.getText().toString().trim();
- String password = etLoginPassword.getText().toString().trim();
+ // Botão de Entrar
+ btnLogin.setOnClickListener(v -> {
+ String email = etLoginEmail.getText().toString().trim();
+ String password = etLoginPassword.getText().toString().trim();
- // Validação básica
- if (email.isEmpty() || password.isEmpty()) {
- Toast.makeText(this, "Preencha o email e a password!", Toast.LENGTH_SHORT).show();
- return;
- }
+ if (email.isEmpty() || password.isEmpty()) {
+ Toast.makeText(this, "Preenche o email e a password!", Toast.LENGTH_SHORT).show();
+ return;
+ }
- UserCredentials credentials = new UserCredentials(email, password);
+ UserCredentials credentials = new UserCredentials(email, password);
+ SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
- // Faz o pedido de Login ao Supabase
- api.login(SupabaseConfig.API_KEY, credentials).enqueue(new Callback() {
- @Override
- public void onResponse(Call call, Response response) {
- if (response.isSuccessful() && response.body() != null) {
+ api.login(SupabaseConfig.SUPABASE_KEY, credentials).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful() && response.body() != null) {
- // Login com sucesso! Vai para a Home
- Toast.makeText(LoginActivity.this, "Login com sucesso!", Toast.LENGTH_SHORT).show();
- Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
- startActivity(intent);
- finish(); // Fecha a tela de login
+ // Guardar o token e o email para usar nas Definições
+ String token = response.body().access_token;
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putString("access_token", token);
+ editor.putString("email", email);
+ editor.apply();
- } else {
- // SE DER ERRO (ex: 404, 400), MOSTRA O LINK EXATO E O CÓDIGO
- String urlQueFalhou = response.raw().request().url().toString();
- if (response.code() == 400) {
- Toast.makeText(LoginActivity.this, "Email ou password incorretos!", Toast.LENGTH_LONG).show();
+ Toast.makeText(LoginActivity.this, "Entraste com sucesso! 🚀", Toast.LENGTH_SHORT).show();
+ Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
+ startActivity(intent);
+ finish();
} else {
- Toast.makeText(LoginActivity.this, "Erro " + response.code() + "! Link: " + urlQueFalhou, Toast.LENGTH_LONG).show();
+ Toast.makeText(LoginActivity.this, "Dados errados! Verifica a tua password e email.", Toast.LENGTH_LONG).show();
+ Log.e("SUPABASE", "Erro Login: " + response.code());
}
}
- }
- @Override
- public void onFailure(Call call, Throwable t) {
- Toast.makeText(LoginActivity.this, "Falha na ligação: " + t.getMessage(), Toast.LENGTH_SHORT).show();
- }
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Toast.makeText(LoginActivity.this, "Falha na ligação! Verifica a internet.", Toast.LENGTH_LONG).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 83651e3..5f4caa8 100644
--- a/app/src/main/java/com/example/pap/PerfilActivity.java
+++ b/app/src/main/java/com/example/pap/PerfilActivity.java
@@ -1,39 +1,51 @@
package com.example.pap;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
-import android.view.View;
import android.widget.Button;
+import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class PerfilActivity extends AppCompatActivity {
+ private TextView tvPerfilNome, tvPerfilPontos, tvPerfilDesafios, tvPerfilSequencia;
+ private Button btnDefinicoes, btnVoltarPerfil;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_perfil);
- Button btnVoltar = findViewById(R.id.btnVoltarPerfil);
- Button btnSair = findViewById(R.id.btnSair);
+ tvPerfilNome = findViewById(R.id.tvPerfilNome);
+ tvPerfilPontos = findViewById(R.id.tvPerfilPontos);
+ tvPerfilDesafios = findViewById(R.id.tvPerfilDesafios);
+ tvPerfilSequencia = findViewById(R.id.tvPerfilSequencia);
+ btnDefinicoes = findViewById(R.id.btnDefinicoes);
+ btnVoltarPerfil = findViewById(R.id.btnVoltarPerfil);
- // Voltar à Home
- btnVoltar.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- }
+ btnDefinicoes.setOnClickListener(v -> {
+ startActivity(new Intent(PerfilActivity.this, DefinicoesActivity.class));
});
- // Terminar Sessão (Voltar ao Login e limpar a Home)
- btnSair.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(PerfilActivity.this, LoginActivity.class);
- // Estas flags garantem que o utilizador não consegue clicar em "Voltar" no telemóvel e ir para a Home de novo
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- startActivity(intent);
- finish();
- }
- });
+ btnVoltarPerfil.setOnClickListener(v -> finish());
+ }
+
+ // A MAGIA ACONTECE AQUI: Atualiza os dados sempre que o ecrã aparece!
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ SharedPreferences prefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+
+ String nome = prefs.getString("nome", "Utilizador");
+ int pontos = prefs.getInt("pontos", 0);
+ int desafios = prefs.getInt("desafios_concluidos", 0);
+ int streak = prefs.getInt("sequencia_diaria", 1);
+
+ tvPerfilNome.setText(nome);
+ tvPerfilPontos.setText(String.valueOf(pontos));
+ tvPerfilDesafios.setText(String.valueOf(desafios));
+ tvPerfilSequencia.setText(String.valueOf(streak));
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/RegisterActivity.java b/app/src/main/java/com/example/pap/RegisterActivity.java
index ce9e85e..908430d 100644
--- a/app/src/main/java/com/example/pap/RegisterActivity.java
+++ b/app/src/main/java/com/example/pap/RegisterActivity.java
@@ -1,8 +1,9 @@
package com.example.pap;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
-import android.view.View;
+import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@@ -12,22 +13,19 @@ import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
-import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
public class RegisterActivity extends AppCompatActivity {
private EditText etRegNome, etRegEmail, etRegPassword, etRegAltura, etRegPeso;
private Button btnRegister;
private TextView tvGoToLogin;
- private SupabaseApi api;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
- // 1. Ligar os elementos do design ao código
+ // Ligar o código aos IDs do ecrã
etRegNome = findViewById(R.id.etRegNome);
etRegEmail = findViewById(R.id.etRegEmail);
etRegPassword = findViewById(R.id.etRegPassword);
@@ -36,99 +34,63 @@ public class RegisterActivity extends AppCompatActivity {
btnRegister = findViewById(R.id.btnRegister);
tvGoToLogin = findViewById(R.id.tvGoToLogin);
- // 2. Configurar o Retrofit (A ponte de comunicação com o Supabase)
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl(SupabaseConfig.URL)
- .addConverterFactory(GsonConverterFactory.create())
- .build();
- api = retrofit.create(SupabaseApi.class);
-
- // 3. Configurar os cliques dos botões
- btnRegister.setOnClickListener(v -> efetuarRegisto());
+ // Se o utilizador já tem conta, volta para o Login
tvGoToLogin.setOnClickListener(v -> finish());
- }
- private void efetuarRegisto() {
- String nome = etRegNome.getText().toString().trim();
- String email = etRegEmail.getText().toString().trim();
- String password = etRegPassword.getText().toString().trim();
- String pesoStr = etRegPeso.getText().toString().trim();
- String alturaStr = etRegAltura.getText().toString().trim();
+ // Quando clica no botão de Registar
+ btnRegister.setOnClickListener(v -> {
+ String nome = etRegNome.getText().toString().trim();
+ String email = etRegEmail.getText().toString().trim();
+ String password = etRegPassword.getText().toString().trim();
+ String alturaStr = etRegAltura.getText().toString().trim();
+ String pesoStr = etRegPeso.getText().toString().trim();
- // Verificações para garantir que o utilizador preencheu tudo
- if (nome.isEmpty() || email.isEmpty() || password.isEmpty() || pesoStr.isEmpty() || alturaStr.isEmpty()) {
- Toast.makeText(this, "Preencha todos os campos!", Toast.LENGTH_SHORT).show();
- return;
- }
+ // 1. Verificar se não há campos vazios
+ if (nome.isEmpty() || email.isEmpty() || password.isEmpty() || alturaStr.isEmpty() || pesoStr.isEmpty()) {
+ Toast.makeText(this, "Por favor, preenche todos os campos!", Toast.LENGTH_SHORT).show();
+ return;
+ }
- if (password.length() < 6) {
- Toast.makeText(this, "A password precisa de pelo menos 6 caracteres.", Toast.LENGTH_SHORT).show();
- return;
- }
+ // 2. GUARDAR NOME, ALTURA E PESO NA MEMÓRIA (SharedPreferences)
+ SharedPreferences prefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString("nome", nome);
+ editor.putString("email", email);
+ editor.putFloat("altura", Float.parseFloat(alturaStr));
+ editor.putFloat("peso", Float.parseFloat(pesoStr));
+ editor.apply();
- float peso = Float.parseFloat(pesoStr);
- float altura = Float.parseFloat(alturaStr);
+ // 3. Preparar os dados para enviar para o Supabase
+ UserCredentials credentials = new UserCredentials(email, password);
- // FASE 1: Criar a conta (Email e Password) no Supabase Auth
- UserCredentials credentials = new UserCredentials(email, password);
+ // Vai buscar o Retrofit ao ficheiro SupabaseConfig
+ SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
- api.signUp(SupabaseConfig.API_KEY, credentials).enqueue(new Callback() {
- @Override
- public void onResponse(Call call, Response response) {
- if (response.isSuccessful() && response.body() != null) {
+ // 4. Fazer o Registo na Internet usando a chave configurada
+ api.signUp(SupabaseConfig.SUPABASE_KEY, credentials).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ Toast.makeText(RegisterActivity.this, "Conta criada! Verifica o teu email.", Toast.LENGTH_LONG).show();
- // Sucesso! A conta foi criada. Vamos extrair o ID e o Token
- String userId = response.body().id != null ? response.body().id : response.body().user.id;
- String token = "Bearer " + response.body().access_token;
+ // NOVO FLUXO: Mandar para o ecrã de espera com os dados passados em segurança!
+ Intent intent = new Intent(RegisterActivity.this, VerificacaoActivity.class);
+ intent.putExtra("email_registo", email);
+ intent.putExtra("password_registo", password);
+ startActivity(intent);
+ finish();
- // FASE 2: Guardar o Nome, Peso e Altura na tabela "profiles"
- salvarPerfil(userId, nome, email, peso, altura, token);
-
- } else {
- String urlQueFalhou = response.raw().request().url().toString();
- if (response.code() == 400) {
- Toast.makeText(RegisterActivity.this, "Erro 400: Este email já existe ou a password é fraca.", Toast.LENGTH_LONG).show();
} else {
- Toast.makeText(RegisterActivity.this, "Erro " + response.code() + " no Auth! Link: " + urlQueFalhou, Toast.LENGTH_LONG).show();
+ Toast.makeText(RegisterActivity.this, "Erro ao criar conta. O email já existe?", Toast.LENGTH_LONG).show();
}
}
- }
- @Override
- public void onFailure(Call call, Throwable t) {
- Toast.makeText(RegisterActivity.this, "Falha de rede (Sem internet?): " + t.getMessage(), Toast.LENGTH_SHORT).show();
- }
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ // Sem internet ou falha de ligação
+ Toast.makeText(RegisterActivity.this, "Falha na ligação! Verifica a tua internet.", Toast.LENGTH_LONG).show();
+ }
+ });
});
}
-
- // Método responsável por guardar os dados extras na tabela profiles
- private void salvarPerfil(String id, String nome, String email, float peso, float altura, String token) {
- ProfileData profile = new ProfileData(id, nome, email, peso, altura);
-
- api.insertProfile(SupabaseConfig.API_KEY, token, "application/json", "return=minimal", profile)
- .enqueue(new Callback() {
- @Override
- public void onResponse(Call call, Response response) {
- if (response.isSuccessful()) {
- // TUDO CORREU BEM!
- Toast.makeText(RegisterActivity.this, "Conta e Perfil criados com sucesso!", Toast.LENGTH_LONG).show();
- finish(); // Volta para o ecrã de login
- } else {
- // DEU ERRO A GUARDAR O PESO E ALTURA. VAMOS LER O ERRO EXATO!
- try {
- String erroExato = response.errorBody().string();
- // Esta mensagem vai mostrar-te o que está mal configurado no painel do Supabase
- Toast.makeText(RegisterActivity.this, "ERRO DA BASE DE DADOS: " + erroExato, Toast.LENGTH_LONG).show();
- } catch (Exception e) {
- Toast.makeText(RegisterActivity.this, "Erro a ler a resposta da base de dados.", Toast.LENGTH_SHORT).show();
- }
- }
- }
-
- @Override
- public void onFailure(Call call, Throwable t) {
- Toast.makeText(RegisterActivity.this, "Erro ao conectar à base de dados.", Toast.LENGTH_SHORT).show();
- }
- });
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/SupabaseApi.java b/app/src/main/java/com/example/pap/SupabaseApi.java
index f832fd1..2604e8c 100644
--- a/app/src/main/java/com/example/pap/SupabaseApi.java
+++ b/app/src/main/java/com/example/pap/SupabaseApi.java
@@ -3,30 +3,29 @@ package com.example.pap;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Header;
+import retrofit2.http.PUT;
import retrofit2.http.POST;
public interface SupabaseApi {
- // Rota para criar a conta
+ // 1. Rota para criar a conta
@POST("auth/v1/signup")
Call signUp(@Header("apikey") String apiKey, @Body UserCredentials credentials);
- // Rota para fazer login
+ // 2. Rota para fazer login
@POST("auth/v1/token?grant_type=password")
Call login(@Header("apikey") String apiKey, @Body UserCredentials credentials);
- // Rota para guardar o Nome, Peso e Altura na base de dados
- @POST("rest/v1/profiles")
- Call insertProfile(
- @Header("apikey") String apiKey,
- @Header("Authorization") String token,
- @Header("Content-Type") String contentType,
- @Header("Prefer") String prefer,
- @Body ProfileData profile
+ // 3. Rota para atualizar a palavra-passe ou dados do utilizador (CORRIGIDO PARA @PUT)
+ @PUT("auth/v1/user")
+ Call updateUserData(
+ @Header("apikey") String apikey,
+ @Header("Authorization") String accessToken,
+ @Body java.util.Map updates
);
}
-// ---- Classes Auxiliares para Formatarem os Dados ----
+// ---- Classes Auxiliares ----
class UserCredentials {
String email;
@@ -38,28 +37,6 @@ class UserCredentials {
}
}
-class ProfileData {
- String id;
- String nome;
- String email;
- float peso;
- float altura;
-
- public ProfileData(String id, String nome, String email, float peso, float altura) {
- this.id = id;
- this.nome = nome;
- this.email = email;
- this.peso = peso;
- this.altura = altura;
- }
-}
-
class SupabaseResponse {
- String id;
- String access_token;
- UserResponse user;
-
- class UserResponse {
- String id;
- }
+ public String access_token;
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/SupabaseConfig.java b/app/src/main/java/com/example/pap/SupabaseConfig.java
index 5393356..c712656 100644
--- a/app/src/main/java/com/example/pap/SupabaseConfig.java
+++ b/app/src/main/java/com/example/pap/SupabaseConfig.java
@@ -1,10 +1,31 @@
package com.example.pap;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
public class SupabaseConfig {
- // O URL limpo, apenas com a barra no final!
- public static final String URL = "https://lkjbbbgavoyknuxaskho.supabase.co";
+ // =========================================================================
+ // CONFIGURAÇÃO DO NOVO PROJETO (SUBSTITUI PELOS TEUS DADOS DO SUPABASE)
+ // =========================================================================
- // A tua chave está perfeita
- public static final String API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxramJiYmdhdm95a251eGFza2hvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzI2MjM4OTMsImV4cCI6MjA4ODE5OTg5M30.HV9ZXYCaF1V8dZwPxv_p5_gDi9cN_ioumDm9mgmEQPU";
-}
+ // 1. O teu Project URL (Ex: https://xyzabc.supabase.co/)
+ public static final String BASE_URL = "https://zfwfimmptccyzxvscbrg.supabase.co/";
+ // 2. A tua API Key (A que diz 'anon' e 'public')
+ public static final String SUPABASE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inpmd2ZpbW1wdGNjeXp4dnNjYnJnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzcyODQ5MDMsImV4cCI6MjA5Mjg2MDkwM30.aE_JSy6OGx3aPfzraCCQ_CtQeA8qFSBGhgkaGBJDHos";
+
+ // =========================================================================
+
+ private static Retrofit retrofit = null;
+
+ // Método que cria e entrega a ligação configurada para o resto da App
+ public static Retrofit getRetrofit() {
+ if (retrofit == null) {
+ retrofit = new Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .addConverterFactory(GsonConverterFactory.create()) // Transforma JSON em código Java
+ .build();
+ }
+ return retrofit;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/VerificacaoActivity.java b/app/src/main/java/com/example/pap/VerificacaoActivity.java
new file mode 100644
index 0000000..57d88b2
--- /dev/null
+++ b/app/src/main/java/com/example/pap/VerificacaoActivity.java
@@ -0,0 +1,75 @@
+package com.example.pap;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class VerificacaoActivity extends AppCompatActivity {
+
+ private Button btnJaConfirmei, btnVoltarLoginVerificacao;
+ private String emailGuardado, passwordGuardada;
+ private SharedPreferences sharedPreferences;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_verificacao);
+
+ btnJaConfirmei = findViewById(R.id.btnJaConfirmei);
+ btnVoltarLoginVerificacao = findViewById(R.id.btnVoltarLoginVerificacao);
+ sharedPreferences = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
+
+ // Receber o email e a password do ecrã de Registo
+ emailGuardado = getIntent().getStringExtra("email_registo");
+ passwordGuardada = getIntent().getStringExtra("password_registo");
+
+ btnJaConfirmei.setOnClickListener(v -> {
+ Toast.makeText(this, "A verificar...", Toast.LENGTH_SHORT).show();
+ tentarFazerLogin();
+ });
+
+ btnVoltarLoginVerificacao.setOnClickListener(v -> {
+ startActivity(new Intent(VerificacaoActivity.this, LoginActivity.class));
+ finish();
+ });
+ }
+
+ private void tentarFazerLogin() {
+ SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
+ UserCredentials creds = new UserCredentials(emailGuardado, passwordGuardada);
+
+ api.login(SupabaseConfig.SUPABASE_KEY, creds).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful() && response.body() != null) {
+ // SUCESSO! O email foi confirmado e o login funcionou
+ String token = response.body().access_token;
+
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putString("access_token", token);
+ editor.putString("email", emailGuardado);
+ editor.apply();
+
+ Toast.makeText(VerificacaoActivity.this, "Email confirmado com sucesso! Bem-vindo!", Toast.LENGTH_LONG).show();
+ startActivity(new Intent(VerificacaoActivity.this, HomeActivity.class));
+ finish();
+ } else {
+ // ERRO! Provavelmente o gajo ainda não clicou no link do email
+ Toast.makeText(VerificacaoActivity.this, "Ainda não confirmaste! Vai ao teu email e clica no link.", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Toast.makeText(VerificacaoActivity.this, "Sem ligação à internet!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/balao_ai.xml b/app/src/main/res/drawable/balao_ai.xml
new file mode 100644
index 0000000..ac98348
--- /dev/null
+++ b/app/src/main/res/drawable/balao_ai.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/balao_user.xml b/app/src/main/res/drawable/balao_user.xml
new file mode 100644
index 0000000..db55b4b
--- /dev/null
+++ b/app/src/main/res/drawable/balao_user.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/fundo_input_ingredientes.xml b/app/src/main/res/drawable/fundo_input_ingredientes.xml
new file mode 100644
index 0000000..0573399
--- /dev/null
+++ b/app/src/main/res/drawable/fundo_input_ingredientes.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_online_dot.xml b/app/src/main/res/drawable/ic_online_dot.xml
new file mode 100644
index 0000000..1c35696
--- /dev/null
+++ b/app/src/main/res/drawable/ic_online_dot.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/retangulo_mira.xml b/app/src/main/res/drawable/retangulo_mira.xml
new file mode 100644
index 0000000..42de42b
--- /dev/null
+++ b/app/src/main/res/drawable/retangulo_mira.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml
index 29894ac..412e7b7 100644
--- a/app/src/main/res/layout/activity_chat.xml
+++ b/app/src/main/res/layout/activity_chat.xml
@@ -1,83 +1,148 @@
+ android:background="#F8FAFC">
+ android:orientation="horizontal">
-
+
+
+
-
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+ android:paddingHorizontal="16dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:clipToPadding="false">
+ android:orientation="vertical" />
-
+ app:cardCornerRadius="24dp"
+ app:cardElevation="2dp"
+ app:cardBackgroundColor="#FFFFFF"
+ android:layout_marginEnd="8dp">
+
+
+
+
+
+
+
+
-
\ 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
new file mode 100644
index 0000000..5c9e5fa
--- /dev/null
+++ b/app/src/main/res/layout/activity_definicoes.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_desafios.xml b/app/src/main/res/layout/activity_desafios.xml
index bf52ad3..9071a49 100644
--- a/app/src/main/res/layout/activity_desafios.xml
+++ b/app/src/main/res/layout/activity_desafios.xml
@@ -1,149 +1,156 @@
-
+ android:orientation="vertical"
+ android:background="#F1F5F9">
-
-
-
+ app:cardElevation="4dp"
+ app:cardCornerRadius="0dp"
+ app:cardBackgroundColor="#03A9F4">
+ android:orientation="vertical"
+ android:padding="24dp"
+ android:gravity="center">
-
-
+ android:text="Os Teus Pontos"
+ android:textColor="#E0F2FE"
+ android:textSize="14sp"
+ android:textStyle="bold"/>
+ android:textColor="#FFFFFF"
+ android:textSize="48sp"
+ android:textStyle="bold"/>
+
+
+
+ android:orientation="vertical">
+ android:textColor="#1E293B"
+ android:layout_marginBottom="16dp"/>
-
-
-
-
+ app:cardCornerRadius="16dp"
+ android:layout_marginBottom="16dp"
+ app:cardElevation="2dp">
-
+
-
+
-
+
-
+
+
+
+
+ app:cardCornerRadius="16dp"
+ android:layout_marginBottom="16dp"
+ app:cardElevation="2dp">
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_editar_perfil.xml b/app/src/main/res/layout/activity_editar_perfil.xml
new file mode 100644
index 0000000..0f02f1c
--- /dev/null
+++ b/app/src/main/res/layout/activity_editar_perfil.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_estatisticas.xml b/app/src/main/res/layout/activity_estatisticas.xml
index e558c20..ea20870 100644
--- a/app/src/main/res/layout/activity_estatisticas.xml
+++ b/app/src/main/res/layout/activity_estatisticas.xml
@@ -1,82 +1,145 @@
+ android:background="#F8FAFC">
+ android:textColor="#1E293B"
+ android:padding="20dp"
+ android:background="#FFFFFF"
+ android:elevation="4dp"/>
-
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:padding="16dp">
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:backgroundTint="#94A3B8"
+ android:textColor="#FFFFFF"
+ app:cornerRadius="12dp"/>
\ 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 b0cc3e5..af80ae8 100644
--- a/app/src/main/res/layout/activity_foto.xml
+++ b/app/src/main/res/layout/activity_foto.xml
@@ -1,129 +1,106 @@
-
+ android:orientation="vertical"
+ android:background="#F8FAFC">
+
+ android:padding="20dp"
+ android:background="#FFFFFF"
+ android:elevation="4dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ android:textColor="#0F172A"/>
+
-
-
-
-
-
+
+ android:orientation="vertical">
-
-
-
+
+ android:layout_height="250dp"
+ app:cardCornerRadius="16dp"
+ app:cardElevation="2dp"
+ android:layout_marginBottom="24dp">
-
-
-
-
-
-
+
+
+
-
-
+ android:backgroundTint="#0284C7"
+ app:cornerRadius="15dp"
+ android:layout_marginBottom="12dp"/>
+
+
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
+
+
\ 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 5e90290..e72d2f9 100644
--- a/app/src/main/res/layout/activity_home.xml
+++ b/app/src/main/res/layout/activity_home.xml
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#F8FAFA"
+ android:background="#F8FAFC"
android:fillViewport="true">
+ android:text="A tua jornada começa aqui,"
+ android:textSize="14sp"
+ android:textColor="#64748B"
+ android:layout_marginTop="8dp"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ app:cardCornerRadius="20dp"
+ app:cardElevation="6dp"
+ app:cardBackgroundColor="#0F172A"
+ app:contentPadding="20dp">
@@ -199,16 +72,16 @@
+ android:textColor="#FFFFFF"/>
@@ -216,8 +89,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="›"
- android:textSize="28sp"
- android:textColor="#D1D5DB"
+ android:textSize="32sp"
+ android:textColor="#38BDF8"
android:layout_marginStart="8dp"/>
@@ -244,7 +117,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
app:cardCornerRadius="12dp"
- app:cardBackgroundColor="#00BFA5"
+ app:cardBackgroundColor="#10B981"
app:cardElevation="0dp">
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginStart="16dp">
+
+
+
+
+
@@ -308,7 +209,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
app:cardCornerRadius="12dp"
- app:cardBackgroundColor="#FF5722"
+ app:cardBackgroundColor="#F59E0B"
app:cardElevation="0dp">
-
-
+
+
-
+
@@ -388,29 +271,11 @@
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="16dp">
-
-
+
+
-
+
diff --git a/app/src/main/res/layout/activity_perfil.xml b/app/src/main/res/layout/activity_perfil.xml
index fbbc9ec..62e49b2 100644
--- a/app/src/main/res/layout/activity_perfil.xml
+++ b/app/src/main/res/layout/activity_perfil.xml
@@ -1,80 +1,189 @@
-
-
-
-
+ android:background="#F8FAFC">
+ android:elevation="4dp">
-
-
-
-
+ android:text="O Meu Perfil"
+ android:textSize="22sp"
+ android:textStyle="bold"
+ android:textColor="#0F172A"/>
-
+ android:layout_height="match_parent"
+ android:padding="24dp">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_verificacao.xml b/app/src/main/res/layout/activity_verificacao.xml
new file mode 100644
index 0000000..af3fc78
--- /dev/null
+++ b/app/src/main/res/layout/activity_verificacao.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file