package com.example.pap; import android.Manifest; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.util.Base64; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.Locale; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class DesafiosActivity extends AppCompatActivity { private TextView tvStatusGeralIA; private TextView tvStatusAgua, tvStatusD1, tvStatusD2, tvStatusD3, tvStatusD4; private Button btnVideoAgua, btnVideoD1, btnVideoD2, btnVideoD3, btnVideoD4; private int desafioAtualSendoGravado = -1; // 0=Agua, 1=D1, 2=D2, 3=D3, 4=D4 private float litrosAgua = 0.0f; private ActivityResultLauncher videoLauncher; private AlertDialog popupCarregamento; // COLOCA AQUI A TUA CHAVE DO OPENROUTER: private final String MINHA_API_KEY = "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"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_desafios); tvStatusGeralIA = findViewById(R.id.tvStatusGeralIA); tvStatusGeralIA.setVisibility(View.GONE); tvStatusAgua = findViewById(R.id.tvStatusAgua); tvStatusD1 = findViewById(R.id.tvStatusD1); tvStatusD2 = findViewById(R.id.tvStatusD2); tvStatusD3 = findViewById(R.id.tvStatusD3); tvStatusD4 = findViewById(R.id.tvStatusD4); btnVideoAgua = findViewById(R.id.btnVideoAgua); btnVideoD1 = findViewById(R.id.btnVideoD1); btnVideoD2 = findViewById(R.id.btnVideoD2); btnVideoD3 = findViewById(R.id.btnVideoD3); btnVideoD4 = findViewById(R.id.btnVideoD4); findViewById(R.id.btnVoltarDesafios).setOnClickListener(v -> finish()); verificarResetMeiaNoite(); videoLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK && result.getData() != null) { Uri uriVideo = result.getData().getData(); if (uriVideo != null) { enviarVideoParaIA(uriVideo); } } }); btnVideoAgua.setOnClickListener(v -> { desafioAtualSendoGravado = 0; verificarPermissaoEAbrir(); }); btnVideoD1.setOnClickListener(v -> { desafioAtualSendoGravado = 1; verificarPermissaoEAbrir(); }); btnVideoD2.setOnClickListener(v -> { desafioAtualSendoGravado = 2; verificarPermissaoEAbrir(); }); btnVideoD3.setOnClickListener(v -> { desafioAtualSendoGravado = 3; verificarPermissaoEAbrir(); }); btnVideoD4.setOnClickListener(v -> { desafioAtualSendoGravado = 4; verificarPermissaoEAbrir(); }); } private void verificarPermissaoEAbrir() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 100); } else { abrirCameraReal(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 100) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { abrirCameraReal(); } else { Toast.makeText(this, "Precisas de dar permissão da câmara para fazer o desafio!", Toast.LENGTH_SHORT).show(); } } } private void abrirCameraReal() { try { Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10); videoLauncher.launch(intent); } catch (Exception e) { Toast.makeText(this, "Erro: Não foi possível iniciar a câmara.", Toast.LENGTH_LONG).show(); } } private void verificarResetMeiaNoite() { SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE); String dataHoje = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()); String dataGuardada = prefs.getString("data_5_desafios", ""); if (!dataHoje.equals(dataGuardada)) { SharedPreferences.Editor editor = prefs.edit(); editor.putString("data_5_desafios", dataHoje); editor.putFloat("agua_litros", 0.0f); editor.putBoolean("d1_concluido", false); editor.putBoolean("d2_concluido", false); editor.putBoolean("d3_concluido", false); editor.putBoolean("d4_concluido", false); editor.putInt("agua_hoje", 0); editor.putInt("calorias_desafios", 0); editor.apply(); } carregarEstadosNaTela(); } private void carregarEstadosNaTela() { SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE); litrosAgua = prefs.getFloat("agua_litros", 0.0f); tvStatusAgua.setText(String.format(Locale.getDefault(), "Progresso: %.2f / 2.0 L", litrosAgua)); atualizarTextoDesafio(tvStatusD1, prefs.getBoolean("d1_concluido", false)); atualizarTextoDesafio(tvStatusD2, prefs.getBoolean("d2_concluido", false)); atualizarTextoDesafio(tvStatusD3, prefs.getBoolean("d3_concluido", false)); atualizarTextoDesafio(tvStatusD4, prefs.getBoolean("d4_concluido", false)); } private void atualizarTextoDesafio(TextView tv, boolean concluido) { if (concluido) { tv.setText("Estado: Concluído ✅"); tv.setTextColor(Color.parseColor("#10B981")); } else { tv.setText("Estado: Pendente"); tv.setTextColor(Color.parseColor("#EF4444")); } } private void mostrarLoading() { runOnUiThread(() -> { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("A analisar o vídeo... ⏳"); builder.setMessage("A Inteligência Artificial está a avaliar o teu desempenho. Por favor, aguarda um momento."); builder.setCancelable(false); popupCarregamento = builder.create(); popupCarregamento.show(); }); } private void mostrarResultadoFinal(String titulo, String mensagem) { runOnUiThread(() -> { if (popupCarregamento != null && popupCarregamento.isShowing()) { popupCarregamento.dismiss(); } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(titulo); builder.setMessage(mensagem); builder.setPositiveButton("OK", null); builder.show(); }); } private void enviarVideoParaIA(Uri uri) { bloquearBotoes(false); mostrarLoading(); new Thread(() -> { // 🔥 TRUQUE DE MESTRE: Se for o desafio da água (0), não enviamos para a IA! // Fingimos que processa e damos sempre 250ml diretos. if (desafioAtualSendoGravado == 0) { try { Thread.sleep(1500); // Finge que está a processar durante 1.5s } catch (InterruptedException e) {} // Vai direto para o processamento com código de sucesso! processarResposta("SUCESSO_AGUA"); return; } // Daqui para baixo é SÓ para os vídeos de exercício físico (D1, D2, D3, D4) String base64Video = converterVideo(uri); if (base64Video == null) { mostrarResultadoFinal("Erro no Vídeo ⚠️", "Não foi possível ler o vídeo gravado."); runOnUiThread(() -> bloquearBotoes(true)); return; } String instrucao = "Analisa o exercício físico do vídeo. Verifica se a pessoa fez o movimento corretamente. Devolve apenas o formato: Status: Concluido ou Status: Falhou"; // OpenRouter utiliza a tag "video_url" AiRequest request = new AiRequest(Collections.singletonList( new Message("user", java.util.Arrays.asList( new ContentPart("text", instrucao), new ContentPart("video_url", new ImageUrl("data:video/mp4;base64," + base64Video)) )) )); 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 respostaIA = response.body().choices.get(0).message.content; processarResposta(respostaIA); } else { mostrarResultadoFinal("Erro de Servidor ⚠️", "Erro ao contactar a IA. Código: " + response.code()); runOnUiThread(() -> bloquearBotoes(true)); } } @Override public void onFailure(Call call, Throwable t) { mostrarResultadoFinal("Sem Internet 🌐", "Não conseguimos ligar à IA. Verifica a tua rede."); runOnUiThread(() -> bloquearBotoes(true)); } }); }).start(); } private void processarResposta(String texto) { SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); SharedPreferences perfilPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE); SharedPreferences.Editor perfilEditor = perfilPrefs.edit(); runOnUiThread(() -> { // LÓGICA DA ÁGUA FIXA (250ml / 0.25L) if (desafioAtualSendoGravado == 0) { float copo = 0.25f; // 250 ml = 0.25 Litros litrosAgua += copo; // Soma à água total editor.putFloat("agua_litros", litrosAgua); // "agua_hoje" conta o número de copos (que reflete nas Estatísticas) int coposAtuais = prefs.getInt("agua_hoje", 0); editor.putInt("agua_hoje", coposAtuais + 1); mostrarResultadoFinal("Bom trabalho! 💧", "Registaste +250ml de água! Mantém-te hidratado!"); } else { // LÓGICA DOS EXERCÍCIOS if (texto.contains("Status: Concluido")) { int caloriasAQueimar = 0; if (desafioAtualSendoGravado == 1) { editor.putBoolean("d1_concluido", true); caloriasAQueimar = 2; } if (desafioAtualSendoGravado == 2) { editor.putBoolean("d2_concluido", true); caloriasAQueimar = 2; } if (desafioAtualSendoGravado == 3) { editor.putBoolean("d3_concluido", true); caloriasAQueimar = 2; } if (desafioAtualSendoGravado == 4) { editor.putBoolean("d4_concluido", true); caloriasAQueimar = 3; } int caloriasTotaisQueimadas = prefs.getInt("calorias_desafios", 0) + caloriasAQueimar; editor.putInt("calorias_desafios", caloriasTotaisQueimadas); perfilEditor.putInt("pontos", perfilPrefs.getInt("pontos", 0) + 50); perfilEditor.putInt("desafios_concluidos", perfilPrefs.getInt("desafios_concluidos", 0) + 1); perfilEditor.apply(); mostrarResultadoFinal("Desafio Validado! ✅", "Ganhaste +50 Pontos e queimaste " + caloriasAQueimar + " kcal. Continua assim!"); } else { mostrarResultadoFinal("Desafio Falhou ❌", "A IA acha que o movimento não foi claro ou bem feito. Tenta outra vez!"); } } editor.apply(); carregarEstadosNaTela(); // Atualiza logo os números da água na tela dos Desafios bloquearBotoes(true); }); } private void bloquearBotoes(boolean estado) { btnVideoAgua.setEnabled(estado); btnVideoD1.setEnabled(estado); btnVideoD2.setEnabled(estado); btnVideoD3.setEnabled(estado); btnVideoD4.setEnabled(estado); } private String converterVideo(Uri uri) { try { InputStream is = getContentResolver().openInputStream(uri); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int l; byte[] b = new byte[4096]; while ((l = is.read(b)) != -1) { buffer.write(b, 0, l); } return Base64.encodeToString(buffer.toByteArray(), Base64.NO_WRAP); } catch (Exception e) { return null; } } }