package com.example.pap; import android.content.Intent; import android.content.SharedPreferences; 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; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Collections; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class FotoActivity extends AppCompatActivity { private ImageView ivFotoComida; private Button btnTirarFoto, btnGaleria, btnAnalisarIA, btnIrParaChat, btnCorrigir; private TextView tvResultadoIA; private Bitmap imagemCapturada; private String textoAnalise = ""; // A TUA CHAVE DA API private final String MINHA_API_KEY = "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"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_foto); // Ligar ao XML 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); btnCorrigir = findViewById(R.id.btnCorrigir); tvResultadoIA = findViewById(R.id.tvResultadoIA); ActivityResultLauncher camLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK && result.getData() != null) { Bundle extras = result.getData().getExtras(); if (extras != null && extras.containsKey("data")) { imagemCapturada = (Bitmap) extras.get("data"); mostrarImagemPreparada(); } } }); 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."); } } }); 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); }); // Clique para analisar pela primeira vez btnAnalisarIA.setOnClickListener(v -> enviarParaIA(null)); btnIrParaChat.setOnClickListener(v -> { Intent intent = new Intent(FotoActivity.this, ChatActivity.class); intent.putExtra("analise_comida", textoAnalise); startActivity(intent); }); // Clique para corrigir o erro da IA btnCorrigir.setOnClickListener(v -> mostrarPopupCorrecao()); 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); btnCorrigir.setVisibility(View.GONE); tvResultadoIA.setText("Pronto para analisar."); } } // Função blindada contra erros da IA private void enviarParaIA(String comidaCerta) { tvResultadoIA.setText("A processar... ⏳"); btnAnalisarIA.setEnabled(false); btnIrParaChat.setVisibility(View.GONE); btnCorrigir.setVisibility(View.GONE); ByteArrayOutputStream os = new ByteArrayOutputStream(); imagemCapturada.compress(Bitmap.CompressFormat.JPEG, 50, os); String base64 = Base64.encodeToString(os.toByteArray(), Base64.NO_WRAP); String ordemParaIA; if (comidaCerta == null) { // Regras super restritas para a primeira análise ordemParaIA = "És uma API de nutrição. Avalia a foto. É ESTRITAMENTE PROIBIDO usar texto de conversa, saudações ou tags de segurança. " + "Responde APENAS E SÓ neste formato exato:\n" + "Prato: [Nome]\n" + "Calorias: [Valor] kcal\n" + "Macros: [X]g Proteína, [X]g Hidratos, [X]g Gordura\n" + "Dica: [Frase curta sem asteriscos]."; } else { // Regras super restritas para a correção ordemParaIA = "Atenção: ignora a imagem. O utilizador confirmou que o prato é '" + comidaCerta + "'. " + "É ESTRITAMENTE PROIBIDO usar texto de conversa ou avisos de segurança (ex: User:safe). " + "Responde APENAS E SÓ com os valores nutricionais médios para '" + comidaCerta + "' neste formato exato:\n" + "Prato: " + comidaCerta + "\n" + "Calorias: [Valor] kcal\n" + "Macros: [X]g Proteína, [X]g Hidratos, [X]g Gordura\n" + "Dica: [Frase curta sem asteriscos]."; } 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)) )) )); 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("*", ""); // O NOSSO ESCUDO: Se a resposta não tiver a palavra "Calorias", a IA deu tilt! if (!textoAnalise.contains("Calorias:") || !textoAnalise.contains("Macros:")) { tvResultadoIA.setText("A IA ficou confusa com o prato 😵\u200D💫. Clica em 'Corrigir' e tenta ser mais específico (Ex: Bife com Arroz)."); btnCorrigir.setVisibility(View.VISIBLE); return; // Pára tudo aqui, não guarda lixo na memória! } tvResultadoIA.setText(textoAnalise); btnIrParaChat.setVisibility(View.VISIBLE); btnCorrigir.setVisibility(View.VISIBLE); // Se ele estiver a corrigir, apagamos o erro passado! if (comidaCerta != null) { desfazerUltimoErro(); } // Guarda a nova resposta extrairEGuardarDados(textoAnalise); } catch (Exception e) { tvResultadoIA.setText("Erro na leitura da resposta."); } } else { tvResultadoIA.setText("Erro no servidor: " + response.code()); } } @Override public void onFailure(Call call, Throwable t) { btnAnalisarIA.setEnabled(true); tvResultadoIA.setText("Sem internet."); } }); } // --- POPUP PARA CORREÇÃO MANUAL --- private void mostrarPopupCorrecao() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Corrigir a IA 🤔"); builder.setMessage("A IA enganou-se? O que estava realmente no teu prato?"); final EditText input = new EditText(this); input.setHint("Ex: Hambúrguer de Frango"); LinearLayout layout = new LinearLayout(this); layout.setPadding(50, 20, 50, 0); layout.addView(input); builder.setView(layout); builder.setPositiveButton("Re-Analisar", (dialog, which) -> { String comidaCerta = input.getText().toString().trim(); if (!comidaCerta.isEmpty()) { enviarParaIA(comidaCerta); } else { Toast.makeText(FotoActivity.this, "Tens de escrever a comida!", Toast.LENGTH_SHORT).show(); } }); builder.setNegativeButton("Cancelar", null); builder.show(); } // --- FUNÇÃO PARA SALVAR OS DADOS E A DICA --- private void extrairEGuardarDados(String texto) { try { int indexNomeStart = texto.indexOf("Prato: ") + 7; int indexNomeEnd = texto.indexOf("\n", indexNomeStart); String nomePrato = texto.substring(indexNomeStart, indexNomeEnd).trim(); int calorias = extrairNumero(texto, "Calorias: ", " kcal"); int proteina = extrairNumero(texto, "Macros: ", "g Proteína"); int hidratos = extrairNumero(texto, "Proteína, ", "g Hidratos"); int gordura = extrairNumero(texto, "Hidratos, ", "g Gordura"); String dicaIA = "Continua a registar refeições para ver dicas."; if (texto.contains("Dica: ")) { int indexDica = texto.indexOf("Dica: ") + 6; dicaIA = texto.substring(indexDica).trim(); } SharedPreferences prefs = getSharedPreferences("DadosSaude", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("ultimo_prato", nomePrato); editor.putString("ultima_dica_ia", dicaIA); editor.putInt("cal_hoje", prefs.getInt("cal_hoje", 0) + calorias); editor.putInt("prot_hoje", prefs.getInt("prot_hoje", 0) + proteina); editor.putInt("hidr_hoje", prefs.getInt("hidr_hoje", 0) + hidratos); editor.putInt("gord_hoje", prefs.getInt("gord_hoje", 0) + gordura); // GUAAAAARDA O ERRO PARA PODERMOS APAGAR SE O GAJO CLICAR EM "CORRIGIR" editor.putInt("ultimo_erro_cal", calorias); editor.putInt("ultimo_erro_prot", proteina); editor.putInt("ultimo_erro_hidr", hidratos); editor.putInt("ultimo_erro_gord", gordura); editor.apply(); } catch (Exception e) {} } // --- FUNÇÃO PARA REMOVER O ÚLTIMO PRATO QUE FOI MAL LIDO --- private void desfazerUltimoErro() { SharedPreferences prefs = getSharedPreferences("DadosSaude", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); int calAntiga = prefs.getInt("ultimo_erro_cal", 0); int protAntiga = prefs.getInt("ultimo_erro_prot", 0); int hidrAntiga = prefs.getInt("ultimo_erro_hidr", 0); int gordAntiga = prefs.getInt("ultimo_erro_gord", 0); editor.putInt("cal_hoje", prefs.getInt("cal_hoje", 0) - calAntiga); editor.putInt("prot_hoje", prefs.getInt("prot_hoje", 0) - protAntiga); editor.putInt("hidr_hoje", prefs.getInt("hidr_hoje", 0) - hidrAntiga); editor.putInt("gord_hoje", prefs.getInt("gord_hoje", 0) - gordAntiga); editor.apply(); } private int extrairNumero(String texto, String inicio, String fim) { try { int start = texto.indexOf(inicio) + inicio.length(); int end = texto.indexOf(fim, start); String valorString = texto.substring(start, end).trim(); valorString = valorString.replaceAll("[^0-9]", ""); return Integer.parseInt(valorString); } catch (Exception e) { return 0; } } }