corrigir os novos erros amnhã e adicionar qual tipo de sexo a pessoa é no register

This commit is contained in:
2026-05-27 12:45:19 +01:00
parent 0698d5bf69
commit d1e83584e3
4 changed files with 314 additions and 315 deletions

View File

@@ -2,23 +2,24 @@ package com.example.pap;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Base64;
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;
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;
@@ -26,195 +27,215 @@ import retrofit2.Response;
public class DesafiosActivity extends AppCompatActivity {
private ProgressBar progressAgua;
private TextView tvStatusAgua;
private Button btnGravarAgua, btnGravarEx1, btnGravarEx2;
private int coposBebidos = 0;
private final int META_COPOS = 8;
private TextView tvStatusGeralIA;
private TextView tvStatusAgua, tvStatusD1, tvStatusD2, tvStatusD3, tvStatusD4;
private Button btnVideoAgua, btnVideoD1, btnVideoD2, btnVideoD3, btnVideoD4;
// Variável para sabermos qual botão o utilizador clicou
private String desafioAtual = "";
private int desafioAtualSendoGravado = -1; // 0=Agua, 1=D1, 2=D2, 3=D3, 4=D4
private float litrosAgua = 0.0f;
// COLA A TUA CHAVE AQUI
private final String MINHA_API_KEY = "sk-or-v1-e65c704789ff164d6ed1be48881dcfa83d9e7f359650f16cf7680dd822e5592bA";
private final String MINHA_API_KEY = "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";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_desafios);
progressAgua = findViewById(R.id.progressAgua);
tvStatusGeralIA = findViewById(R.id.tvStatusGeralIA);
tvStatusAgua = findViewById(R.id.tvStatusAgua);
btnGravarAgua = findViewById(R.id.btnGravarAgua);
btnGravarEx1 = findViewById(R.id.btnGravarEx1);
btnGravarEx2 = findViewById(R.id.btnGravarEx2);
tvStatusD1 = findViewById(R.id.tvStatusD1);
tvStatusD2 = findViewById(R.id.tvStatusD2);
tvStatusD3 = findViewById(R.id.tvStatusD3);
tvStatusD4 = findViewById(R.id.tvStatusD4);
// Carregar a água que já bebeu hoje
SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
coposBebidos = prefs.getInt("agua_hoje", 0);
atualizarProgressoAgua();
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);
// Launcher que recebe o vídeo da câmara
findViewById(R.id.btnVoltarDesafios).setOnClickListener(v -> finish());
verificarResetMeiaNoite();
// Launcher da Câmara
ActivityResultLauncher<Intent> videoLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
Uri videoUri = result.getData().getData();
extrairFrameEEnviarIA(videoUri);
} else {
Toast.makeText(this, "Vídeo cancelado.", Toast.LENGTH_SHORT).show();
desbloquearBotoes();
Uri uriVideo = result.getData().getData();
if (uriVideo != null) {
enviarVideoParaIA(uriVideo);
}
}
});
// Configurar Botões (Avisamos qual é o desafio antes de abrir a câmara)
btnGravarAgua.setOnClickListener(v -> {
desafioAtual = "agua";
bloquearBotoes();
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 5); // 5 Segundos chega para beber água
videoLauncher.launch(intent);
});
// Configurar Botões
btnVideoAgua.setOnClickListener(v -> { desafioAtualSendoGravado = 0; abrirCamera(videoLauncher); });
btnVideoD1.setOnClickListener(v -> { desafioAtualSendoGravado = 1; abrirCamera(videoLauncher); });
btnVideoD2.setOnClickListener(v -> { desafioAtualSendoGravado = 2; abrirCamera(videoLauncher); });
btnVideoD3.setOnClickListener(v -> { desafioAtualSendoGravado = 3; abrirCamera(videoLauncher); });
btnVideoD4.setOnClickListener(v -> { desafioAtualSendoGravado = 4; abrirCamera(videoLauncher); });
}
btnGravarEx1.setOnClickListener(v -> {
desafioAtual = "agachamento";
bloquearBotoes();
private void abrirCamera(ActivityResultLauncher<Intent> launcher) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
videoLauncher.launch(intent);
});
btnGravarEx2.setOnClickListener(v -> {
desafioAtual = "flexoes";
bloquearBotoes();
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
videoLauncher.launch(intent);
});
findViewById(R.id.btnVoltarDesafios).setOnClickListener(v -> finish());
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
launcher.launch(intent);
}
private void extrairFrameEEnviarIA(Uri videoUri) {
Toast.makeText(this, "A extrair frame do vídeo...", Toast.LENGTH_SHORT).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", "");
try {
// Isto vai ao vídeo e tira uma "foto" a meio do tempo
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(this, videoUri);
if (!dataHoje.equals(dataGuardada)) {
// Limpar tudo
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);
// Zera a água para as estatísticas
editor.putInt("agua_hoje", 0);
editor.apply();
}
// Pega num frame do segundo 2 (2000000 microsegundos)
Bitmap frame = retriever.getFrameAtTime(2000000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
carregarEstadosNaTela();
}
if (frame != null) {
enviarParaIA(frame);
private void carregarEstadosNaTela() {
SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
litrosAgua = prefs.getFloat("agua_litros", 0.0f);
tvStatusAgua.setText(String.format(Locale.getDefault(), "Progresso: %.1f / 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")); // Verde
} else {
Toast.makeText(this, "Erro ao ler vídeo.", Toast.LENGTH_SHORT).show();
desbloquearBotoes();
}
} catch (Exception e) {
Toast.makeText(this, "Erro técnico no vídeo.", Toast.LENGTH_SHORT).show();
desbloquearBotoes();
tv.setText("Estado: Pendente");
tv.setTextColor(Color.parseColor("#EF4444")); // Vermelho
}
}
private void enviarParaIA(Bitmap bitmap) {
Toast.makeText(this, "IA a avaliar o teu desafio... ⏳", Toast.LENGTH_LONG).show();
private void enviarVideoParaIA(Uri uri) {
tvStatusGeralIA.setText("A processar vídeo e enviar para a IA... ⏳");
bloquearBotoes(false);
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);
String base64 = Base64.encodeToString(os.toByteArray(), Base64.NO_WRAP);
String dataUrl = "data:image/jpeg;base64," + base64;
new Thread(() -> {
String base64Video = converterVideo(uri);
if (base64Video == null) {
runOnUiThread(() -> { tvStatusGeralIA.setText("Erro ao ler vídeo."); bloquearBotoes(true); });
return;
}
// O SEGREDO ESTÁ AQUI: Perguntas diferentes para desafios diferentes
String ordem = "";
int pontosGanhos = 0;
if (desafioAtual.equals("agua")) {
ordem = "Verifica se há uma pessoa a beber água ou com um copo/garrafa na mão. Responde APENAS 'SIM' se houver, ou 'NAO' se não houver. Não digas mais nada.";
pontosGanhos = 10;
} else if (desafioAtual.equals("agachamento")) {
ordem = "Verifica se há uma pessoa a fazer desporto ou na posição de agachamento. Responde APENAS 'SIM' ou 'NAO'.";
pontosGanhos = 50;
} else if (desafioAtual.equals("flexoes")) {
ordem = "Verifica se há uma pessoa no chão a fazer flexões ou desporto. Responde APENAS 'SIM' ou 'NAO'.";
pontosGanhos = 60;
String instrucao = "";
if (desafioAtualSendoGravado == 0) {
instrucao = "Analisa a pessoa a beber água. Devolve apenas os litros consumidos. Formato exato: Litros: [valor_decimal]";
} else {
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";
}
AiRequest request = new AiRequest(Collections.singletonList(
new Message("user", java.util.Arrays.asList(
new ContentPart("text", ordem),
new ContentPart("image_url", new ImageUrl(dataUrl))
new ContentPart("text", instrucao),
new ContentPart("video_url", new ImageUrl("data:video/mp4;base64," + base64Video))
))
));
// Guarda as variáveis para usar dentro da resposta
final int pontosDar = pontosGanhos;
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) {
desbloquearBotoes();
if (response.isSuccessful() && response.body() != null) {
String respostaIA = response.body().choices.get(0).message.content.trim().toUpperCase();
if (respostaIA.contains("SIM")) {
sucessoDesafio(pontosDar);
String respostaIA = response.body().choices.get(0).message.content;
processarResposta(respostaIA);
} else {
Toast.makeText(DesafiosActivity.this, "A IA diz que NÃO estás a fazer o desafio! ❌", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(DesafiosActivity.this, "Erro no servidor da IA.", Toast.LENGTH_SHORT).show();
runOnUiThread(() -> { tvStatusGeralIA.setText("Erro na IA: " + response.code()); bloquearBotoes(true); });
}
}
@Override
public void onFailure(Call<AiResponse> call, Throwable t) {
desbloquearBotoes();
Toast.makeText(DesafiosActivity.this, "Falha na Internet.", Toast.LENGTH_SHORT).show();
runOnUiThread(() -> { tvStatusGeralIA.setText("Erro de rede."); bloquearBotoes(true); });
}
});
}).start();
}
private void sucessoDesafio(int pontos) {
Toast.makeText(this, "✅ IA Aprovou! Ganhaste " + pontos + " pontos!", Toast.LENGTH_LONG).show();
private void processarResposta(String texto) {
SharedPreferences prefs = getSharedPreferences("DadosGamificacao", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
// Dá os pontos e sobe o contador de desafios
int pontosAtuais = prefs.getInt("pontos", 0);
int desafiosAtuais = prefs.getInt("desafios", 0);
editor.putInt("pontos", pontosAtuais + pontos);
editor.putInt("desafios", desafiosAtuais + 1);
// Dar pontos no perfil
SharedPreferences perfilPrefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
SharedPreferences.Editor perfilEditor = perfilPrefs.edit();
// Se foi o desafio da água, sobe a barra de progresso
if (desafioAtual.equals("agua")) {
coposBebidos++;
if (coposBebidos > META_COPOS) coposBebidos = META_COPOS;
editor.putInt("agua_hoje", coposBebidos);
atualizarProgressoAgua();
runOnUiThread(() -> {
if (desafioAtualSendoGravado == 0) {
if (texto.contains("Litros:")) {
try {
String valorStr = texto.substring(texto.indexOf("Litros:") + 7).trim().replaceAll("[^0-9.]", "");
float lido = Float.parseFloat(valorStr);
litrosAgua += lido;
editor.putFloat("agua_litros", litrosAgua);
editor.putInt("agua_hoje", (int) (litrosAgua / 0.25f)); // Atualiza Estatísticas
tvStatusGeralIA.setText("IA leu: +" + lido + " Litros!");
} catch (Exception e) {
tvStatusGeralIA.setText("Erro a ler os litros. Tenta de novo.");
}
}
} else {
if (texto.contains("Status: Concluido")) {
tvStatusGeralIA.setText("IA: Desafio Validado! ✅ +50 Pontos");
if (desafioAtualSendoGravado == 1) editor.putBoolean("d1_concluido", true);
if (desafioAtualSendoGravado == 2) editor.putBoolean("d2_concluido", true);
if (desafioAtualSendoGravado == 3) editor.putBoolean("d3_concluido", true);
if (desafioAtualSendoGravado == 4) editor.putBoolean("d4_concluido", true);
perfilEditor.putInt("pontos", perfilPrefs.getInt("pontos", 0) + 50);
perfilEditor.putInt("desafios_concluidos", perfilPrefs.getInt("desafios_concluidos", 0) + 1);
perfilEditor.apply();
} else {
tvStatusGeralIA.setText("IA: Desafio Falhou ou vídeo pouco claro. ❌");
}
}
editor.apply();
carregarEstadosNaTela();
bloquearBotoes(true);
});
}
editor.apply(); // Guarda tudo!
private void bloquearBotoes(boolean estado) {
btnVideoAgua.setEnabled(estado);
btnVideoD1.setEnabled(estado);
btnVideoD2.setEnabled(estado);
btnVideoD3.setEnabled(estado);
btnVideoD4.setEnabled(estado);
}
private void atualizarProgressoAgua() {
progressAgua.setProgress(coposBebidos);
tvStatusAgua.setText(coposBebidos + " de " + META_COPOS + " copos (" + (coposBebidos * 250) + "ml / 2L)");
}
private void bloquearBotoes() {
btnGravarAgua.setEnabled(false);
btnGravarEx1.setEnabled(false);
btnGravarEx2.setEnabled(false);
}
private void desbloquearBotoes() {
btnGravarAgua.setEnabled(true);
btnGravarEx1.setEnabled(true);
btnGravarEx2.setEnabled(true);
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; }
}
}

View File

@@ -5,7 +5,6 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
@@ -27,7 +26,6 @@ public class RegisterActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
// Ligar o código aos IDs do ecrã
etRegNome = findViewById(R.id.etRegNome);
etRegEmail = findViewById(R.id.etRegEmail);
etRegPassword = findViewById(R.id.etRegPassword);
@@ -38,10 +36,8 @@ public class RegisterActivity extends AppCompatActivity {
btnRegister = findViewById(R.id.btnRegister);
tvGoToLogin = findViewById(R.id.tvGoToLogin);
// Se o utilizador já tem conta, volta para o Login
tvGoToLogin.setOnClickListener(v -> finish());
// Quando clica no botão de Registar
btnRegister.setOnClickListener(v -> {
String nome = etRegNome.getText().toString().trim();
String email = etRegEmail.getText().toString().trim();
@@ -50,65 +46,54 @@ public class RegisterActivity extends AppCompatActivity {
String alturaStr = etRegAltura.getText().toString().trim();
String pesoStr = etRegPeso.getText().toString().trim();
// 1. Verificar se não há campos vazios
if (nome.isEmpty() || email.isEmpty() || password.isEmpty() || idadeStr.isEmpty() || alturaStr.isEmpty() || pesoStr.isEmpty()) {
Toast.makeText(this, "Por favor, preenche todos os campos!", Toast.LENGTH_SHORT).show();
return;
}
// 1.1 Verificar se o utilizador escolheu o sexo
int selectedSexoId = radioGroupSexo.getCheckedRadioButtonId();
if (selectedSexoId == -1) {
Toast.makeText(this, "Por favor, escolhe o teu sexo!", Toast.LENGTH_SHORT).show();
return;
}
String sexoSelecionado = "";
if (selectedSexoId == R.id.radioMasculino) {
sexoSelecionado = "Masculino";
} else if (selectedSexoId == R.id.radioFeminino) {
sexoSelecionado = "Feminino";
}
String sexoSelecionado = (selectedSexoId == R.id.radioMasculino) ? "Masculino" : "Feminino";
int idade = Integer.parseInt(idadeStr);
float altura = Float.parseFloat(alturaStr);
float peso = Float.parseFloat(pesoStr);
// 2. GUARDAR TODOS OS DADOS NA MEMÓRIA (SharedPreferences)
// Grava no telemóvel para acesso rápido
SharedPreferences prefs = getSharedPreferences("MeusDadosApp", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("nome", nome);
editor.putString("email", email);
editor.putString("sexo", sexoSelecionado);
editor.putInt("idade", Integer.parseInt(idadeStr));
editor.putFloat("altura", Float.parseFloat(alturaStr));
editor.putFloat("peso", Float.parseFloat(pesoStr));
editor.putInt("idade", idade);
editor.putFloat("altura", altura);
editor.putFloat("peso", peso);
editor.apply();
// 3. Preparar os dados para enviar para o Supabase
UserCredentials credentials = new UserCredentials(email, password);
// Vai buscar o Retrofit ao ficheiro SupabaseConfig
// Empacota tudo para enviar para o Supabase
UserCredentials credentials = new UserCredentials(email, password, nome, idade, altura, peso, sexoSelecionado);
SupabaseApi api = SupabaseConfig.getRetrofit().create(SupabaseApi.class);
// 4. Fazer o Registo na Internet usando a chave configurada
api.signUp(SupabaseConfig.SUPABASE_KEY, credentials).enqueue(new Callback<SupabaseResponse>() {
@Override
public void onResponse(Call<SupabaseResponse> call, Response<SupabaseResponse> response) {
if (response.isSuccessful()) {
Toast.makeText(RegisterActivity.this, "Conta criada! Verifica o teu email.", Toast.LENGTH_LONG).show();
// Mandar para o ecrã de espera
Toast.makeText(RegisterActivity.this, "Conta criada no Supabase! Verifica o teu email.", Toast.LENGTH_LONG).show();
Intent intent = new Intent(RegisterActivity.this, VerificacaoActivity.class);
intent.putExtra("email_registo", email);
intent.putExtra("password_registo", password);
startActivity(intent);
finish();
} else {
Toast.makeText(RegisterActivity.this, "Erro ao criar conta. O email já existe?", Toast.LENGTH_LONG).show();
Toast.makeText(RegisterActivity.this, "Erro ao criar conta online.", Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<SupabaseResponse> call, Throwable t) {
Toast.makeText(RegisterActivity.this, "Falha na ligação! Verifica a tua internet.", Toast.LENGTH_LONG).show();
Toast.makeText(RegisterActivity.this, "Falha na ligação à internet.", Toast.LENGTH_LONG).show();
}
});
});

View File

@@ -5,6 +5,8 @@ import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.PUT;
import retrofit2.http.POST;
import java.util.HashMap;
import java.util.Map;
public interface SupabaseApi {
@@ -16,7 +18,7 @@ public interface SupabaseApi {
@POST("auth/v1/token?grant_type=password")
Call<SupabaseResponse> login(@Header("apikey") String apiKey, @Body UserCredentials credentials);
// 3. Rota para atualizar a palavra-passe ou dados do utilizador (CORRIGIDO PARA @PUT)
// 3. Rota para atualizar a palavra-passe ou dados do utilizador
@PUT("auth/v1/user")
Call<Void> updateUserData(
@Header("apikey") String apikey,
@@ -30,11 +32,27 @@ public interface SupabaseApi {
class UserCredentials {
String email;
String password;
Map<String, Object> data; // O Supabase exige que os dados extras fiquem aqui dentro!
// Construtor 1: Usado para o LOGIN (só precisa de email e password)
public UserCredentials(String email, String password) {
this.email = email;
this.password = password;
}
// Construtor 2: Usado para o REGISTO (arruma os dados de saúde na pasta "data")
public UserCredentials(String email, String password, String nome, int idade, float altura, float peso, String sexo) {
this.email = email;
this.password = password;
// Empacotar os dados exatamente como o Supabase pede
this.data = new HashMap<>();
this.data.put("nome", nome);
this.data.put("idade", idade);
this.data.put("altura", altura);
this.data.put("peso", peso);
this.data.put("sexo", sexo);
}
}
class SupabaseResponse {

View File

@@ -5,12 +5,12 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFFFF"
android:padding="24dp">
android:padding="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp">
android:layout_marginBottom="16dp">
<TextView
android:id="@+id/btnVoltarDesafios"
android:layout_width="wrap_content"
@@ -18,19 +18,28 @@
android:text="Voltar"
android:textSize="16sp"
android:textColor="#8E8E93"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:padding="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Missões Diárias"
android:text="5 Desafios do Dia"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#1C1C1E"
android:layout_centerInParent="true" />
</RelativeLayout>
<TextView
android:id="@+id/tvStatusGeralIA"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Grava um vídeo para a IA avaliar."
android:textColor="#03A9F4"
android:textStyle="bold"
android:textAlignment="center"
android:layout_marginBottom="16dp"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -38,147 +47,113 @@
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F0F9FF"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="💧 Hidratação"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#0284C7"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+10 pts / copo"
android:textSize="12sp"
android:textColor="#0284C7"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Beber 250ml de água"
android:textSize="14sp"
android:textColor="#0369A1"
android:layout_marginTop="4dp"/>
<ProgressBar
android:id="@+id/progressAgua"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_marginTop="16dp"
android:progress="3"
android:max="8"
android:progressTint="#0EA5E9"/>
<TextView
android:id="@+id/tvStatusAgua"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="3 de 8 copos (750ml / 2L)"
android:textSize="12sp"
android:textColor="#0369A1"
android:textAlignment="center"
android:layout_marginTop="8dp"/>
<Button
android:id="@+id/btnGravarAgua"
android:layout_width="match_parent"
android:layout_height="54dp"
android:text="Gravar Golo"
android:backgroundTint="#0284C7"
android:textColor="#FFFFFF"
app:cornerRadius="16dp"
android:layout_marginTop="16dp"
android:elevation="0dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Outros Desafios"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="#8E8E93"
android:layout_marginBottom="12dp"
android:layout_marginStart="4dp"/>
android:paddingBottom="24dp">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="24dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
app:cardBackgroundColor="#E0F2FE">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="🦵 Agachamentos (15x)" android:textSize="16sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="+50 pts" android:textSize="12sp" android:textColor="#8E8E93" android:layout_alignParentEnd="true"/>
</RelativeLayout>
<Button
android:id="@+id/btnGravarEx1"
android:layout_width="match_parent"
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:padding="16dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafio 1: Hidratação" android:textSize="16sp" android:textStyle="bold" android:textColor="#0284C7"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Grava-te a beber água." android:textSize="14sp" android:textColor="#0369A1" android:layout_marginBottom="8dp"/>
<TextView android:id="@+id/tvStatusAgua" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Progresso: 0.0 / 2.0 L" android:textSize="14sp" android:textStyle="bold" android:textColor="#1C1C1E" android:layout_marginBottom="8dp"/>
<Button android:id="@+id/btnVideoAgua" android:layout_width="match_parent" android:layout_height="50dp" android:text="Gravar Vídeo (Água)" android:backgroundTint="#0284C7" android:textColor="#FFFFFF" app:cornerRadius="8dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
app:cardCornerRadius="24dp"
android:layout_marginBottom="16dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="💪 Flexões (10x)" android:textSize="16sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="+60 pts" android:textSize="12sp" android:textColor="#8E8E93" android:layout_alignParentEnd="true"/>
</RelativeLayout>
<Button
android:id="@+id/btnGravarEx2"
android:padding="16dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafio 2: Flexões" android:textSize="16sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Grava 10 seg de flexões." android:textSize="14sp" android:textColor="#3A3A3C" android:layout_marginBottom="8dp"/>
<TextView android:id="@+id/tvStatusD1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Estado: Pendente" android:textSize="14sp" android:textStyle="bold" android:textColor="#EF4444" android:layout_marginBottom="8dp"/>
<Button android:id="@+id/btnVideoD1" android:layout_width="match_parent" android:layout_height="50dp" android:text="Gravar Vídeo" android:backgroundTint="#1C1C1E" android:textColor="#FFFFFF" app:cornerRadius="8dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
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_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafio 3: Agachamentos" android:textSize="16sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Grava 10 seg de agachamentos." android:textSize="14sp" android:textColor="#3A3A3C" android:layout_marginBottom="8dp"/>
<TextView android:id="@+id/tvStatusD2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Estado: Pendente" android:textSize="14sp" android:textStyle="bold" android:textColor="#EF4444" android:layout_marginBottom="8dp"/>
<Button android:id="@+id/btnVideoD2" android:layout_width="match_parent" android:layout_height="50dp" android:text="Gravar Vídeo" android:backgroundTint="#1C1C1E" android:textColor="#FFFFFF" app:cornerRadius="8dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafio 4: Prancha" android:textSize="16sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Mantém a prancha por 10 seg." android:textSize="14sp" android:textColor="#3A3A3C" android:layout_marginBottom="8dp"/>
<TextView android:id="@+id/tvStatusD3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Estado: Pendente" android:textSize="14sp" android:textStyle="bold" android:textColor="#EF4444" android:layout_marginBottom="8dp"/>
<Button android:id="@+id/btnVideoD3" android:layout_width="match_parent" android:layout_height="50dp" android:text="Gravar Vídeo" android:backgroundTint="#1C1C1E" android:textColor="#FFFFFF" app:cornerRadius="8dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#F2F2F7">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Desafio 5: Saltos de Tesoura" android:textSize="16sp" android:textStyle="bold" android:textColor="#1C1C1E"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Grava 10 seg de jumping jacks." android:textSize="14sp" android:textColor="#3A3A3C" android:layout_marginBottom="8dp"/>
<TextView android:id="@+id/tvStatusD4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Estado: Pendente" android:textSize="14sp" android:textStyle="bold" android:textColor="#EF4444" android:layout_marginBottom="8dp"/>
<Button android:id="@+id/btnVideoD4" android:layout_width="match_parent" android:layout_height="50dp" android:text="Gravar Vídeo" android:backgroundTint="#1C1C1E" android:textColor="#FFFFFF" app:cornerRadius="8dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>