diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..5833da7 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/example/pap/ChatActivity.java b/app/src/main/java/com/example/pap/ChatActivity.java
index 2e57238..5b7f3c4 100644
--- a/app/src/main/java/com/example/pap/ChatActivity.java
+++ b/app/src/main/java/com/example/pap/ChatActivity.java
@@ -1,168 +1,87 @@
package com.example.pap;
-import android.graphics.Color;
+import android.content.Intent;
import android.os.Bundle;
-import android.view.Gravity;
+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;
+import java.util.Collections;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
public class ChatActivity extends AppCompatActivity {
+ private TextView tvChatLog;
private EditText etMensagem;
- private ImageButton btnEnviar;
- private LinearLayout chatLayout;
- private ScrollView chatScrollView;
+ private Button btnEnviar;
+ private TextView 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;
+ // NÃO TE ESQUEÇAS DE COLAR A TUA CHAVE AQUI!
+ private final String MINHA_API_KEY = "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";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
+ tvChatLog = findViewById(R.id.tvChatLog);
etMensagem = findViewById(R.id.etMensagem);
- btnEnviar = findViewById(R.id.btnEnviar);
- chatLayout = findViewById(R.id.chatLayout);
- chatScrollView = findViewById(R.id.chatScrollView);
+ btnEnviar = findViewById(R.id.btnEnviarChat);
+ btnVoltarChat = findViewById(R.id.btnVoltarChat);
- client = new OkHttpClient();
+ // --- LÓGICA DO BOTÃO VOLTAR PARA O HOME ---
+ btnVoltarChat.setOnClickListener(v -> {
+ // Cria a intenção de ir para a MainActivity (Home)
+ Intent intent = new Intent(ChatActivity.this, HomeActivity.class);
+ // Esta linha garante que não ficas com mil ecrãs abertos uns por cima dos outros
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ finish(); // Fecha o Chat
+ });
- // Dá as boas vindas
- adicionarBalaoNoEcra("Olá! Sou o NutriChat AI 🤖. O que comeste hoje ou que dúvidas tens sobre a tua dieta?", false);
+ // Receber a análise da foto se existir
+ String analiseComida = getIntent().getStringExtra("analise_comida");
+ if (analiseComida != null && !analiseComida.isEmpty()) {
+ tvChatLog.setText("IA: Analisei o teu prato.\n" + analiseComida + "\n\nO que queres saber mais?");
+ }
btnEnviar.setOnClickListener(v -> {
- String pergunta = etMensagem.getText().toString().trim();
- if (pergunta.isEmpty()) {
- Toast.makeText(this, "Escreve alguma merda primeiro!", Toast.LENGTH_SHORT).show();
- return;
+ String pergunta = etMensagem.getText().toString();
+ if (!pergunta.isEmpty()) {
+ tvChatLog.append("\n\nTu: " + pergunta);
+ etMensagem.setText("");
+ perguntarIA(pergunta);
}
-
- // 1. Mostrar a mensagem do utilizador no ecrã
- adicionarBalaoNoEcra(pergunta, true);
- etMensagem.setText(""); // Limpa a caixa de texto
-
- // 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"));
+ private void perguntarIA(String texto) {
+ tvChatLog.append("\n\nIA: A pensar... ⏳");
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- params.setMargins(0, 10, 0, 10);
+ AiRequest request = new AiRequest(java.util.Arrays.asList(
+ new Message("system", Collections.singletonList(new ContentPart("text", "És um nutricionista de Portugal. Responde SEMPRE de forma muito curta (máximo 2 frases). Nunca uses asteriscos."))),
+ new Message("user", Collections.singletonList(new ContentPart("text", texto)))
+ ));
- // 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);
- }
-
- @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");
-
- // Mostrar a resposta no ecrã
- adicionarBalaoNoEcra(respostaIA, false);
-
- } catch (Exception e) {
- adicionarBalaoNoEcra("Deu um nó no meu cérebro ao ler os dados. 🤯", false);
+ AiConfig.getRetrofit().create(AiApi.class)
+ .analisarImagem("Bearer " + MINHA_API_KEY, request)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful() && response.body() != null) {
+ String resposta = response.body().choices.get(0).message.content;
+ String limpa = resposta.replace("**", "").replace("*", "");
+ String atual = tvChatLog.getText().toString();
+ tvChatLog.setText(atual.replace("IA: A pensar... ⏳", "IA: " + limpa));
}
- } else {
- adicionarBalaoNoEcra("Erro da Google: " + response.code(), false);
}
- }
- });
-
- } catch (Exception e) {
- e.printStackTrace();
- }
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ String atual = tvChatLog.getText().toString();
+ tvChatLog.setText(atual.replace("IA: A pensar... ⏳", "IA: Erro de rede."));
+ }
+ });
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/DesafiosActivity.java b/app/src/main/java/com/example/pap/DesafiosActivity.java
index 7080206..2e52284 100644
--- a/app/src/main/java/com/example/pap/DesafiosActivity.java
+++ b/app/src/main/java/com/example/pap/DesafiosActivity.java
@@ -1,147 +1,81 @@
package com.example.pap;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.Color;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.provider.MediaStore;
-import android.view.View;
import android.widget.Button;
+import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
public class DesafiosActivity extends AppCompatActivity {
- private TextView tvPontos, tvStatusDesafio1, tvStatusDesafio2;
- private Button btnVideoDesafio1, btnVideoDesafio2, btnVoltarDesafios;
-
- private int pontosAtuais = 0;
- private SharedPreferences sharedPreferences;
-
- // Variável para saber qual desafio está a ser gravado no momento
- private int desafioAtualEmGravacao = -1;
-
- // Ferramenta para abrir a câmara de vídeo e receber o resultado
- private final ActivityResultLauncher videoLauncher = registerForActivityResult(
- new ActivityResultContracts.StartActivityForResult(),
- result -> {
- if (result.getResultCode() == RESULT_OK) {
- // O utilizador gravou o vídeo com sucesso!
- Toast.makeText(this, "Vídeo capturado! A enviar para a IA...", Toast.LENGTH_SHORT).show();
- simularAnaliseDaIA(desafioAtualEmGravacao);
- } else {
- Toast.makeText(this, "Gravação cancelada.", Toast.LENGTH_SHORT).show();
- }
- }
- );
+ private ProgressBar progressAgua;
+ private TextView tvStatusAgua;
+ private int coposBebidos = 3; // Simulação de progresso atual
+ private final int META_COPOS = 8;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_desafios);
- // 1. Inicializar os componentes
- tvPontos = findViewById(R.id.tvPontos);
- tvStatusDesafio1 = findViewById(R.id.tvStatusDesafio1);
- tvStatusDesafio2 = findViewById(R.id.tvStatusDesafio2);
- btnVideoDesafio1 = findViewById(R.id.btnVideoDesafio1);
- btnVideoDesafio2 = findViewById(R.id.btnVideoDesafio2);
- btnVoltarDesafios = findViewById(R.id.btnVoltarDesafios);
+ progressAgua = findViewById(R.id.progressAgua);
+ tvStatusAgua = findViewById(R.id.tvStatusAgua);
+ Button btnGravarAgua = findViewById(R.id.btnGravarAgua);
+ Button btnEx1 = findViewById(R.id.btnGravarEx1);
+ Button btnEx2 = findViewById(R.id.btnGravarEx2);
- // 2. Carregar os dados guardados no telemóvel
- sharedPreferences = getSharedPreferences("AppEmagrecimento", MODE_PRIVATE);
- carregarDadosGuardados();
+ // Launcher para capturar VÍDEO
+ ActivityResultLauncher videoLauncher = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ if (result.getResultCode() == RESULT_OK) {
+ // AQUI É ONDE A IA ENTRARIA PARA ANALISAR O VÍDEO
+ validarDesafioIA();
+ }
+ });
- // 3. Configurar os cliques para gravar vídeo
- btnVideoDesafio1.setOnClickListener(v -> abrirCameraVideo(1));
- btnVideoDesafio2.setOnClickListener(v -> abrirCameraVideo(2));
-
- // 4. Botão de voltar
- btnVoltarDesafios.setOnClickListener(v -> finish());
- }
-
- // Função para abrir a câmara do telemóvel em modo VÍDEO
- private void abrirCameraVideo(int numeroDoDesafio) {
- desafioAtualEmGravacao = numeroDoDesafio;
- Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
-
- try {
+ // Configurar botões para abrir a câmara em modo VÍDEO
+ btnGravarAgua.setOnClickListener(v -> {
+ Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+ intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10); // Limita a 10 segundos para ser rápido
videoLauncher.launch(intent);
- } catch (ActivityNotFoundException e) {
- // Caso o emulador não tenha uma câmara configurada, abre a galeria para selecionar um vídeo
- Toast.makeText(this, "Câmara não encontrada. Selecione um vídeo da galeria.", Toast.LENGTH_LONG).show();
- Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
- galleryIntent.setType("video/*");
- videoLauncher.launch(galleryIntent);
- }
+ });
+
+ btnEx1.setOnClickListener(v -> {
+ Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+ videoLauncher.launch(intent);
+ });
+
+ btnEx2.setOnClickListener(v -> {
+ Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+ videoLauncher.launch(intent);
+ });
+
+ findViewById(R.id.btnVoltarDesafios).setOnClickListener(v -> finish());
}
- // Função que simula o tempo de pensamento da Inteligência Artificial
- private void simularAnaliseDaIA(int numeroDoDesafio) {
- TextView tvStatusAtual = (numeroDoDesafio == 1) ? tvStatusDesafio1 : tvStatusDesafio2;
- Button btnAtual = (numeroDoDesafio == 1) ? btnVideoDesafio1 : btnVideoDesafio2;
+ private void validarDesafioIA() {
+ // Simulação: A IA demora 2 segundos a processar e diz que está OK
+ Toast.makeText(this, "IA a analisar movimento...", Toast.LENGTH_SHORT).show();
- // Muda a interface para mostrar que a IA está a trabalhar
- tvStatusAtual.setText("A IA está a analisar os teus movimentos...");
- tvStatusAtual.setTextColor(Color.parseColor("#2196F3")); // Azul
- btnAtual.setEnabled(false);
- btnAtual.setText("A processar...");
+ progressAgua.postDelayed(() -> {
+ coposBebidos++;
+ if (coposBebidos > META_COPOS) coposBebidos = META_COPOS;
- // Simula uma espera de 3.5 segundos (tempo que a IA demoraria a analisar o vídeo)
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ atualizarProgressoAgua();
+ Toast.makeText(this, "Desafio Validado! +10 pontos", Toast.LENGTH_LONG).show();
- // Depois de 3.5 segundos, a IA aprova o desafio!
- int pontosGanhos = (numeroDoDesafio == 1) ? 20 : 30;
-
- // Atualizar UI
- tvStatusAtual.setText("Aprovado pela IA! +" + pontosGanhos + " pts");
- tvStatusAtual.setTextColor(Color.parseColor("#4CAF50")); // Verde
- btnAtual.setText("Desafio Concluído");
- btnAtual.setBackgroundTintList(android.content.res.ColorStateList.valueOf(Color.parseColor("#81C784")));
-
- // Adicionar pontos e guardar
- adicionarPontosEGuardar(numeroDoDesafio, pontosGanhos);
-
- }, 3500); // 3500 milissegundos = 3.5 segundos
+ // Aqui depois vamos enviar os pontos para o Perfil
+ }, 2000);
}
- private void adicionarPontosEGuardar(int numeroDoDesafio, int pontos) {
- pontosAtuais += pontos;
- tvPontos.setText(String.valueOf(pontosAtuais));
-
- SharedPreferences.Editor editor = sharedPreferences.edit();
- editor.putInt("pontosTotais", pontosAtuais);
- editor.putBoolean("desafio_" + numeroDoDesafio + "_concluido", true);
- editor.apply();
- }
-
- private void carregarDadosGuardados() {
- pontosAtuais = sharedPreferences.getInt("pontosTotais", 0);
- tvPontos.setText(String.valueOf(pontosAtuais));
-
- // Verificar se o Desafio 1 já foi feito anteriormente
- if (sharedPreferences.getBoolean("desafio_1_concluido", false)) {
- tvStatusDesafio1.setText("Aprovado pela IA!");
- tvStatusDesafio1.setTextColor(Color.parseColor("#4CAF50"));
- btnVideoDesafio1.setEnabled(false);
- btnVideoDesafio1.setText("Desafio Concluído");
- btnVideoDesafio1.setBackgroundTintList(android.content.res.ColorStateList.valueOf(Color.parseColor("#81C784")));
- }
-
- // Verificar se o Desafio 2 já foi feito anteriormente
- if (sharedPreferences.getBoolean("desafio_2_concluido", false)) {
- tvStatusDesafio2.setText("Aprovado pela IA!");
- tvStatusDesafio2.setTextColor(Color.parseColor("#4CAF50"));
- btnVideoDesafio2.setEnabled(false);
- btnVideoDesafio2.setText("Desafio Concluído");
- btnVideoDesafio2.setBackgroundTintList(android.content.res.ColorStateList.valueOf(Color.parseColor("#81C784")));
- }
+ private void atualizarProgressoAgua() {
+ progressAgua.setProgress(coposBebidos);
+ tvStatusAgua.setText(coposBebidos + " de " + META_COPOS + " copos (" + (coposBebidos * 250) + "ml / 2L)");
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap/EstatisticasActivity.java b/app/src/main/java/com/example/pap/EstatisticasActivity.java
index 2bd6fdd..365369c 100644
--- a/app/src/main/java/com/example/pap/EstatisticasActivity.java
+++ b/app/src/main/java/com/example/pap/EstatisticasActivity.java
@@ -1,8 +1,7 @@
package com.example.pap;
import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
+import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class EstatisticasActivity extends AppCompatActivity {
@@ -12,15 +11,8 @@ public class EstatisticasActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_estatisticas);
- Button btnVoltarHome = findViewById(R.id.btnVoltarHome);
-
- // Ao clicar no botão voltar, a janela de estatísticas fecha
- // e o utilizador regressa naturalmente à HomeActivity que estava por baixo
- btnVoltarHome.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- }
- });
+ // Funcionalidade do botão de voltar
+ TextView btnVoltarStats = findViewById(R.id.btnVoltarStats);
+ btnVoltarStats.setOnClickListener(v -> finish());
}
}
\ 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 cfdaddb..46a0876 100644
--- a/app/src/main/java/com/example/pap/FotoActivity.java
+++ b/app/src/main/java/com/example/pap/FotoActivity.java
@@ -2,6 +2,9 @@ package com.example.pap;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
+import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Base64;
@@ -14,6 +17,7 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.Collections;
import retrofit2.Call;
@@ -23,11 +27,12 @@ import retrofit2.Response;
public class FotoActivity extends AppCompatActivity {
private ImageView ivFotoComida;
- private Button btnTirarFoto, btnAnalisarIA, btnVoltarFoto;
+ private Button btnTirarFoto, btnGaleria, btnAnalisarIA, btnIrParaChat;
private TextView tvResultadoIA;
private Bitmap imagemCapturada;
+ private String textoAnalise = "";
- // COLA A TUA CHAVE DO OPENROUTER AQUI (Aquela que tiraste sem telemóvel e sem VPN)
+ // COLA A TUA CHAVE AQUI
private final String MINHA_API_KEY = "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";
@Override
@@ -37,72 +42,114 @@ public class FotoActivity extends AppCompatActivity {
ivFotoComida = findViewById(R.id.ivFotoComida);
btnTirarFoto = findViewById(R.id.btnTirarFoto);
+ btnGaleria = findViewById(R.id.btnGaleria);
btnAnalisarIA = findViewById(R.id.btnAnalisarIA);
+ btnIrParaChat = findViewById(R.id.btnIrParaChat);
tvResultadoIA = findViewById(R.id.tvResultadoIA);
- btnVoltarFoto = findViewById(R.id.btnVoltarFoto);
- ActivityResultLauncher cameraLauncher = registerForActivityResult(
+ ActivityResultLauncher camLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
Bundle extras = result.getData().getExtras();
- imagemCapturada = (Bitmap) extras.get("data");
-
- ivFotoComida.setPadding(0, 0, 0, 0);
- ivFotoComida.setImageBitmap(imagemCapturada);
-
- btnTirarFoto.setVisibility(View.GONE);
- btnAnalisarIA.setVisibility(View.VISIBLE);
- tvResultadoIA.setText("Prato detetado! Clica em Analisar.");
+ if (extras != null && extras.containsKey("data")) {
+ imagemCapturada = (Bitmap) extras.get("data");
+ mostrarImagemPreparada();
+ }
}
- }
- );
+ });
- btnTirarFoto.setOnClickListener(v -> cameraLauncher.launch(new Intent(MediaStore.ACTION_IMAGE_CAPTURE)));
+ ActivityResultLauncher galLauncher = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ if (result.getResultCode() == RESULT_OK && result.getData() != null) {
+ Uri uri = result.getData().getData();
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ ImageDecoder.Source source = ImageDecoder.createSource(this.getContentResolver(), uri);
+ imagemCapturada = ImageDecoder.decodeBitmap(source);
+ } else {
+ imagemCapturada = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
+ }
+ mostrarImagemPreparada();
+ } catch (IOException e) {
+ tvResultadoIA.setText("Erro ao processar imagem.");
+ }
+ }
+ });
- btnAnalisarIA.setOnClickListener(v -> { if (imagemCapturada != null) enviarParaIA(); });
- btnVoltarFoto.setOnClickListener(v -> finish());
+ btnTirarFoto.setOnClickListener(v -> camLauncher.launch(new Intent(MediaStore.ACTION_IMAGE_CAPTURE)));
+ btnGaleria.setOnClickListener(v -> {
+ Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ galLauncher.launch(intent);
+ });
+
+ btnAnalisarIA.setOnClickListener(v -> enviarParaIA());
+
+ btnIrParaChat.setOnClickListener(v -> {
+ Intent intent = new Intent(FotoActivity.this, ChatActivity.class);
+ intent.putExtra("analise_comida", textoAnalise);
+ startActivity(intent);
+ });
+
+ findViewById(R.id.btnVoltarFoto).setOnClickListener(v -> finish());
+ }
+
+ private void mostrarImagemPreparada() {
+ if (imagemCapturada != null) {
+ ivFotoComida.setVisibility(View.VISIBLE);
+ ivFotoComida.setPadding(0, 0, 0, 0);
+ ivFotoComida.setImageBitmap(imagemCapturada);
+ btnAnalisarIA.setVisibility(View.VISIBLE);
+ btnIrParaChat.setVisibility(View.GONE);
+ tvResultadoIA.setText("Pronto para analisar.");
+ }
}
private void enviarParaIA() {
- tvResultadoIA.setText("A procurar servidor livre e a analisar... ⏳");
+ tvResultadoIA.setText("A processar... ⏳");
btnAnalisarIA.setEnabled(false);
+ btnIrParaChat.setVisibility(View.GONE);
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- imagemCapturada.compress(Bitmap.CompressFormat.JPEG, 60, outputStream);
- String base64Image = Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ imagemCapturada.compress(Bitmap.CompressFormat.JPEG, 50, os);
+ String base64 = Base64.encodeToString(os.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.";
+ String ordemParaIA = "És um nutricionista prático. Identifica a comida e dá os valores de forma SUPER RESUMIDA. " +
+ "REGRAS: 1. Português de Portugal. 2. SEM asteriscos. 3. Máximo 4 linhas. " +
+ "Formato exato: \n" +
+ "Prato: [Nome]\n" +
+ "Calorias: [Valor] kcal\n" +
+ "Macros: [X]g Proteína, [X]g Hidratos, [X]g Gordura\n" +
+ "Dica: [Uma frase curta].";
- 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));
+ AiRequest request = new AiRequest(Collections.singletonList(
+ new Message("user", java.util.Arrays.asList(
+ new ContentPart("text", ordemParaIA),
+ new ContentPart("image_url", new ImageUrl("data:image/jpeg;base64," + base64))
+ ))
+ ));
- AiApi api = AiConfig.getRetrofit().create(AiApi.class);
- api.analisarImagem("Bearer " + MINHA_API_KEY, request).enqueue(new Callback() {
- @Override
- 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.");
- }
- });
+ AiConfig.getRetrofit().create(AiApi.class)
+ .analisarImagem("Bearer " + MINHA_API_KEY, request)
+ .enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ btnAnalisarIA.setEnabled(true);
+ if (response.isSuccessful() && response.body() != null) {
+ try {
+ String resposta = response.body().choices.get(0).message.content;
+ textoAnalise = resposta.replace("**", "").replace("*", "");
+ tvResultadoIA.setText(textoAnalise);
+ btnIrParaChat.setVisibility(View.VISIBLE);
+ } catch (Exception e) { tvResultadoIA.setText("Erro na resposta."); }
+ } else { tvResultadoIA.setText("Erro: " + response.code()); }
+ }
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ btnAnalisarIA.setEnabled(true);
+ tvResultadoIA.setText("Sem internet.");
+ }
+ });
}
}
\ 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 a3cf471..76b93bc 100644
--- a/app/src/main/java/com/example/pap/HomeActivity.java
+++ b/app/src/main/java/com/example/pap/HomeActivity.java
@@ -51,6 +51,7 @@ public class HomeActivity extends AppCompatActivity {
cardPerfil.setOnClickListener(v -> startActivity(new Intent(HomeActivity.this, PerfilActivity.class)));
+
// 5. Verifica se já passou o tempo para pedir o novo peso
verificarAtualizacaoSemanal();
}
diff --git a/app/src/main/res/drawable/aero_glass_panel.xml b/app/src/main/res/drawable/aero_glass_panel.xml
new file mode 100644
index 0000000..08bf398
--- /dev/null
+++ b/app/src/main/res/drawable/aero_glass_panel.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_cyber_button.xml b/app/src/main/res/drawable/bg_cyber_button.xml
new file mode 100644
index 0000000..fd5a55d
--- /dev/null
+++ b/app/src/main/res/drawable/bg_cyber_button.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_cyber_panel.xml b/app/src/main/res/drawable/bg_cyber_panel.xml
new file mode 100644
index 0000000..fa5ee35
--- /dev/null
+++ b/app/src/main/res/drawable/bg_cyber_panel.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_frutiger.xml b/app/src/main/res/drawable/bg_frutiger.xml
new file mode 100644
index 0000000..2d39760
--- /dev/null
+++ b/app/src/main/res/drawable/bg_frutiger.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_frutiger_aero.xml b/app/src/main/res/drawable/bg_frutiger_aero.xml
new file mode 100644
index 0000000..d5f30d4
--- /dev/null
+++ b/app/src/main/res/drawable/bg_frutiger_aero.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/jelly_button_blue.xml b/app/src/main/res/drawable/jelly_button_blue.xml
new file mode 100644
index 0000000..8421348
--- /dev/null
+++ b/app/src/main/res/drawable/jelly_button_blue.xml
@@ -0,0 +1,27 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/jelly_button_green.xml b/app/src/main/res/drawable/jelly_button_green.xml
new file mode 100644
index 0000000..6db6a1d
--- /dev/null
+++ b/app/src/main/res/drawable/jelly_button_green.xml
@@ -0,0 +1,27 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/jelly_button_grey.xml b/app/src/main/res/drawable/jelly_button_grey.xml
new file mode 100644
index 0000000..e7ce2d0
--- /dev/null
+++ b/app/src/main/res/drawable/jelly_button_grey.xml
@@ -0,0 +1,27 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ 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 412e7b7..e3efdcf 100644
--- a/app/src/main/res/layout/activity_chat.xml
+++ b/app/src/main/res/layout/activity_chat.xml
@@ -1,148 +1,83 @@
+ android:background="#FFFFFF">
-
+ android:layout_alignParentTop="true"
+ android:background="#FFFFFF"
+ android:paddingVertical="12dp">
-
-
-
-
-
+ android:text="Voltar"
+ android:textSize="16sp"
+ android:textColor="#8E8E93"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:paddingHorizontal="20dp"
+ android:paddingVertical="8dp"/>
-
-
-
-
-
-
-
-
-
-
+
+
+ android:padding="16dp">
-
+ android:textSize="16sp"
+ android:textColor="#3A3A3C"
+ android:lineSpacingExtra="8dp"
+ android:text="Olá! Como posso ajudar?"/>
+ android:background="#F2F2F7">
-
-
-
-
-
-
-
-
-
+ android:hint="Escreve aqui..."
+ android:background="@android:color/transparent"
+ android:padding="12dp"
+ android:textSize="16sp"/>
+
-
\ 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 9071a49..0ec461a 100644
--- a/app/src/main/res/layout/activity_desafios.xml
+++ b/app/src/main/res/layout/activity_desafios.xml
@@ -4,153 +4,184 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#F1F5F9">
+ android:background="#FFFFFF"
+ android:padding="24dp">
-
-
-
+
-
-
-
-
-
-
+ android:text="Voltar"
+ android:textSize="16sp"
+ android:textColor="#8E8E93"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:padding="8dp" />
+
+
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollbars="none">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:textColor="#8E8E93"
+ android:layout_marginBottom="12dp"
+ android:layout_marginStart="4dp"/>
-
+ app:cardCornerRadius="24dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
-
-
-
-
-
+ android:padding="20dp">
+
+
+
+
+ android:layout_height="54dp"
+ android:text="Gravar Exercício"
+ android:backgroundTint="#1C1C1E"
+ android:textColor="#FFFFFF"
+ app:cornerRadius="16dp"
+ android:layout_marginTop="16dp"
+ android:elevation="0dp"/>
-
+ android:layout_marginBottom="32dp"
+ app:cardCornerRadius="24dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
-
-
-
-
-
+ android:padding="20dp">
+
+
+
+
+ android:layout_height="54dp"
+ android:text="Gravar Exercício"
+ android:backgroundTint="#1C1C1E"
+ android:textColor="#FFFFFF"
+ app:cornerRadius="16dp"
+ android:layout_marginTop="16dp"
+ android:elevation="0dp"/>
-
-
-
\ 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 ea20870..9af186a 100644
--- a/app/src/main/res/layout/activity_estatisticas.xml
+++ b/app/src/main/res/layout/activity_estatisticas.xml
@@ -4,74 +4,87 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#F8FAFC">
+ android:background="#FFFFFF"
+ android:paddingHorizontal="24dp"
+ android:paddingTop="24dp">
-
+ android:layout_marginBottom="24dp">
+
+
+
+
+
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollbars="none">
+ android:orientation="vertical"
+ android:paddingBottom="40dp">
+ android:layout_marginBottom="16dp"
+ app:cardCornerRadius="20dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F8FAFC">
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+
+
+
@@ -79,21 +92,21 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:layout_marginBottom="16dp">
+ android:layout_marginBottom="16dp"
+ android:baselineAligned="false">
-
-
+ android:layout_marginEnd="8dp"
+ app:cardCornerRadius="20dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
+
+
+
+
@@ -101,15 +114,14 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- app:cardCornerRadius="12dp"
- android:layout_marginStart="8dp">
-
-
+ android:layout_marginStart="8dp"
+ app:cardCornerRadius="20dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
+
+
+
+
@@ -117,29 +129,112 @@
+ android:layout_marginBottom="16dp"
+ app:cardCornerRadius="24dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
+ android:padding="20dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
\ 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 af80ae8..d645e03 100644
--- a/app/src/main/res/layout/activity_foto.xml
+++ b/app/src/main/res/layout/activity_foto.xml
@@ -4,44 +4,50 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#F8FAFC">
+ android:background="#FFFFFF"
+ android:padding="24dp">
-
-
-
+ android:layout_marginBottom="24dp">
+
-
+ android:textColor="#1C1C1E"
+ android:layout_centerInParent="true" />
+
+ android:fillViewport="true"
+ android:scrollbars="none">
-
+ android:scaleType="centerCrop" />
-
-
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginBottom="16dp">
+
+
+
-
+ android:text="Analisar"
+ android:backgroundTint="#1C1C1E"
+ android:textColor="#FFFFFF"
+ app:cornerRadius="16dp"
+ android:visibility="gone"
+ android:layout_marginBottom="16dp"/>
-
+ android:layout_marginBottom="24dp"/>
-
+ android:layout_height="60dp"
+ android:text="Dúvidas no Chat →"
+ android:backgroundTint="#F2F2F7"
+ android:textColor="#1C1C1E"
+ app:cornerRadius="16dp"
+ android:visibility="gone" />
\ 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 e72d2f9..3af03e9 100644
--- a/app/src/main/res/layout/activity_home.xml
+++ b/app/src/main/res/layout/activity_home.xml
@@ -3,95 +3,85 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#F8FAFC"
- android:fillViewport="true">
+ android:background="#FFFFFF"
+ android:fillViewport="true"
+ android:scrollbars="none">
+ android:paddingHorizontal="24dp"
+ android:paddingTop="40dp"
+ android:paddingBottom="40dp">
+ android:textSize="16sp"
+ android:textColor="#8E8E93"
+ android:layout_marginBottom="4dp"/>
+ android:textColor="#1C1C1E"
+ android:layout_marginBottom="40dp"/>
+ app:cardCornerRadius="24dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#1C1C1E">
-
-
-
+
+ android:layout_marginStart="20dp">
+
+
+ android:textSize="14sp"
+ android:textColor="#A1A1AA"
+ android:layout_marginTop="4dp"/>
-
@@ -103,41 +93,35 @@
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
- app:cardCornerRadius="16dp"
- app:cardElevation="2dp"
- app:contentPadding="16dp">
+ app:cardCornerRadius="20dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
-
-
-
+
-
-
+ android:layout_marginStart="20dp">
+
+
+
-
+
@@ -149,41 +133,35 @@
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
- app:cardCornerRadius="16dp"
- app:cardElevation="2dp"
- app:contentPadding="16dp">
+ app:cardCornerRadius="20dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
-
-
-
+
-
-
+ android:layout_marginStart="20dp">
+
+
+
-
+
@@ -195,41 +173,35 @@
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
- app:cardCornerRadius="16dp"
- app:cardElevation="2dp"
- app:contentPadding="16dp">
+ app:cardCornerRadius="20dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
-
-
-
+
-
-
+ android:layout_marginStart="20dp">
+
+
+
-
+
@@ -241,41 +213,35 @@
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
- app:cardCornerRadius="16dp"
- app:cardElevation="2dp"
- app:contentPadding="16dp">
+ app:cardCornerRadius="20dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="#F2F2F7">
-
-
-
+
-
-
+ android:layout_marginStart="20dp">
+
+
+
-
+
diff --git a/app/src/main/res/layout/activity_ingrediente.xml b/app/src/main/res/layout/activity_ingrediente.xml
deleted file mode 100644
index 04ea85b..0000000
--- a/app/src/main/res/layout/activity_ingrediente.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file