320 lines
14 KiB
Java
320 lines
14 KiB
Java
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<Intent> 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<AiResponse>() {
|
|
@Override
|
|
public void onResponse(Call<AiResponse> call, Response<AiResponse> 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<AiResponse> 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; }
|
|
}
|
|
} |