Compare commits
2 Commits
6844651245
...
d1e83584e3
| Author | SHA1 | Date | |
|---|---|---|---|
| d1e83584e3 | |||
| 0698d5bf69 |
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
muda<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -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" />
|
||||
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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user