From 1a8ddf4b7906a7dad721fd76b7dfdfae3b81bbb4 Mon Sep 17 00:00:00 2001 From: 230412 <230412@epvc.pt> Date: Mon, 11 May 2026 17:19:20 +0100 Subject: [PATCH] =?UTF-8?q?corrige=20o=20erro=20da=20camera=20e=20da=20ia?= =?UTF-8?q?=20n=C3=A3o=20funcionar=20e=20mudar=20o=20layout=20futuramente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetSelector.xml | 8 + .../java/com/example/pap/ChatActivity.java | 191 ++++--------- .../com/example/pap/DesafiosActivity.java | 164 ++++------- .../com/example/pap/EstatisticasActivity.java | 16 +- .../java/com/example/pap/FotoActivity.java | 149 ++++++---- .../java/com/example/pap/HomeActivity.java | 1 + .../main/res/drawable/aero_glass_panel.xml | 7 + app/src/main/res/drawable/bg_cyber_button.xml | 4 + app/src/main/res/drawable/bg_cyber_panel.xml | 6 + app/src/main/res/drawable/bg_frutiger.xml | 10 + .../main/res/drawable/bg_frutiger_aero.xml | 10 + .../main/res/drawable/jelly_button_blue.xml | 27 ++ .../main/res/drawable/jelly_button_green.xml | 27 ++ .../main/res/drawable/jelly_button_grey.xml | 27 ++ app/src/main/res/layout/activity_chat.xml | 167 ++++------- app/src/main/res/layout/activity_desafios.xml | 239 +++++++++------- .../main/res/layout/activity_estatisticas.xml | 261 ++++++++++++------ app/src/main/res/layout/activity_foto.xml | 122 ++++---- app/src/main/res/layout/activity_home.xml | 204 ++++++-------- .../main/res/layout/activity_ingrediente.xml | 39 --- 20 files changed, 850 insertions(+), 829 deletions(-) create mode 100644 app/src/main/res/drawable/aero_glass_panel.xml create mode 100644 app/src/main/res/drawable/bg_cyber_button.xml create mode 100644 app/src/main/res/drawable/bg_cyber_panel.xml create mode 100644 app/src/main/res/drawable/bg_frutiger.xml create mode 100644 app/src/main/res/drawable/bg_frutiger_aero.xml create mode 100644 app/src/main/res/drawable/jelly_button_blue.xml create mode 100644 app/src/main/res/drawable/jelly_button_green.xml create mode 100644 app/src/main/res/drawable/jelly_button_grey.xml delete mode 100644 app/src/main/res/layout/activity_ingrediente.xml 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"/> +