From 53c58397506a6b3a02ddb8853f383bfffdc63ca7 Mon Sep 17 00:00:00 2001
From: 230402 <230402@epvc.pt>
Date: Wed, 18 Mar 2026 10:37:27 +0000
Subject: [PATCH] =?UTF-8?q?Corre=C3=A7=C3=A3o=20de=20bugs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/deploymentTargetSelector.xml | 8 +
app/build.gradle.kts | 7 +
app/src/main/AndroidManifest.xml | 48 +-
.../finzora/AdicionarTransacaoActivity.java | 241 ++++----
.../java/com/example/finzora/DBHelper.java | 11 +-
.../example/finzora/DefinicoesActivity.java | 214 +++++--
.../com/example/finzora/DicasFragment.java | 468 +++++++++++---
.../example/finzora/EditarPerfilActivity.java | 100 ++-
.../com/example/finzora/GraficosFragment.java | 257 ++++++--
.../com/example/finzora/LockActivity.java | 84 +++
.../com/example/finzora/LoginActivity.java | 83 ++-
.../com/example/finzora/MainActivity.java | 505 +++++++++++++--
.../example/finzora/NovaPasswordActivity.java | 116 ++++
.../java/com/example/finzora/Objetivo.java | 17 +
.../com/example/finzora/ObjetivosAdapter.java | 119 ++++
.../example/finzora/ObjetivosFragment.java | 326 ++++++++++
.../example/finzora/OnboardingActivity.java | 18 +-
.../com/example/finzora/OrcamentoAdapter.java | 98 ++-
.../example/finzora/OrcamentoFragment.java | 288 +++++++--
.../com/example/finzora/RegisterActivity.java | 41 +-
.../java/com/example/finzora/Transacao.java | 18 +-
.../example/finzora/TransacoesAdapter.java | 173 ++++--
.../example/finzora/TransacoesFragment.java | 209 +++++--
.../com/example/finzora/ViewPagerAdapter.java | 5 +-
app/src/main/res/drawable/bg_bottom_sheet.xml | 8 +
.../main/res/drawable/bg_coach_input_rect.xml | 10 +
app/src/main/res/drawable/bg_coach_rect.xml | 3 +
.../res/drawable/bg_dialog_arredondado.xml | 8 +
app/src/main/res/drawable/bg_search_bar.xml | 11 +
app/src/main/res/drawable/bg_tech_input.xml | 17 +
.../main/res/drawable/custom_progress_bar.xml | 20 +
app/src/main/res/drawable/ic_alimentacao.xml | 3 +
app/src/main/res/drawable/ic_cafe.xml | 3 +
app/src/main/res/drawable/ic_carrinho.xml | 3 +
app/src/main/res/drawable/ic_casa.xml | 3 +
app/src/main/res/drawable/ic_compras.xml | 3 +
app/src/main/res/drawable/ic_contas.xml | 3 +
app/src/main/res/drawable/ic_educacao.xml | 3 +
app/src/main/res/drawable/ic_ginasio.xml | 3 +
app/src/main/res/drawable/ic_lazer.xml | 3 +
app/src/main/res/drawable/ic_outros.xml | 3 +
app/src/main/res/drawable/ic_salario.xml | 3 +
app/src/main/res/drawable/ic_saude.xml | 3 +
app/src/main/res/drawable/ic_telemovel.xml | 3 +
app/src/main/res/drawable/ic_transportes.xml | 3 +
.../layout/activity_adicionar_transacao.xml | 61 +-
.../main/res/layout/activity_definicoes.xml | 75 ++-
.../res/layout/activity_editar_perfil.xml | 63 +-
app/src/main/res/layout/activity_lock.xml | 43 ++
app/src/main/res/layout/activity_main.xml | 43 +-
.../res/layout/activity_nova_password.xml | 113 ++++
.../main/res/layout/bottom_sheet_detalhe.xml | 136 ++++
app/src/main/res/layout/dialog_categorias.xml | 57 ++
app/src/main/res/layout/dialog_contactar.xml | 11 +-
app/src/main/res/layout/dialog_contactos.xml | 141 +++++
app/src/main/res/layout/dialog_eliminar.xml | 78 +++
app/src/main/res/layout/dialog_exportar.xml | 137 +++++
app/src/main/res/layout/dialog_faq.xml | 6 +
.../main/res/layout/dialog_novo_objetivo.xml | 73 +++
.../main/res/layout/dialog_tipo_transacao.xml | 161 +++++
app/src/main/res/layout/dialog_tutorial.xml | 8 +-
app/src/main/res/layout/fragment_dicas.xml | 582 ++++++++----------
app/src/main/res/layout/fragment_graficos.xml | 199 +++---
.../main/res/layout/fragment_objetivos.xml | 56 ++
.../main/res/layout/fragment_orcamento.xml | 167 +++--
.../main/res/layout/fragment_transacoes.xml | 88 ++-
app/src/main/res/layout/item_dropdown.xml | 9 +-
app/src/main/res/layout/item_objetivo.xml | 111 ++++
app/src/main/res/layout/item_orcamento.xml | 62 +-
app/src/main/res/layout/item_transacao.xml | 7 +-
app/src/main/res/raw/anim_dicas_vazio.json | 1 +
app/src/main/res/raw/anim_grafico_vazio.json | 1 +
app/src/main/res/raw/anim_vazio.json | 1 +
app/src/main/res/values-night/colors.xml | 3 +-
app/src/main/res/values/colors.xml | 16 +-
app/src/main/res/values/refs.xml | 3 -
gradle/libs.versions.toml | 2 +
77 files changed, 4921 insertions(+), 1166 deletions(-)
create mode 100644 app/src/main/java/com/example/finzora/LockActivity.java
create mode 100644 app/src/main/java/com/example/finzora/NovaPasswordActivity.java
create mode 100644 app/src/main/java/com/example/finzora/Objetivo.java
create mode 100644 app/src/main/java/com/example/finzora/ObjetivosAdapter.java
create mode 100644 app/src/main/java/com/example/finzora/ObjetivosFragment.java
create mode 100644 app/src/main/res/drawable/bg_bottom_sheet.xml
create mode 100644 app/src/main/res/drawable/bg_coach_input_rect.xml
create mode 100644 app/src/main/res/drawable/bg_coach_rect.xml
create mode 100644 app/src/main/res/drawable/bg_dialog_arredondado.xml
create mode 100644 app/src/main/res/drawable/bg_search_bar.xml
create mode 100644 app/src/main/res/drawable/bg_tech_input.xml
create mode 100644 app/src/main/res/drawable/custom_progress_bar.xml
create mode 100644 app/src/main/res/drawable/ic_alimentacao.xml
create mode 100644 app/src/main/res/drawable/ic_cafe.xml
create mode 100644 app/src/main/res/drawable/ic_carrinho.xml
create mode 100644 app/src/main/res/drawable/ic_casa.xml
create mode 100644 app/src/main/res/drawable/ic_compras.xml
create mode 100644 app/src/main/res/drawable/ic_contas.xml
create mode 100644 app/src/main/res/drawable/ic_educacao.xml
create mode 100644 app/src/main/res/drawable/ic_ginasio.xml
create mode 100644 app/src/main/res/drawable/ic_lazer.xml
create mode 100644 app/src/main/res/drawable/ic_outros.xml
create mode 100644 app/src/main/res/drawable/ic_salario.xml
create mode 100644 app/src/main/res/drawable/ic_saude.xml
create mode 100644 app/src/main/res/drawable/ic_telemovel.xml
create mode 100644 app/src/main/res/drawable/ic_transportes.xml
create mode 100644 app/src/main/res/layout/activity_lock.xml
create mode 100644 app/src/main/res/layout/activity_nova_password.xml
create mode 100644 app/src/main/res/layout/bottom_sheet_detalhe.xml
create mode 100644 app/src/main/res/layout/dialog_categorias.xml
create mode 100644 app/src/main/res/layout/dialog_contactos.xml
create mode 100644 app/src/main/res/layout/dialog_eliminar.xml
create mode 100644 app/src/main/res/layout/dialog_exportar.xml
create mode 100644 app/src/main/res/layout/dialog_novo_objetivo.xml
create mode 100644 app/src/main/res/layout/dialog_tipo_transacao.xml
create mode 100644 app/src/main/res/layout/fragment_objetivos.xml
create mode 100644 app/src/main/res/layout/item_objetivo.xml
create mode 100644 app/src/main/res/raw/anim_dicas_vazio.json
create mode 100644 app/src/main/res/raw/anim_grafico_vazio.json
create mode 100644 app/src/main/res/raw/anim_vazio.json
delete mode 100644 app/src/main/res/values/refs.xml
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..071a253 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b621ebf..41447b0 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -32,6 +32,12 @@ android {
}
dependencies {
+ implementation("androidx.biometric:biometric:1.2.0-alpha05")
+ implementation("com.airbnb.android:lottie:6.3.0")
+ implementation("com.github.bumptech.glide:glide:4.15.1")
+ annotationProcessor("com.github.bumptech.glide:compiler:4.15.1")
+ implementation("com.google.ai.client.generativeai:generativeai:0.9.0")
+ implementation("com.google.guava:guava:31.1-android")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.13.0")
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
@@ -40,6 +46,7 @@ dependencies {
implementation("com.google.code.gson:gson:2.10.1")
implementation(libs.activity)
implementation(libs.constraintlayout)
+ implementation(libs.generativeai)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index be00765..e078032 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,8 @@
package="com.example.finzora">
+
+
+
-
-
+ android:windowSoftInputMode="adjustResize">
+
+
+
+
-
+
+
+
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/AdicionarTransacaoActivity.java b/app/src/main/java/com/example/finzora/AdicionarTransacaoActivity.java
index 11a7e21..7cf8806 100644
--- a/app/src/main/java/com/example/finzora/AdicionarTransacaoActivity.java
+++ b/app/src/main/java/com/example/finzora/AdicionarTransacaoActivity.java
@@ -1,28 +1,30 @@
package com.example.finzora;
import android.app.AlertDialog;
+import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
-import android.widget.ArrayAdapter;
+import android.util.TypedValue;
+import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.Spinner;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import android.widget.Toast;
-
import androidx.appcompat.app.AppCompatActivity;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
+import androidx.cardview.widget.CardView;
public class AdicionarTransacaoActivity extends AppCompatActivity {
- // Declaração dos componentes do ecrã (Sem os RadioButtons!)
- private EditText editValor;
- private Spinner spinnerCategoria;
+ private EditText editValor, editDescricao;
+ private TextView txtCategoria;
private Button btnGuardar;
private ImageView btnVoltar;
+ private String categoriaSelecionada = "";
+ private final String[] categorias = {"Alimentação", "Contas", "Transportes", "Compras", "Lazer", "Educação", "Saúde", "Salário", "Mesada", "Prémios", "Outros"};
+
+ private String idTransacaoParaEditar = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -31,147 +33,160 @@ public class AdicionarTransacaoActivity extends AppCompatActivity {
inicializarComponentes();
- // --- ENCHER O SPINNER COM AS CATEGORIAS ---
- String[] categorias = {"Alimentação", "Transportes", "Lazer", "Educação", "Saúde", "Salário", "Mesada", "Prémios", "Outros"};
+ Intent intent = getIntent();
+ if (intent.hasExtra("transacao_id")) {
+ idTransacaoParaEditar = intent.getStringExtra("transacao_id");
- ArrayAdapter adapter = new ArrayAdapter<>(
- this,
- R.layout.item_dropdown,
- categorias
- );
- adapter.setDropDownViewResource(R.layout.item_dropdown);
- spinnerCategoria.setAdapter(adapter);
+ editValor.setText(String.valueOf(intent.getDoubleExtra("valor", 0.0)));
+ editDescricao.setText(intent.getStringExtra("descricao"));
+ categoriaSelecionada = intent.getStringExtra("categoria");
+ txtCategoria.setText(categoriaSelecionada);
- // Botão para voltar para trás
- if (btnVoltar != null) {
- btnVoltar.setOnClickListener(v -> finish());
+ btnGuardar.setText("ATUALIZAR TRANSAÇÃO");
}
- // AGORA O BOTÃO GUARDAR CHAMA O POP-UP!
+ txtCategoria.setOnClickListener(v -> mostrarDialogCategorias());
+ btnVoltar.setOnClickListener(v -> finish());
btnGuardar.setOnClickListener(v -> perguntarTipoTransacao());
}
- // ====================================================================
- // A MAGIA DO POP-UP
- // ====================================================================
private void perguntarTipoTransacao() {
String valorStr = editValor.getText().toString().trim();
+ if (TextUtils.isEmpty(valorStr)) { editValor.setError("Define o valor"); return; }
+ if (categoriaSelecionada.isEmpty()) { Toast.makeText(this, "Escolhe a categoria!", Toast.LENGTH_SHORT).show(); return; }
- // Primeiro, verifica se ele preencheu o valor antes de perguntar o tipo
- if (TextUtils.isEmpty(valorStr)) {
- editValor.setError("Preenche o valor primeiro!");
- editValor.requestFocus();
- return;
+ if (idTransacaoParaEditar != null) {
+ int tipoOriginal = getIntent().getIntExtra("tipo", 2);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Confirmar Alteração");
+ builder.setMessage("Desejas guardar estas alterações?");
+ builder.setPositiveButton("Confirmar", (dialog, which) -> salvarOuAtualizar(tipoOriginal));
+ builder.setNegativeButton("Cancelar", null);
+ builder.show();
+ } else {
+ View view = getLayoutInflater().inflate(R.layout.dialog_tipo_transacao, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setView(view);
+ AlertDialog dialog = builder.create();
+
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(0));
+ }
+
+ CardView btnReceita = view.findViewById(R.id.btnTipoReceita);
+ CardView btnDespesa = view.findViewById(R.id.btnTipoDespesa);
+ TextView btnCancelar = view.findViewById(R.id.btnCancelarTipo);
+
+ btnReceita.setOnClickListener(v -> {
+ dialog.dismiss();
+ salvarOuAtualizar(1);
+ });
+
+ btnDespesa.setOnClickListener(v -> {
+ dialog.dismiss();
+ salvarOuAtualizar(2);
+ });
+
+ btnCancelar.setOnClickListener(v -> dialog.dismiss());
+
+ dialog.show();
}
-
- // Criar o Pop-up de escolha
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Tipo de Transação");
- builder.setMessage("Esta transação é uma Receita (entrada) ou uma Despesa (saída)?");
-
- // Botão de Receita (Passa o tipo 1 para a base de dados)
- builder.setPositiveButton("Receita 📈", (dialog, which) -> {
- salvarTransacaoNaBaseDeDados(1);
- });
-
- // Botão de Despesa (Passa o tipo 2 para a base de dados)
- builder.setNegativeButton("Despesa 📉", (dialog, which) -> {
- salvarTransacaoNaBaseDeDados(2);
- });
-
- // Botão Cancelar (Caso o utilizador queira fechar e alterar algo)
- builder.setNeutralButton("Cancelar", (dialog, which) -> {
- dialog.dismiss();
- });
-
- // Mostrar o Pop-up no ecrã
- AlertDialog dialog = builder.create();
- dialog.show();
}
- // ====================================================================
- // GUARDAR NA BASE DE DADOS APÓS A ESCOLHA
- // ====================================================================
- private void salvarTransacaoNaBaseDeDados(int tipoEscolhido) {
- // Primeiro, vamos buscar o "Carimbo" (ID) de quem está a usar a app
- android.content.SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
- String userId = prefs.getString("user_id", null);
+ // 🏆 NOVO MENU DE CATEGORIAS (PREMIUM)
+ // 🏆 NOVO MENU DE CATEGORIAS (PREMIUM)
+ private void mostrarDialogCategorias() {
+ View view = getLayoutInflater().inflate(R.layout.dialog_categorias, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setView(view);
+ AlertDialog dialog = builder.create();
- if (userId == null) {
- android.widget.Toast.makeText(this, "Erro: Utilizador não identificado. Faz login novamente.", android.widget.Toast.LENGTH_LONG).show();
- return;
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(0));
}
+ LinearLayout container = view.findViewById(R.id.containerCategorias);
+ TextView btnCancelar = view.findViewById(R.id.btnCancelarCategoria);
+
+ // Gera a lista de categorias visualmente perfeita
+ for (String cat : categorias) {
+ TextView tv = new TextView(this);
+ tv.setText(cat);
+ tv.setTextSize(16f); // ⚠️ CORRIGIDO AQUI PARA JAVA!
+
+ // Rouba a cor certa ao ecrã (Claro/Escuro) para não falhar
+ tv.setTextColor(txtCategoria.getCurrentTextColor());
+
+ // Muito espaço para ser fácil clicar com o dedo
+ tv.setPadding(32, 40, 32, 40);
+
+ // Efeito de onda ao clicar (Ripple Effect nativo)
+ android.util.TypedValue outValue = new android.util.TypedValue();
+ getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
+ tv.setBackgroundResource(outValue.resourceId);
+ tv.setClickable(true);
+ tv.setFocusable(true);
+
+ tv.setOnClickListener(v -> {
+ categoriaSelecionada = cat;
+ txtCategoria.setText(categoriaSelecionada);
+ dialog.dismiss();
+ });
+
+ container.addView(tv);
+ }
+ btnCancelar.setOnClickListener(v -> dialog.dismiss());
+ dialog.show();
+ }
+ private void salvarOuAtualizar(int tipoEscolhido) {
+ android.content.SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+ if (userId == null) return;
+
try {
- String valorStr = editValor.getText().toString().trim();
- valorStr = valorStr.replace(",", "."); // Evitar crashes com vírgulas
- double valor = Double.parseDouble(valorStr);
- String categoria = spinnerCategoria.getSelectedItem().toString();
+ double valor = Double.parseDouble(editValor.getText().toString().replace(",", "."));
+ String descricao = editDescricao.getText().toString().trim();
String dataStr = new java.text.SimpleDateFormat("dd/MM/yyyy", java.util.Locale.getDefault()).format(new java.util.Date());
- // Mudar o texto do botão para o utilizador perceber que está a gravar
btnGuardar.setEnabled(false);
- btnGuardar.setText("A GRAVAR NAS NUVENS...");
+ btnGuardar.setText("A GUARDAR...");
- // Preparar o cliente de Internet
okhttp3.OkHttpClient client = new okhttp3.OkHttpClient();
- // Construir o JSON que vai viajar até ao Supabase
- // AQUI ESTÁ A CORREÇÃO: a coluna chama-se "data" para bater certo com o teu SQL!
- String json = "{"
- + "\"user_id\":\"" + userId + "\", "
- + "\"valor\":" + valor + ", "
- + "\"categoria\":\"" + categoria + "\", "
- + "\"tipo\":" + tipoEscolhido + ", "
- + "\"data\":\"" + dataStr + "\""
- + "}";
-
+ String json = "{\"user_id\":\"" + userId + "\", \"valor\":" + valor + ", \"categoria\":\"" + categoriaSelecionada + "\", \"tipo\":" + tipoEscolhido + ", \"descricao\":\"" + descricao + "\", \"data\":\"" + dataStr + "\"}";
okhttp3.RequestBody body = okhttp3.RequestBody.create(json, okhttp3.MediaType.parse("application/json; charset=utf-8"));
- // Fazer o pedido POST para a tabela "transacoes" do Supabase
+ String url = SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes";
+ String metodo = "POST";
+
+ if (idTransacaoParaEditar != null) {
+ url += "?id=eq." + idTransacaoParaEditar;
+ metodo = "PATCH";
+ }
+
okhttp3.Request request = new okhttp3.Request.Builder()
- .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes")
+ .url(url)
.addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
.addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
.addHeader("Content-Type", "application/json")
- .addHeader("Prefer", "return=minimal") // Diz ao Supabase para não devolver os dados de volta
- .post(body)
+ .method(metodo, body)
.build();
- // Executar o envio em segundo plano para não bloquear o ecrã
client.newCall(request).enqueue(new okhttp3.Callback() {
- @Override
- public void onFailure(@androidx.annotation.NonNull okhttp3.Call call, @androidx.annotation.NonNull java.io.IOException e) {
- runOnUiThread(() -> {
- btnGuardar.setEnabled(true);
- btnGuardar.setText("Guardar Transação");
- android.widget.Toast.makeText(AdicionarTransacaoActivity.this, "Erro de net! A transação não foi guardada.", android.widget.Toast.LENGTH_SHORT).show();
- });
+ @Override public void onFailure(okhttp3.Call call, java.io.IOException e) {
+ runOnUiThread(() -> { btnGuardar.setEnabled(true); btnGuardar.setText("GUARDAR"); });
}
-
- @Override
- public void onResponse(@androidx.annotation.NonNull okhttp3.Call call, @androidx.annotation.NonNull okhttp3.Response response) throws java.io.IOException {
- runOnUiThread(() -> {
- if (response.isSuccessful()) {
- android.widget.Toast.makeText(AdicionarTransacaoActivity.this, "Transação guardada com sucesso! 🎉", android.widget.Toast.LENGTH_SHORT).show();
- finish(); // Volta ao ecrã principal
- } else {
- btnGuardar.setEnabled(true);
- btnGuardar.setText("Guardar Transação");
- android.widget.Toast.makeText(AdicionarTransacaoActivity.this, "Erro no Supabase. Tenta novamente.", android.widget.Toast.LENGTH_LONG).show();
- }
- });
+ @Override public void onResponse(okhttp3.Call call, okhttp3.Response response) {
+ runOnUiThread(() -> { if (response.isSuccessful()) finish(); });
}
});
-
- } catch (Exception e) {
- android.widget.Toast.makeText(this, "ERRO LOCAL: " + e.getMessage(), android.widget.Toast.LENGTH_LONG).show();
- e.printStackTrace();
- }
+ } catch (Exception e) { Toast.makeText(this, "Erro: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }
}
+
private void inicializarComponentes() {
editValor = findViewById(R.id.editValor);
- spinnerCategoria = findViewById(R.id.spinnerCategoria);
+ txtCategoria = findViewById(R.id.txtCategoriaTransacao);
+ editDescricao = findViewById(R.id.editDescricaoTransacao);
btnGuardar = findViewById(R.id.btnGuardar);
btnVoltar = findViewById(R.id.btnVoltar);
}
diff --git a/app/src/main/java/com/example/finzora/DBHelper.java b/app/src/main/java/com/example/finzora/DBHelper.java
index e1160d0..db5cb11 100644
--- a/app/src/main/java/com/example/finzora/DBHelper.java
+++ b/app/src/main/java/com/example/finzora/DBHelper.java
@@ -56,12 +56,13 @@ public class DBHelper extends SQLiteOpenHelper {
if (cursor.moveToFirst()) {
do {
+ // ⚠️ A JOGADA MÁGICA: Transformar o int antigo numa String!
lista.add(new Transacao(
- cursor.getInt(0), // id
- cursor.getFloat(1), // valor
- cursor.getString(2),// categoria
- cursor.getInt(3), // tipo
- cursor.getString(4) // data
+ String.valueOf(cursor.getInt(0)), // id convertido para texto
+ cursor.getFloat(1), // valor
+ cursor.getString(2), // categoria
+ cursor.getInt(3), // tipo
+ cursor.getString(4) // data
));
} while (cursor.moveToNext());
}
diff --git a/app/src/main/java/com/example/finzora/DefinicoesActivity.java b/app/src/main/java/com/example/finzora/DefinicoesActivity.java
index f7a040a..5d94f09 100644
--- a/app/src/main/java/com/example/finzora/DefinicoesActivity.java
+++ b/app/src/main/java/com/example/finzora/DefinicoesActivity.java
@@ -1,10 +1,13 @@
package com.example.finzora;
+import android.app.AlertDialog;
import android.app.Dialog;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.Button;
@@ -12,9 +15,20 @@ import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
public class DefinicoesActivity extends AppCompatActivity {
@Override
@@ -22,19 +36,19 @@ public class DefinicoesActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_definicoes);
- // Ligar todos os botões do ecrã ao nosso código Java
TextView btnVoltarDefinicoes = findViewById(R.id.btnVoltarDefinicoes);
TextView btnEditarPerfil = findViewById(R.id.btnEditarPerfil);
Switch switchModoEscuro = findViewById(R.id.switchModoEscuro);
Switch switchNotificacoes = findViewById(R.id.switchNotificacoes);
+ Switch switchBiometria = findViewById(R.id.switchBiometria);
TextView btnSuporte = findViewById(R.id.btnSuporte);
Button btnTerminarSessao = findViewById(R.id.btnTerminarSessao);
+ Button btnEliminarConta = findViewById(R.id.btnEliminarConta);
// --- 0. BOTÃO DE VOLTAR ---
btnVoltarDefinicoes.setOnClickListener(v -> finish());
// --- 1. EDITAR PERFIL ---
- // Agora já abre o novo ecrã de Edição de Perfil!
btnEditarPerfil.setOnClickListener(v -> {
startActivity(new Intent(DefinicoesActivity.this, EditarPerfilActivity.class));
});
@@ -56,7 +70,22 @@ public class DefinicoesActivity extends AppCompatActivity {
}
});
- // --- 3. NOTIFICAÇÕES ---
+ // --- 3. MAGIA DA BIOMETRIA (SEGURANÇA) ---
+ SharedPreferences prefsUser = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
+ boolean usarBiometria = prefsUser.getBoolean("usar_biometria", false);
+ switchBiometria.setChecked(usarBiometria);
+
+ switchBiometria.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ prefsUser.edit().putBoolean("usar_biometria", isChecked).apply();
+
+ if (isChecked) {
+ Toast.makeText(this, "A app vai pedir o teu dedo ao abrir! 🔒", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Bloqueio Desativado 🔓", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ // --- 4. NOTIFICAÇÕES ---
switchNotificacoes.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
Toast.makeText(this, "Notificações Ligadas 🔔", Toast.LENGTH_SHORT).show();
@@ -65,11 +94,111 @@ public class DefinicoesActivity extends AppCompatActivity {
}
});
- // --- 4. CENTRO DE SUPORTE (POP-UP) ---
+ // --- 5. CENTRO DE SUPORTE (POP-UP) ---
btnSuporte.setOnClickListener(v -> mostrarDialogSuporte());
- // --- 5. TERMINAR SESSÃO ---
+ // --- 6. TERMINAR SESSÃO ---
btnTerminarSessao.setOnClickListener(v -> terminarSessao());
+
+ // --- 7. APAGAR CONTA ---
+ btnEliminarConta.setOnClickListener(v -> mostrarAvisoEliminar());
+ }
+
+ private void mostrarAvisoEliminar() {
+ new AlertDialog.Builder(this)
+ .setTitle("⚠️ Ação Irreversível")
+ .setMessage("Tens a certeza? Todos os teus orçamentos e transações serão apagados para sempre da nossa nuvem, de acordo com as normas de proteção de dados (RGPD).")
+ .setPositiveButton("Sim, Apagar Tudo", (dialog, which) -> executarLimpezaDeDados())
+ .setNegativeButton("Cancelar", null)
+ .show();
+ }
+
+ private void executarLimpezaDeDados() {
+ SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+
+ if (userId == null) {
+ Toast.makeText(this, "ERRO: Não encontrei o teu ID no telemóvel!", Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ OkHttpClient client = new OkHttpClient();
+ Toast.makeText(this, "A apagar transações... 🗑️", Toast.LENGTH_SHORT).show();
+
+ Request reqTransacoes = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .delete()
+ .build();
+
+ client.newCall(reqTransacoes).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
+ runOnUiThread(() -> Toast.makeText(DefinicoesActivity.this, "Erro de net a apagar transações!", Toast.LENGTH_SHORT).show());
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ apagarOrcamentos(userId);
+ }
+ });
+ }
+
+ private void apagarOrcamentos(String userId) {
+ OkHttpClient client = new OkHttpClient();
+
+ Request reqOrcamentos = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/orcamentos?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .delete()
+ .build();
+
+ client.newCall(reqOrcamentos).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) { }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+
+ String jsonParams = "{\"id_alvo\":\"" + userId + "\"}";
+ RequestBody body = RequestBody.create(jsonParams, MediaType.parse("application/json; charset=utf-8"));
+
+ Request reqApagarConta = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/rpc/apagar_conta_finzora")
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .post(body)
+ .build();
+
+ client.newCall(reqApagarConta).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
+ runOnUiThread(() -> Toast.makeText(DefinicoesActivity.this, "Erro a apagar a conta final!", Toast.LENGTH_LONG).show());
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ final String respostaSupabase = response.body() != null ? response.body().string() : "";
+
+ runOnUiThread(() -> {
+ if (response.isSuccessful()) {
+ getSharedPreferences("DadosUtilizador", MODE_PRIVATE).edit().clear().apply();
+ Toast.makeText(DefinicoesActivity.this, "Conta e dados eliminados para sempre. 🧹", Toast.LENGTH_LONG).show();
+
+ Intent intent = new Intent(DefinicoesActivity.this, LoginActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ finish();
+ } else {
+ Toast.makeText(DefinicoesActivity.this, "Erro: " + respostaSupabase, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+ });
+ }
+ });
}
private void mostrarDialogSuporte() {
@@ -78,13 +207,11 @@ public class DefinicoesActivity extends AppCompatActivity {
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- // O Botão de Fechar o Pop-up principal
dialog.findViewById(R.id.btnFecharSuporte).setOnClickListener(v -> dialog.dismiss());
- // Ligar os cliques nos Cartões
dialog.findViewById(R.id.cardFAQ).setOnClickListener(v -> {
- dialog.dismiss(); // Fecha este menu
- mostrarDialogFAQ(); // Abre o FAQ
+ dialog.dismiss();
+ mostrarDialogFAQ();
});
dialog.findViewById(R.id.cardTutorial).setOnClickListener(v -> {
@@ -92,30 +219,62 @@ public class DefinicoesActivity extends AppCompatActivity {
mostrarDialogTutorial();
});
+ // ✉️ CONTACTAR SUPORTE: Abre a app do Gmail
dialog.findViewById(R.id.cardMensagem).setOnClickListener(v -> {
dialog.dismiss();
- mostrarDialogContactar();
+ enviarEmailProfissional();
});
+ // 📞 CONTACTOS DIRETOS: Abre o nosso Novo Design Premium!
dialog.findViewById(R.id.cardContactos).setOnClickListener(v -> {
- Toast.makeText(this, "Email: suporte@finzora.pt\nTel: +351 800 123 456", Toast.LENGTH_LONG).show();
+ dialog.dismiss();
+ mostrarDialogContactosInfo();
});
dialog.show();
}
+ // 🏆 A NOVA FUNÇÃO QUE CHAMA O DESIGN PREMIUM
+ private void mostrarDialogContactosInfo() {
+ Dialog dialog = new Dialog(this);
+ dialog.setContentView(R.layout.dialog_contactos);
+
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ // Quando clica em Voltar, fechamos este e abrimos o menu de Suporte outra vez!
+ dialog.findViewById(R.id.btnFecharContactos).setOnClickListener(v -> {
+ dialog.dismiss();
+ mostrarDialogSuporte();
+ });
+
+ dialog.show();
+ }
+
+ private void enviarEmailProfissional() {
+ Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
+ emailIntent.setData(Uri.parse("mailto:suporte@finzora.pt"));
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Suporte Finzora - Ajuda");
+
+ try {
+ startActivity(Intent.createChooser(emailIntent, "Abrir com..."));
+ } catch (ActivityNotFoundException ex) {
+ Toast.makeText(this, "Não tens nenhuma app de e-mail instalada no telemóvel!", Toast.LENGTH_LONG).show();
+ }
+ }
+
private void mostrarDialogFAQ() {
Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_faq);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- // Setinha de voltar (Fecha o FAQ e volta a abrir o menu principal)
dialog.findViewById(R.id.btnVoltarFAQ).setOnClickListener(v -> {
dialog.dismiss();
mostrarDialogSuporte();
});
-
dialog.show();
}
@@ -125,39 +284,10 @@ public class DefinicoesActivity extends AppCompatActivity {
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- // Setinha de voltar
dialog.findViewById(R.id.btnVoltarTutorial).setOnClickListener(v -> {
dialog.dismiss();
mostrarDialogSuporte();
});
-
- dialog.show();
- }
-
- private void mostrarDialogContactar() {
- Dialog dialog = new Dialog(this);
- dialog.setContentView(R.layout.dialog_contactar);
- dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- // Setinha de voltar
- dialog.findViewById(R.id.btnVoltarContactar).setOnClickListener(v -> {
- dialog.dismiss();
- mostrarDialogSuporte();
- });
-
- // Botões do formulário
- dialog.findViewById(R.id.btnCancelarContacto).setOnClickListener(v -> {
- dialog.dismiss();
- mostrarDialogSuporte();
- });
-
- dialog.findViewById(R.id.btnEnviarMensagem).setOnClickListener(v -> {
- Toast.makeText(this, "Mensagem enviada com sucesso!", Toast.LENGTH_SHORT).show();
- dialog.dismiss();
- mostrarDialogSuporte();
- });
-
dialog.show();
}
diff --git a/app/src/main/java/com/example/finzora/DicasFragment.java b/app/src/main/java/com/example/finzora/DicasFragment.java
index 4b87698..c3b2edf 100644
--- a/app/src/main/java/com/example/finzora/DicasFragment.java
+++ b/app/src/main/java/com/example/finzora/DicasFragment.java
@@ -1,41 +1,66 @@
package com.example.finzora;
-import android.content.res.ColorStateList;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
public class DicasFragment extends Fragment {
- // Componentes de Saúde Financeira
private TextView tvTaxaPoupanca, tvDicasReceitas, tvDicasDespesas;
private ProgressBar progressPoupanca;
-
- // Componentes das Dicas
- private TextView tvTituloDica1, tvDescDica1;
- private TextView tvTituloDica2, tvDescDica2;
-
- // Distribuição de Gastos
+ private TextView tvTituloDica1, tvDescDica1, tvTituloDica2, tvDescDica2, tvTituloDica3, tvDescDica3;
private LinearLayout layoutDistribuicao;
- private DBHelper dbHelper;
+ private View layoutConteudoDicas;
+ private View layoutEstadoVazioDicas;
+
+ private TextView tvRespostaAI;
+ private EditText editPerguntaAI;
+ private ImageButton btnEnviarAI;
+ private ProgressBar pbCarregandoAI;
+
+ private int corFundoCartao;
+ private int corTextoDinamico;
+
+ private String contextoFinanceiroParaAI = "O utilizador ainda não tem dados financeiros registados.";
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_dicas, container, false);
- // Ligar os componentes
tvTaxaPoupanca = view.findViewById(R.id.tvTaxaPoupanca);
tvDicasReceitas = view.findViewById(R.id.tvDicasReceitas);
tvDicasDespesas = view.findViewById(R.id.tvDicasDespesas);
@@ -45,119 +70,386 @@ public class DicasFragment extends Fragment {
tvDescDica1 = view.findViewById(R.id.tvDescDica1);
tvTituloDica2 = view.findViewById(R.id.tvTituloDica2);
tvDescDica2 = view.findViewById(R.id.tvDescDica2);
+ tvTituloDica3 = view.findViewById(R.id.tvTituloDica3);
+ tvDescDica3 = view.findViewById(R.id.tvDescDica3);
layoutDistribuicao = view.findViewById(R.id.layoutDistribuicao);
+ layoutConteudoDicas = view.findViewById(R.id.layoutConteudoDicas);
+ layoutEstadoVazioDicas = view.findViewById(R.id.layoutEstadoVazioDicas);
- dbHelper = new DBHelper(getActivity());
+ tvRespostaAI = view.findViewById(R.id.tvRespostaAI);
+ editPerguntaAI = view.findViewById(R.id.editPerguntaAI);
+ btnEnviarAI = view.findViewById(R.id.btnEnviarAI);
+ pbCarregandoAI = view.findViewById(R.id.pbCarregandoAI);
+
+ if (getContext() != null) {
+ corFundoCartao = ContextCompat.getColor(getContext(), R.color.fundo_cartao);
+ corTextoDinamico = ContextCompat.getColor(getContext(), R.color.texto_principal);
+ }
+
+ btnEnviarAI.setOnClickListener(v -> perguntarAoNovoCoach());
return view;
}
+ private void perguntarAoNovoCoach() {
+ String pergunta = editPerguntaAI.getText().toString().trim();
+ if (pergunta.isEmpty()) return;
+
+ pbCarregandoAI.setVisibility(View.VISIBLE);
+ tvRespostaAI.setText("A analisar os dados de forma inteligente...");
+ editPerguntaAI.setText("");
+
+ OkHttpClient client = new OkHttpClient();
+
+ JSONObject jsonBody = new JSONObject();
+ try {
+ jsonBody.put("model", "llama-3.1-8b-instant");
+ JSONArray messages = new JSONArray();
+
+ String regrasBase = "És o Assistente de IA da Finzora, um consultor financeiro altamente profissional e analítico. " +
+ "Usa ESTRITAMENTE o Português de Portugal (PT-PT). Trata o utilizador SEMPRE por 'tu'. " +
+ "Sê natural, claro, focado em literacia financeira e responde com um máximo de 3 ou 4 parágrafos curtos. ";
+
+ JSONObject systemMsg = new JSONObject();
+ systemMsg.put("role", "system");
+ systemMsg.put("content", regrasBase + contextoFinanceiroParaAI);
+ messages.put(systemMsg);
+
+ JSONObject userMsg = new JSONObject();
+ userMsg.put("role", "user");
+ userMsg.put("content", pergunta);
+ messages.put(userMsg);
+
+ jsonBody.put("messages", messages);
+ } catch (Exception e) { e.printStackTrace(); }
+
+ RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.parse("application/json; charset=utf-8"));
+ String groqApiKey = "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";
+
+ Request request = new Request.Builder()
+ .url("https://api.groq.com/openai/v1/chat/completions")
+ .addHeader("Authorization", "Bearer " + groqApiKey)
+ .post(body)
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ pbCarregandoAI.setVisibility(View.GONE);
+ tvRespostaAI.setText("Erro de ligação ao serviço de Inteligência Artificial.");
+ });
+ }
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ final String respBody = response.body() != null ? response.body().string() : "";
+
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ pbCarregandoAI.setVisibility(View.GONE);
+ if (response.isSuccessful()) {
+ try {
+ JSONObject jsonObject = new JSONObject(respBody);
+ String respostaIA = jsonObject.getJSONArray("choices")
+ .getJSONObject(0).getJSONObject("message").getString("content");
+
+ String textoFormatado = respostaIA.replaceAll("\\*\\*(.*?)\\*\\*", "$1");
+ textoFormatado = textoFormatado.replace("\n", "
");
+ tvRespostaAI.setText(android.text.Html.fromHtml(textoFormatado, android.text.Html.FROM_HTML_MODE_LEGACY));
+
+ } catch (Exception e) {
+ tvRespostaAI.setText("Erro a ler os dados da análise: " + e.getMessage());
+ }
+ } else {
+ tvRespostaAI.setText("O Assistente não está disponível neste momento.");
+ }
+ });
+ }
+ }
+ });
+ }
+
@Override
public void onResume() {
super.onResume();
- analisarFinancas();
+ analisarFinancasDaNuvem();
}
- private void analisarFinancas() {
- float receitas = dbHelper.getTotalReceitas();
- float despesas = dbHelper.getTotalDespesas();
+ private void analisarFinancasDaNuvem() {
+ if (getActivity() == null) return;
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+ if (userId == null) return;
- // 1. Atualizar Textos Iniciais
- tvDicasReceitas.setText(String.format("€ %.2f", receitas));
- tvDicasDespesas.setText(String.format("€ %.2f", despesas));
+ OkHttpClient client = new OkHttpClient();
- // 2. Calcular Taxa de Poupança
- float taxaPoupanca = 0;
- if (receitas > 0) {
- taxaPoupanca = ((receitas - despesas) / receitas) * 100;
- }
+ Request requestTransacoes = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
- // Se gastou mais do que ganhou, a taxa é 0
- if (taxaPoupanca < 0) taxaPoupanca = 0;
+ client.newCall(requestTransacoes).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) { }
- tvTaxaPoupanca.setText(String.format("%.1f%%", taxaPoupanca));
- progressPoupanca.setProgress((int) taxaPoupanca);
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (!response.isSuccessful()) return;
+ try {
+ String body = response.body().string();
+ JSONArray array = new JSONArray(body);
+ float rec = 0, desp = 0;
+ HashMap mapaGastos = new HashMap<>();
- // Cores consoante a saúde financeira
- if (taxaPoupanca >= 20) {
- tvTaxaPoupanca.setTextColor(Color.parseColor("#00E676")); // Verde
- progressPoupanca.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#00E676")));
+ for (int i = 0; i < array.length(); i++) {
+ JSONObject obj = array.getJSONObject(i);
+ float v = (float) obj.getDouble("valor");
+ if (obj.getInt("tipo") == 1) rec += v;
+ else {
+ desp += v;
+ String cat = obj.getString("categoria");
+ mapaGastos.put(cat, mapaGastos.getOrDefault(cat, 0f) + v);
+ }
+ }
- tvTituloDica1.setText("Excelente Taxa de Poupança! \uD83C\uDF1F");
- tvTituloDica1.setTextColor(Color.parseColor("#00E676"));
- tvDescDica1.setText("Estás a poupar " + String.format("%.1f", taxaPoupanca) + "% dos teus rendimentos. Continua com este ótimo hábito financeiro!");
- } else if (taxaPoupanca > 0) {
- tvTaxaPoupanca.setTextColor(Color.parseColor("#FFD600")); // Amarelo
- progressPoupanca.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#FFD600")));
+ buscarOrcamentosECriarCerebro(userId, client, rec, desp, mapaGastos);
- tvTituloDica1.setText("Atenção à Poupança \uD83D\uDD0D");
- tvTituloDica1.setTextColor(Color.parseColor("#FFD600"));
- tvDescDica1.setText("Estás a poupar muito pouco. A meta recomendada é guardar pelo menos 20% do que ganhas.");
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ });
+ }
+
+ private void buscarOrcamentosECriarCerebro(String userId, OkHttpClient client, float rec, float desp, HashMap mapaGastos) {
+ Request requestOrcamentos = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/orcamentos?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
+
+ client.newCall(requestOrcamentos).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (!response.isSuccessful()) return;
+ try {
+ String body = response.body().string();
+ JSONArray arrayOrcamentos = new JSONArray(body);
+
+ // 🧠 RECONSTRUIR O CÉREBRO DA IA COM TODOS OS DETALHES!
+ StringBuilder cerebro = new StringBuilder();
+ cerebro.append("DADOS FINANCEIROS ATUAIS DO UTILIZADOR: ");
+ cerebro.append("Receitas Totais: ").append(rec).append("€. ");
+ cerebro.append("Despesas Totais: ").append(desp).append("€. ");
+
+ // Passar as categorias onde gastaste dinheiro
+ if (!mapaGastos.isEmpty()) {
+ cerebro.append("Gastos por categoria: ");
+ for (Map.Entry entry : mapaGastos.entrySet()) {
+ cerebro.append(entry.getKey()).append(" (").append(entry.getValue()).append("€), ");
+ }
+ }
+
+ if (arrayOrcamentos.length() > 0) {
+ cerebro.append(". ORÇAMENTOS DEFINIDOS: ");
+ for (int i = 0; i < arrayOrcamentos.length(); i++) {
+ JSONObject obj = arrayOrcamentos.getJSONObject(i);
+ String cat = obj.getString("categoria");
+ float limite = (float) obj.getDouble("valor_limite");
+ float gasto = mapaGastos.containsKey(cat) ? mapaGastos.get(cat) : 0f;
+ cerebro.append("[").append(cat).append(": Limite definido ").append(limite).append("€, Gasto atual ").append(gasto).append("€] ");
+ }
+ } else {
+ cerebro.append(". O utilizador não tem orçamentos definidos de momento. ");
+ }
+
+ // ⚠️ A JOGADA QUE FALTAVA: Guardar a memória na variável que a IA vai ler!
+ contextoFinanceiroParaAI = cerebro.toString();
+
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> aplicarLogicaDeDicas(rec, desp, mapaGastos, arrayOrcamentos));
+ }
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ });
+ }
+
+ private void aplicarLogicaDeDicas(float rec, float desp, HashMap mapa, JSONArray arrayOrcamentos) {
+
+ if (rec == 0 && desp == 0) {
+ layoutConteudoDicas.setVisibility(View.GONE);
+ layoutEstadoVazioDicas.setVisibility(View.VISIBLE);
+ return;
} else {
- tvTaxaPoupanca.setTextColor(Color.parseColor("#FF1744")); // Vermelho
- progressPoupanca.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#FF1744")));
-
- tvTituloDica1.setText("Alerta Vermelho! \uD83D\uDEA8");
- tvTituloDica1.setTextColor(Color.parseColor("#FF1744"));
- tvDescDica1.setText("Os teus gastos superam ou igualam os teus ganhos. Verifica urgentemente para onde está a ir o teu dinheiro!");
+ layoutConteudoDicas.setVisibility(View.VISIBLE);
+ layoutEstadoVazioDicas.setVisibility(View.GONE);
}
- // 3. Descobrir a Categoria mais gasta
- HashMap gastosPorCategoria = dbHelper.getDespesasPorCategoria();
- String piorCategoria = "Nenhuma";
- float maiorGasto = 0;
+ // --- TOPO: RESUMO ---
+ tvDicasReceitas.setText(String.format("€ %.2f", rec));
+ tvDicasDespesas.setText(String.format("€ %.2f", desp));
+ float taxa = (rec > 0) ? ((rec - desp) / rec) * 100 : 0;
+ if (taxa < 0) taxa = 0;
+ tvTaxaPoupanca.setText(String.format("%.1f%%", taxa));
+ progressPoupanca.setProgress((int) taxa);
- for (Map.Entry entry : gastosPorCategoria.entrySet()) {
- if (entry.getValue() > maiorGasto) {
- maiorGasto = entry.getValue();
- piorCategoria = entry.getKey();
+ // --- CARTÃO 1: REGRA 50/30/20 ---
+ float necessidades = 0; float desejos = 0;
+ for (Map.Entry entry : mapa.entrySet()) {
+ String cat = entry.getKey().toLowerCase();
+ float val = entry.getValue();
+ if (cat.contains("conta") || cat.contains("alimen") || cat.contains("saúd") || cat.contains("educa") || cat.contains("casa") || cat.contains("transp")) {
+ necessidades += val;
+ } else {
+ desejos += val;
}
}
- if (maiorGasto > 0) {
- float percPiorCategoria = (maiorGasto / despesas) * 100;
- tvTituloDica2.setText("Gastos Elevados em " + piorCategoria);
- tvTituloDica2.setTextColor(Color.parseColor("#FF1744"));
- tvDescDica2.setText(String.format("%.1f%%", percPiorCategoria) + " das tuas despesas são em " + piorCategoria + " (€ " + String.format("%.2f", maiorGasto) + "). Tenta reduzir aqui!");
+ if (rec > 0) {
+ float percNecessidades = (necessidades / rec) * 100;
+ float percDesejos = (desejos / rec) * 100;
+
+ if (percNecessidades <= 50 && percDesejos <= 30) {
+ tvTituloDica1.setText("Balanço Perfeito ⚖️");
+ tvTituloDica1.setTextColor(Color.parseColor("#00E676"));
+ tvDescDica1.setText("Estás a cumprir a Regra de Ouro (50/30/20). Os teus gastos essenciais e de lazer estão equilibrados face aos teus rendimentos.");
+ } else if (percDesejos > 30) {
+ tvTituloDica1.setText("Atenção aos Gastos Supérfluos 🛍️");
+ tvTituloDica1.setTextColor(Color.parseColor("#ECC94B"));
+ tvDescDica1.setText(String.format("A alocação em despesas não essenciais representa %.0f%% do teu orçamento. Recomenda-se reduzir para a margem dos 30%%.", percDesejos));
+ } else {
+ tvTituloDica1.setText("Despesas Fixas Elevadas 🏠");
+ tvTituloDica1.setTextColor(Color.parseColor("#F56565"));
+ tvDescDica1.setText(String.format("Os teus encargos fixos representam %.0f%% do salário. O indicador ideal para manter a estabilidade financeira é de 50%%.", percNecessidades));
+ }
} else {
- tvTituloDica2.setText("Tudo Controlado ✅");
- tvTituloDica2.setTextColor(Color.parseColor("#00E676"));
- tvDescDica2.setText("Ainda não tens despesas suficientes para analisarmos. Continua o bom trabalho!");
+ tvTituloDica1.setText("Regra 50/30/20 ⚖️");
+ tvTituloDica1.setTextColor(corTextoDinamico);
+ tvDescDica1.setText("Regista receitas para que possamos calcular a distribuição ideal do teu património.");
}
- // 4. Construir as barras de Distribuição de Gastos magicamente
- layoutDistribuicao.removeAllViews(); // Limpa as barras antigas
+ // --- CARTÃO 2: RADAR DE ORÇAMENTOS ---
+ String alertaOrcamento = "Todos os orçamentos definidos encontram-se dentro dos limites previstos.";
+ int corAlerta = Color.parseColor("#00E676");
+ String tituloAlerta = "Orçamentos Controlados ✅";
- if (despesas > 0) {
- for (Map.Entry entry : gastosPorCategoria.entrySet()) {
- float valorCat = entry.getValue();
- if (valorCat > 0) {
- float percentagem = (valorCat / despesas) * 100;
+ try {
+ float maiorRisco = 0;
+ String catRisco = "";
+ float faltaParaLimite = 0;
- // Criar o título da categoria (Ex: Alimentação - €50.00 (20%))
- TextView tvCat = new TextView(getActivity());
- tvCat.setText(entry.getKey() + " — € " + String.format("%.2f", valorCat) + " (" + (int) percentagem + "%)");
- tvCat.setTextColor(Color.WHITE);
- tvCat.setTextSize(14f);
- tvCat.setPadding(0, 16, 0, 8); // Margens
+ for (int i = 0; i < arrayOrcamentos.length(); i++) {
+ JSONObject obj = arrayOrcamentos.getJSONObject(i);
+ String cat = obj.getString("categoria");
+ float limite = (float) obj.getDouble("valor_limite");
+ float gasto = mapa.containsKey(cat) ? mapa.get(cat) : 0f;
- // Criar a barra de progresso horizontal
- ProgressBar pb = new ProgressBar(getActivity(), null, android.R.attr.progressBarStyleHorizontal);
- pb.setMax(100);
- pb.setProgress((int) percentagem);
- pb.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#00E5FF"))); // Azul Tech
-
- // Adicionar ao ecrã
- layoutDistribuicao.addView(tvCat);
- layoutDistribuicao.addView(pb);
+ if (limite > 0) {
+ float risco = gasto / limite;
+ if (risco > maiorRisco) {
+ maiorRisco = risco;
+ catRisco = cat;
+ faltaParaLimite = limite - gasto;
+ }
}
}
+
+ if (maiorRisco >= 1.0) {
+ tituloAlerta = "Orçamento Excedido 🚨";
+ corAlerta = Color.parseColor("#FF1744");
+ alertaOrcamento = "O limite definido para a categoria '" + catRisco + "' foi ultrapassado. Sugere-se o reajuste das restantes categorias.";
+ } else if (maiorRisco >= 0.8) {
+ tituloAlerta = "Aviso de Limite Próximo ⚠️";
+ corAlerta = Color.parseColor("#ECC94B");
+ alertaOrcamento = String.format("Atenção: A margem disponível para o orçamento de '%s' é de apenas %.2f€.", catRisco, faltaParaLimite);
+ } else if (arrayOrcamentos.length() == 0) {
+ tituloAlerta = "Planeamento Financeiro 🎯";
+ corAlerta = corTextoDinamico;
+ alertaOrcamento = "Acede ao separador 'Orçamentos' e estabelece limites para otimizar a tua gestão financeira.";
+ }
+ } catch (Exception e) { e.printStackTrace(); }
+
+ tvTituloDica2.setText(tituloAlerta);
+ tvTituloDica2.setTextColor(corAlerta);
+ tvDescDica2.setText(alertaOrcamento);
+
+ // --- CARTÃO 3: PREVISÃO E TENDÊNCIA DIÁRIA ---
+ Calendar cal = Calendar.getInstance();
+ int diaAtual = cal.get(Calendar.DAY_OF_MONTH);
+ int diasNoMes = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+
+ if (desp > 0 && diaAtual > 0) {
+ float mediaDiaria = desp / diaAtual;
+ float previsaoFimDoMes = mediaDiaria * diasNoMes;
+
+ if (previsaoFimDoMes > rec && rec > 0) {
+ tvTituloDica3.setText("Projeção Mensal Elevada 📈");
+ tvTituloDica3.setTextColor(Color.parseColor("#FF1744"));
+ tvDescDica3.setText(String.format("A média de custos diários situa-se em %.2f€. Mantendo esta tendência, o custo final estimado será de %.2f€ (acima dos rendimentos).", mediaDiaria, previsaoFimDoMes));
+ } else {
+ tvTituloDica3.setText("Projeção Mensal Controlada 📉");
+ tvTituloDica3.setTextColor(Color.parseColor("#00E676"));
+ tvDescDica3.setText(String.format("A tua média de custos é de %.2f€ diários. A estimativa projetada para o final do mês é de %.2f€.", mediaDiaria, previsaoFimDoMes));
+ }
} else {
- TextView semDespesas = new TextView(getActivity());
- semDespesas.setText("Ainda não existem despesas registadas.");
- semDespesas.setTextColor(Color.parseColor("#B0BEC5"));
- layoutDistribuicao.addView(semDespesas);
+ tvTituloDica3.setText("Projeção de Despesas 📊");
+ tvTituloDica3.setTextColor(corTextoDinamico);
+ tvDescDica3.setText("Regista mais movimentos ao longo do mês para que o sistema possa projetar a tua média diária de despesas.");
+ }
+
+ // --- LISTA DE TOP DESPESAS ---
+ layoutDistribuicao.removeAllViews();
+ for (Map.Entry entry : mapa.entrySet()) {
+ String categoria = entry.getKey();
+ float valor = entry.getValue();
+
+ LinearLayout row = new LinearLayout(getContext());
+ row.setOrientation(LinearLayout.HORIZONTAL);
+ row.setPadding(40, 30, 40, 30);
+ row.setElevation(2f);
+
+ GradientDrawable shape = new GradientDrawable();
+ shape.setCornerRadius(24f);
+ shape.setColor(corFundoCartao);
+ row.setBackground(shape);
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.setMargins(0, 0, 0, 20);
+ row.setLayoutParams(params);
+
+ String emoji = "💰";
+ String catLower = categoria.toLowerCase();
+ if (catLower.contains("alimen") || catLower.contains("restaurante")) emoji = "🍔";
+ else if (catLower.contains("transp") || catLower.contains("carro")) emoji = "🚗";
+ else if (catLower.contains("lazer") || catLower.contains("divers")) emoji = "🎮";
+ else if (catLower.contains("saúd") || catLower.contains("farmácia")) emoji = "💊";
+ else if (catLower.contains("educa")) emoji = "📚";
+ else if (catLower.contains("casa") || catLower.contains("renda") || catLower.contains("conta")) emoji = "🏠";
+ else if (catLower.contains("compras") || catLower.contains("roupa")) emoji = "🛍️";
+
+ TextView tvCat = new TextView(getContext());
+ tvCat.setText(emoji + " " + categoria);
+ tvCat.setTextColor(corTextoDinamico);
+ tvCat.setTextSize(16f);
+ tvCat.setTypeface(null, Typeface.BOLD);
+ tvCat.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
+
+ TextView tvVal = new TextView(getContext());
+ tvVal.setText(String.format("€ %.2f", valor));
+ tvVal.setTextColor(Color.parseColor("#FF1744"));
+ tvVal.setTextSize(16f);
+ tvVal.setTypeface(null, Typeface.BOLD);
+
+ row.addView(tvCat);
+ row.addView(tvVal);
+ layoutDistribuicao.addView(row);
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/EditarPerfilActivity.java b/app/src/main/java/com/example/finzora/EditarPerfilActivity.java
index c30b3b8..7579100 100644
--- a/app/src/main/java/com/example/finzora/EditarPerfilActivity.java
+++ b/app/src/main/java/com/example/finzora/EditarPerfilActivity.java
@@ -1,42 +1,90 @@
package com.example.finzora;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageView;
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 com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
public class EditarPerfilActivity extends AppCompatActivity {
private EditText editNomePerfil;
private EditText editEmailPerfil;
+ private ImageView imgFotoPerfil;
+ private String caminhoFotoGuardada = null;
+
+ private final ActivityResultLauncher seletorImagens =
+ registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
+ if (uri != null) {
+ String novoCaminho = guardarImagemInternamente(uri);
+ if (novoCaminho != null) {
+ caminhoFotoGuardada = novoCaminho;
+
+ imgFotoPerfil.setPadding(0, 0, 0, 0);
+ imgFotoPerfil.setImageTintList(null);
+
+ Glide.with(this)
+ .load(new File(caminhoFotoGuardada))
+ .skipMemoryCache(true)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .circleCrop()
+ .into(imgFotoPerfil);
+ }
+ }
+ });
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_editar_perfil);
- // Ligar ao XML
TextView btnVoltar = findViewById(R.id.btnVoltarEditarPerfil);
editNomePerfil = findViewById(R.id.editNomePerfil);
editEmailPerfil = findViewById(R.id.editEmailPerfil);
Button btnGuardarPerfil = findViewById(R.id.btnGuardarPerfil);
+ imgFotoPerfil = findViewById(R.id.imgFotoPerfil);
- // Voltar para as definições
btnVoltar.setOnClickListener(v -> finish());
+ imgFotoPerfil.setOnClickListener(v -> seletorImagens.launch("image/*"));
- // 1. CARREGAR OS DADOS ATUAIS DA MEMÓRIA
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
- String nomeAtual = prefs.getString("nome_usuario", "Investidor");
- String emailAtual = prefs.getString("email_usuario", ""); // Pode estar vazio se não guardaste no login
+ editNomePerfil.setText(prefs.getString("nome_usuario", "Investidor"));
+ editEmailPerfil.setText(prefs.getString("email_usuario", ""));
- editNomePerfil.setText(nomeAtual);
- editEmailPerfil.setText(emailAtual);
+ caminhoFotoGuardada = prefs.getString("foto_usuario_path", null);
+ if (caminhoFotoGuardada != null) {
+ File arquivoFoto = new File(caminhoFotoGuardada);
+ if (arquivoFoto.exists()) {
+ imgFotoPerfil.setPadding(0, 0, 0, 0);
+ imgFotoPerfil.setImageTintList(null);
+
+ Glide.with(this)
+ .load(arquivoFoto)
+ .skipMemoryCache(true)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .circleCrop()
+ .into(imgFotoPerfil);
+ }
+ }
- // 2. GUARDAR OS DADOS NOVOS
btnGuardarPerfil.setOnClickListener(v -> {
String novoNome = editNomePerfil.getText().toString().trim();
String novoEmail = editEmailPerfil.getText().toString().trim();
@@ -46,14 +94,46 @@ public class EditarPerfilActivity extends AppCompatActivity {
return;
}
- // Grava na memória (SharedPreferences)
SharedPreferences.Editor editor = prefs.edit();
editor.putString("nome_usuario", novoNome);
editor.putString("email_usuario", novoEmail);
+
+ if (caminhoFotoGuardada != null) {
+ editor.putString("foto_usuario_path", caminhoFotoGuardada);
+ }
+
editor.apply();
+ // ⚠️ BACKUP TÁTICO DO NOME: Escrever no disco rígido para sobreviver ao Logout!
+ try {
+ FileOutputStream fos = openFileOutput("nome_perfil.txt", MODE_PRIVATE);
+ fos.write(novoNome.getBytes());
+ fos.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
Toast.makeText(this, "Perfil atualizado com sucesso! 🎉", Toast.LENGTH_SHORT).show();
- finish(); // Fecha o ecrã e volta atrás
+ finish();
});
}
+
+ private String guardarImagemInternamente(Uri uri) {
+ try {
+ InputStream inputStream = getContentResolver().openInputStream(uri);
+ Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
+ File pasta = getFilesDir();
+ File arquivoFoto = new File(pasta, "foto_perfil.jpg");
+
+ FileOutputStream out = new FileOutputStream(arquivoFoto);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
+ out.flush();
+ out.close();
+
+ return arquivoFoto.getAbsolutePath();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/GraficosFragment.java b/app/src/main/java/com/example/finzora/GraficosFragment.java
index 4f3bd4c..bd18614 100644
--- a/app/src/main/java/com/example/finzora/GraficosFragment.java
+++ b/app/src/main/java/com/example/finzora/GraficosFragment.java
@@ -1,5 +1,7 @@
package com.example.finzora;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -7,6 +9,7 @@ import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.charts.PieChart;
@@ -18,16 +21,32 @@ import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
import com.github.mikephil.charting.formatter.IndexAxisValueFormatter;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
public class GraficosFragment extends Fragment {
private PieChart pieChartDespesas;
private BarChart barChartOrcamento;
private BarChart barChartTendencia;
- private DBHelper dbHelper;
+
+ // ⚠️ 1. As Vistas do Estado Vazio
+ private View scrollviewGraficos;
+ private View layoutEstadoVazio;
+
+ private int corTextoDinamica;
@Nullable
@Override
@@ -37,7 +56,16 @@ public class GraficosFragment extends Fragment {
pieChartDespesas = view.findViewById(R.id.pieChartDespesas);
barChartOrcamento = view.findViewById(R.id.barChartOrcamento);
barChartTendencia = view.findViewById(R.id.barChartTendencia);
- dbHelper = new DBHelper(getActivity());
+
+ // ⚠️ 2. Ligar ao XML
+ scrollviewGraficos = view.findViewById(R.id.scrollviewGraficos);
+ layoutEstadoVazio = view.findViewById(R.id.layoutEstadoVazioGraficos);
+
+ if (getContext() != null) {
+ corTextoDinamica = ContextCompat.getColor(getContext(), R.color.texto_principal);
+ } else {
+ corTextoDinamica = Color.BLACK;
+ }
return view;
}
@@ -45,25 +73,134 @@ public class GraficosFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
- carregarPieChart();
- carregarBarChartOrcamento();
- carregarBarChartTendencia();
+ carregarDadosDaNuvem();
}
- // ==========================================
- // 1. GRÁFICO CIRCULAR (Despesas por Categoria)
- // ==========================================
- private void carregarPieChart() {
+ private void carregarDadosDaNuvem() {
+ if (getActivity() == null) return;
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+ if (userId == null) return;
+
+ OkHttpClient client = new OkHttpClient();
+
+ Request reqTransacoes = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
+
+ client.newCall(reqTransacoes).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (!response.isSuccessful()) return;
+
+ try {
+ String jsonTransacoes = response.body().string();
+ JSONArray arrTransacoes = new JSONArray(jsonTransacoes);
+
+ float somaReceitas = 0;
+ float somaDespesas = 0;
+ Map gastosPorCategoria = new HashMap<>();
+
+ for (int i = 0; i < arrTransacoes.length(); i++) {
+ JSONObject obj = arrTransacoes.getJSONObject(i);
+ int tipo = obj.getInt("tipo");
+ float valor = (float) obj.getDouble("valor");
+ String categoria = obj.getString("categoria");
+
+ if (tipo == 1) {
+ somaReceitas += valor;
+ } else if (tipo == 2) {
+ somaDespesas += valor;
+ float atual = gastosPorCategoria.containsKey(categoria) ? gastosPorCategoria.get(categoria) : 0f;
+ gastosPorCategoria.put(categoria, atual + valor);
+ }
+ }
+
+ final float totalReceitas = somaReceitas;
+ final float totalDespesas = somaDespesas;
+ final Map mapaGastos = gastosPorCategoria;
+
+ // ⚠️ 3. A Lógica de Mostrar/Esconder
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ // Se as receitas E as despesas forem 0, é porque não há transações nenhumas!
+ if (totalReceitas == 0 && totalDespesas == 0) {
+ scrollviewGraficos.setVisibility(View.GONE);
+ layoutEstadoVazio.setVisibility(View.VISIBLE);
+ } else {
+ scrollviewGraficos.setVisibility(View.VISIBLE);
+ layoutEstadoVazio.setVisibility(View.GONE);
+
+ // O utilizador tem dados! Vamos buscar os orçamentos para cruzar a informação
+ carregarOrcamentosEDesenhar(userId, mapaGastos, totalReceitas, totalDespesas);
+ }
+ });
+ }
+
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ });
+ }
+
+ private void carregarOrcamentosEDesenhar(String userId, Map mapaGastos, float totalReceitas, float totalDespesas) {
+ OkHttpClient client = new OkHttpClient();
+ Request reqOrcamentos = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/orcamentos?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
+
+ client.newCall(reqOrcamentos).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response2) throws IOException {
+ if (!response2.isSuccessful()) return;
+
+ try {
+ String jsonOrcamentos = response2.body().string();
+ JSONArray arrOrcamentos = new JSONArray(jsonOrcamentos);
+ Map limitesOrcamento = new HashMap<>();
+
+ for (int i = 0; i < arrOrcamentos.length(); i++) {
+ JSONObject obj = arrOrcamentos.getJSONObject(i);
+ limitesOrcamento.put(obj.getString("categoria"), (float) obj.getDouble("valor_limite"));
+ }
+
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ desenharPieChart(mapaGastos);
+ desenharBarChartOrcamento(limitesOrcamento, mapaGastos);
+ desenharBarChartTendencia(totalReceitas, totalDespesas);
+ });
+ }
+
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ });
+ }
+
+ private void desenharPieChart(Map despesas) {
pieChartDespesas.getDescription().setEnabled(false);
- pieChartDespesas.setHoleColor(Color.parseColor("#2C5364"));
- pieChartDespesas.getLegend().setTextColor(Color.WHITE);
+ pieChartDespesas.setHoleColor(Color.TRANSPARENT);
+
+ pieChartDespesas.getLegend().setTextColor(corTextoDinamica);
+ pieChartDespesas.getLegend().setTextSize(12f);
+ pieChartDespesas.getLegend().setWordWrapEnabled(true);
+
pieChartDespesas.setCenterText("Despesas");
- pieChartDespesas.setCenterTextColor(Color.WHITE);
- pieChartDespesas.setEntryLabelColor(Color.WHITE);
+ pieChartDespesas.setCenterTextColor(corTextoDinamica);
+ pieChartDespesas.setCenterTextSize(16f);
+
+ pieChartDespesas.setDrawEntryLabels(false);
- HashMap despesas = dbHelper.getDespesasPorCategoria();
ArrayList entradas = new ArrayList<>();
-
for (Map.Entry entry : despesas.entrySet()) {
if (entry.getValue() > 0) entradas.add(new PieEntry(entry.getValue(), entry.getKey()));
}
@@ -72,21 +209,18 @@ public class GraficosFragment extends Fragment {
PieDataSet dataSet = new PieDataSet(entradas, "");
dataSet.setColors(new int[]{Color.parseColor("#7C4DFF"), Color.parseColor("#00E5FF"), Color.parseColor("#FFD600"), Color.parseColor("#FF4081")});
+ dataSet.setSliceSpace(3f);
PieData data = new PieData(dataSet);
data.setValueTextSize(14f);
- data.setValueTextColor(Color.WHITE);
+ data.setValueTextColor(corTextoDinamica);
pieChartDespesas.setData(data);
pieChartDespesas.animateY(1000);
}
- // ==========================================
- // 2. GRÁFICO DE BARRAS (Orçamento vs Gastos)
- // ==========================================
- private void carregarBarChartOrcamento() {
+ private void desenharBarChartOrcamento(Map orcamentos, Map gastos) {
configurarEstiloBarChart(barChartOrcamento);
- Map orcamentos = dbHelper.getOrcamentosDefinidos();
ArrayList gastosEntries = new ArrayList<>();
ArrayList orcamentoEntries = new ArrayList<>();
ArrayList categorias = new ArrayList<>();
@@ -95,7 +229,7 @@ public class GraficosFragment extends Fragment {
for (Map.Entry entry : orcamentos.entrySet()) {
String categoria = entry.getKey();
float limite = entry.getValue();
- float gasto = dbHelper.getGastoPorCategoria(categoria);
+ float gasto = gastos.containsKey(categoria) ? gastos.get(categoria) : 0f;
categorias.add(categoria);
gastosEntries.add(new BarEntry(index, gasto));
@@ -106,83 +240,96 @@ public class GraficosFragment extends Fragment {
if (categorias.isEmpty()) { barChartOrcamento.clear(); return; }
BarDataSet setGastos = new BarDataSet(gastosEntries, "Gastos Reais");
- setGastos.setColor(Color.parseColor("#FF4081")); // Rosa (Figma)
- setGastos.setValueTextColor(Color.WHITE);
+ setGastos.setColor(Color.parseColor("#FF4081"));
+ setGastos.setValueTextColor(corTextoDinamica);
BarDataSet setOrcamento = new BarDataSet(orcamentoEntries, "Orçamento");
- setOrcamento.setColor(Color.parseColor("#00E5FF")); // Azul (Figma)
- setOrcamento.setValueTextColor(Color.WHITE);
+ setOrcamento.setColor(Color.parseColor("#00E5FF"));
+ setOrcamento.setValueTextColor(corTextoDinamica);
BarData data = new BarData(setGastos, setOrcamento);
- // Lógica de agrupamento (Grouped Bar Chart)
- float groupSpace = 0.2f; float barSpace = 0.05f; float barWidth = 0.35f;
+
+ // ⚠️ A MATEMÁTICA PERFEITA (tem de somar 1.00)
+ float barWidth = 0.35f;
+ float barSpace = 0.05f;
+ float groupSpace = 0.20f;
+
data.setBarWidth(barWidth);
barChartOrcamento.setData(data);
- barChartOrcamento.groupBars(-0.5f, groupSpace, barSpace);
- // Labels no Eixo X
+ // ⚠️ COMEÇA NO 0 PARA ALINHAR AO CENTRO
+ barChartOrcamento.groupBars(0f, groupSpace, barSpace);
+
XAxis xAxis = barChartOrcamento.getXAxis();
xAxis.setValueFormatter(new IndexAxisValueFormatter(categorias));
- xAxis.setAxisMinimum(-0.5f);
- xAxis.setAxisMaximum(categorias.size() - 0.5f);
+
+ // ⚠️ LIMITES DINÂMICOS PARA ENCAIXAR OS GRUPOS TODOS
+ xAxis.setAxisMinimum(0f);
+ xAxis.setAxisMaximum(barChartOrcamento.getBarData().getGroupWidth(groupSpace, barSpace) * categorias.size());
+
+ // Para não ficar tudo esmagado se tiveres muitas categorias, mete limite visível a 4
+ barChartOrcamento.setVisibleXRangeMaximum(4);
barChartOrcamento.animateY(1000);
+ barChartOrcamento.invalidate(); // Refresca o gráfico
}
- // ==========================================
- // 3. GRÁFICO DE BARRAS (Tendência Mensal Geral)
- // ==========================================
- private void carregarBarChartTendencia() {
+ private void desenharBarChartTendencia(float receitas, float despesas) {
configurarEstiloBarChart(barChartTendencia);
- float totalReceitas = dbHelper.getTotalReceitas();
- float totalDespesas = dbHelper.getTotalDespesas();
-
ArrayList despesaEntry = new ArrayList<>();
ArrayList receitaEntry = new ArrayList<>();
- despesaEntry.add(new BarEntry(0, totalDespesas));
- receitaEntry.add(new BarEntry(0, totalReceitas));
+ despesaEntry.add(new BarEntry(0, despesas));
+ receitaEntry.add(new BarEntry(0, receitas));
BarDataSet setDespesas = new BarDataSet(despesaEntry, "Despesas");
- setDespesas.setColor(Color.parseColor("#FF1744")); // Vermelho
- setDespesas.setValueTextColor(Color.WHITE);
+ setDespesas.setColor(Color.parseColor("#FF1744"));
+ setDespesas.setValueTextColor(corTextoDinamica);
BarDataSet setReceitas = new BarDataSet(receitaEntry, "Receitas");
- setReceitas.setColor(Color.parseColor("#00E676")); // Verde
- setReceitas.setValueTextColor(Color.WHITE);
+ setReceitas.setColor(Color.parseColor("#00E676"));
+ setReceitas.setValueTextColor(corTextoDinamica);
BarData data = new BarData(setDespesas, setReceitas);
- float groupSpace = 0.3f; float barSpace = 0.05f; float barWidth = 0.3f;
+ // ⚠️ MATEMÁTICA PERFEITA PARA A TENDÊNCIA
+ float groupSpace = 0.3f;
+ float barSpace = 0.05f;
+ float barWidth = 0.3f;
+
data.setBarWidth(barWidth);
barChartTendencia.setData(data);
- barChartTendencia.groupBars(-0.5f, groupSpace, barSpace);
+
+ // ⚠️ COMEÇA NO 0 TAMBÉM
+ barChartTendencia.groupBars(0f, groupSpace, barSpace);
ArrayList labelMes = new ArrayList<>();
labelMes.add("Atual");
XAxis xAxis = barChartTendencia.getXAxis();
xAxis.setValueFormatter(new IndexAxisValueFormatter(labelMes));
- xAxis.setAxisMinimum(-0.5f);
- xAxis.setAxisMaximum(0.5f);
+
+ // ⚠️ COMO É SÓ 1 GRUPO, O MÁXIMO É 1
+ xAxis.setAxisMinimum(0f);
+ xAxis.setAxisMaximum(1f);
barChartTendencia.animateY(1000);
+ barChartTendencia.invalidate();
}
- // Função de limpeza de design comum aos dois gráficos de barras
private void configurarEstiloBarChart(BarChart chart) {
chart.getDescription().setEnabled(false);
- chart.getLegend().setTextColor(Color.WHITE);
- chart.getAxisRight().setEnabled(false); // Esconde números à direita
+ chart.getLegend().setTextColor(corTextoDinamica);
+ chart.getAxisRight().setEnabled(false);
- chart.getAxisLeft().setTextColor(Color.WHITE);
+ chart.getAxisLeft().setTextColor(corTextoDinamica);
chart.getAxisLeft().setDrawGridLines(true);
- chart.getAxisLeft().setGridColor(Color.parseColor("#455A64")); // Linhas de fundo subtis
+ chart.getAxisLeft().setGridColor(Color.LTGRAY);
XAxis xAxis = chart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
- xAxis.setTextColor(Color.WHITE);
+ xAxis.setTextColor(corTextoDinamica);
xAxis.setDrawGridLines(false);
xAxis.setGranularity(1f);
xAxis.setCenterAxisLabels(true);
diff --git a/app/src/main/java/com/example/finzora/LockActivity.java b/app/src/main/java/com/example/finzora/LockActivity.java
new file mode 100644
index 0000000..e19cf2a
--- /dev/null
+++ b/app/src/main/java/com/example/finzora/LockActivity.java
@@ -0,0 +1,84 @@
+package com.example.finzora;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.widget.Button;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.core.content.ContextCompat;
+
+import java.util.concurrent.Executor;
+
+public class LockActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_lock);
+
+ Button btnDesbloquearApp = findViewById(R.id.btnDesbloquearApp);
+
+ btnDesbloquearApp.setOnClickListener(v -> solicitarBiometria());
+
+ // ⚠️ CORREÇÃO 1: Esperar meio segundo para o telemóvel real não entrar em pânico
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ solicitarBiometria();
+ }, 500);
+ }
+
+ private void solicitarBiometria() {
+ BiometricManager biometricManager = BiometricManager.from(this);
+
+ // ⚠️ CORREÇÃO 2: Mudamos para BIOMETRIC_WEAK para ser compatível com mais telemóveis reais
+ int authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
+
+ int canAuthenticate = biometricManager.canAuthenticate(authenticators);
+
+ if (canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS) {
+ Executor executor = ContextCompat.getMainExecutor(this);
+ BiometricPrompt biometricPrompt = new BiometricPrompt(LockActivity.this,
+ executor, new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
+ super.onAuthenticationError(errorCode, errString);
+ // Se der erro 10 (User Canceled), não fazemos nada, ele clica no botão se quiser tentar de novo
+ if (errorCode != 10) {
+ Toast.makeText(getApplicationContext(), "Erro: " + errString, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ startActivity(new Intent(LockActivity.this, MainActivity.class));
+ finish();
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ super.onAuthenticationFailed();
+ Toast.makeText(getApplicationContext(), "Biometria não reconhecida.", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
+ .setTitle("Finzora: Escudo de Privacidade")
+ .setSubtitle("Usa a impressão digital ou o PIN do telemóvel")
+ .setAllowedAuthenticators(authenticators) // ⚠️ CORREÇÃO 3: Usar os mesmos flags aqui
+ .build();
+
+ biometricPrompt.authenticate(promptInfo);
+ } else {
+ // Se cair aqui, o Toast vai dizer o código do erro para investigarmos
+ Toast.makeText(this, "Escudo Inativo (Erro: " + canAuthenticate + "). A aceder...", Toast.LENGTH_LONG).show();
+ startActivity(new Intent(LockActivity.this, MainActivity.class));
+ finish();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/LoginActivity.java b/app/src/main/java/com/example/finzora/LoginActivity.java
index d7c387d..aa0033b 100644
--- a/app/src/main/java/com/example/finzora/LoginActivity.java
+++ b/app/src/main/java/com/example/finzora/LoginActivity.java
@@ -12,6 +12,9 @@ import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.textfield.TextInputEditText;
import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import okhttp3.Call;
@@ -36,28 +39,36 @@ public class LoginActivity extends AppCompatActivity {
boolean jaDeuLogin = prefs.getBoolean("is_logged_in", false);
if (jaDeuLogin) {
- // Já tem o carimbo! Vai direto para o ecrã principal.
- Intent intent = new Intent(LoginActivity.this, MainActivity.class);
- startActivity(intent);
- finish();
- return; // Para o código aqui para não desenhar o ecrã de login
- }
+ boolean usarBiometria = prefs.getBoolean("usar_biometria", false);
+ if (usarBiometria) {
+ startActivity(new Intent(LoginActivity.this, LockActivity.class));
+ } else {
+ startActivity(new Intent(LoginActivity.this, MainActivity.class));
+ }
+ finish();
+ return;
+ }
setContentView(R.layout.activity_login);
+ Intent intentDeepLink = getIntent();
+ if (intentDeepLink != null && Intent.ACTION_VIEW.equals(intentDeepLink.getAction())) {
+ android.net.Uri uri = intentDeepLink.getData();
+ if (uri != null && "finzora".equals(uri.getScheme()) && "confirmado".equals(uri.getHost())) {
+ Toast.makeText(this, "✅ Conta confirmada com sucesso! Já podes entrar.", Toast.LENGTH_LONG).show();
+ }
+ }
+
inicializarComponentes();
- // Clique no botão "Entrar" -> Agora faz Login de verdade!
btnEntrar.setOnClickListener(v -> validarDados());
- // Clique para ir para Registo
txtRegistrar.setOnClickListener(v -> {
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
});
- // Clique no "Esqueci-me da palavra-passe" -> Agora abre o novo ecrã!
txtEsqueciPassword.setOnClickListener(v -> {
startActivity(new Intent(LoginActivity.this, RecuperarPasswordActivity.class));
});
@@ -74,7 +85,6 @@ public class LoginActivity extends AppCompatActivity {
editPassword.setError("Introduza a sua palavra-passe");
editPassword.requestFocus();
} else {
- // Desativa o botão enquanto pensa
btnEntrar.setEnabled(false);
btnEntrar.setText("A VERIFICAR DADOS...");
fazerLoginNoSupabase(email, password);
@@ -87,7 +97,6 @@ public class LoginActivity extends AppCompatActivity {
String json = "{\"email\":\"" + email + "\", \"password\":\"" + password + "\"}";
RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
- // URL para fazer login (grant_type=password)
Request request = new Request.Builder()
.url(SupabaseConfig.SUPABASE_URL + "/auth/v1/token?grant_type=password")
.addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
@@ -114,7 +123,6 @@ public class LoginActivity extends AppCompatActivity {
btnEntrar.setText("INICIAR SESSÃO");
if (response.isSuccessful()) {
- // SUCESSO! A palavra-passe estava certa!
try {
JSONObject jsonResponse = new JSONObject(responseData);
String userId = jsonResponse.getJSONObject("user").getString("id");
@@ -122,30 +130,65 @@ public class LoginActivity extends AppCompatActivity {
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean("is_logged_in", true); // O nosso carimbo!
- editor.putString("user_id", userId); // O ID do Supabase
- editor.putString("email_usuario", email);// Guardamos o email para o Perfil
+ editor.putBoolean("is_logged_in", true);
+ editor.putString("user_id", userId);
+ editor.putString("email_usuario", email);
+
+ // 🏆 A MAGIA ACONTECE AQUI: Recuperar Nome e Foto!
+
+ // 1. Recuperar Nome do Backup Físico
+ String nomeRecuperado = "Investidor";
+ try {
+ FileInputStream fis = openFileInput("nome_perfil.txt");
+ byte[] bytes = new byte[fis.available()];
+ fis.read(bytes);
+ fis.close();
+ nomeRecuperado = new String(bytes);
+ } catch (Exception e) {
+ // Se não tiver backup, tenta ver se a Base de Dados devolveu algum nome
+ try {
+ JSONObject meta = jsonResponse.getJSONObject("user").optJSONObject("user_metadata");
+ if (meta != null && meta.has("nome")) nomeRecuperado = meta.getString("nome");
+ } catch (Exception ignored) {}
+ }
+ editor.putString("nome_usuario", nomeRecuperado);
+
+ // 2. Recuperar Caminho da Foto (Ela está sempre guardada na pasta principal)
+ File arquivoFoto = new File(getFilesDir(), "foto_perfil.jpg");
+ if (arquivoFoto.exists()) {
+ editor.putString("foto_usuario_path", arquivoFoto.getAbsolutePath());
+ }
+
editor.apply();
Toast.makeText(LoginActivity.this, "Bem-vindo de volta!", Toast.LENGTH_SHORT).show();
- irParaDashboard();
+
+ irParaSeguranca();
} catch (Exception e) {
Toast.makeText(LoginActivity.this, "Erro a processar os dados.", Toast.LENGTH_SHORT).show();
}
} else {
- // ERRO! Palavra-passe errada ou email não existe!
Toast.makeText(LoginActivity.this, "Credenciais incorretas. Tenta novamente!", Toast.LENGTH_LONG).show();
editPassword.setError("Palavra-passe errada");
- editPassword.setText(""); // Limpa a password para ele tentar de novo
+ editPassword.setText("");
}
});
}
});
}
- private void irParaDashboard() {
- Intent intent = new Intent(LoginActivity.this, MainActivity.class);
+ private void irParaSeguranca() {
+ SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
+ boolean usarBiometria = prefs.getBoolean("usar_biometria", false);
+
+ Intent intent;
+ if (usarBiometria) {
+ intent = new Intent(LoginActivity.this, LockActivity.class);
+ } else {
+ intent = new Intent(LoginActivity.this, MainActivity.class);
+ }
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
diff --git a/app/src/main/java/com/example/finzora/MainActivity.java b/app/src/main/java/com/example/finzora/MainActivity.java
index 6740c69..9e3e313 100644
--- a/app/src/main/java/com/example/finzora/MainActivity.java
+++ b/app/src/main/java/com/example/finzora/MainActivity.java
@@ -1,16 +1,31 @@
package com.example.finzora;
+import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.pdf.PdfDocument;
import android.os.Bundle;
+import android.os.Environment;
+import android.view.LayoutInflater;
+import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
+
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
+import androidx.cardview.widget.CardView;
import androidx.viewpager2.widget.ViewPager2;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
@@ -18,7 +33,12 @@ import com.google.android.material.tabs.TabLayoutMediator;
import org.json.JSONArray;
import org.json.JSONObject;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
import okhttp3.Call;
import okhttp3.Callback;
@@ -35,6 +55,7 @@ public class MainActivity extends AppCompatActivity {
private Button btnSair;
private TextView tvSaldoGeral, tvReceitasGeral, tvDespesasGeral;
+ private JSONArray listaTransacoesGlobal;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -65,60 +86,480 @@ public class MainActivity extends AppCompatActivity {
String nome = prefs.getString("nome_usuario", "Investidor");
tvNomeUsuario.setText("Olá, " + nome);
- btnSair.setOnClickListener(v -> {
- prefs.edit().clear().apply();
- startActivity(new Intent(this, LoginActivity.class));
- finish();
- });
+ btnSair.setOnClickListener(v -> finishAffinity());
- fabAdicionar.setOnClickListener(v -> {
- startActivity(new Intent(this, AdicionarTransacaoActivity.class));
- });
+ fabAdicionar.setOnClickListener(v -> startActivity(new Intent(this, AdicionarTransacaoActivity.class)));
ImageView btnAbrirDefinicoes = findViewById(R.id.btnAbrirDefinicoes);
if (btnAbrirDefinicoes != null) {
- btnAbrirDefinicoes.setOnClickListener(v -> {
- startActivity(new Intent(MainActivity.this, DefinicoesActivity.class));
+ btnAbrirDefinicoes.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, DefinicoesActivity.class)));
+ }
+
+ ImageView imgLogoPerfil = findViewById(R.id.imgLogo);
+ if (imgLogoPerfil != null) {
+ imgLogoPerfil.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, EditarPerfilActivity.class)));
+ }
+
+ ImageView btnExportarPDF = findViewById(R.id.btnExportarPDF);
+ if (btnExportarPDF != null) {
+ btnExportarPDF.setOnClickListener(v -> {
+ if (listaTransacoesGlobal != null && listaTransacoesGlobal.length() > 0) {
+ mostrarDialogoExportacao();
+ } else {
+ Toast.makeText(this, "Ainda não tens dados para exportar!", Toast.LENGTH_SHORT).show();
+ }
});
}
configurarAbas();
- atualizarCartoes(); // Chama a nova função ligada à net!
+ atualizarCartoes();
+ carregarFotoPerfil();
+
+ // 🛡️ TÁTICA DE VISIBILIDADE DO BOTÃO +
+ viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
+ @Override
+ public void onPageSelected(int position) {
+ super.onPageSelected(position);
+ // Posições: 0 = Transações, 1 = Orçamentos, 2 = Gráficos, 3 = Objetivos, 4 = Dicas
+ if (position == 0 || position == 1) {
+ fabAdicionar.show(); // Só mostra nas Transações e Orçamentos
+ } else {
+ fabAdicionar.hide(); // Esconde nos Gráficos, Objetivos e Dicas
+ }
+ }
+ });
+ }
+
+ private void mostrarDialogoExportacao() {
+ View view = getLayoutInflater().inflate(R.layout.dialog_exportar, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setView(view);
+ AlertDialog dialog = builder.create();
+
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new ColorDrawable(0));
+ }
+
+ CardView btnPdf = view.findViewById(R.id.btnOpcaoPDF);
+ CardView btnExcel = view.findViewById(R.id.btnOpcaoExcel);
+ TextView btnCancelar = view.findViewById(R.id.btnCancelarExportacao);
+
+ btnPdf.setOnClickListener(v -> {
+ gerarRelatorioPDF(listaTransacoesGlobal);
+ dialog.dismiss();
+ });
+
+ btnExcel.setOnClickListener(v -> {
+ gerarRelatorioCSV(listaTransacoesGlobal);
+ dialog.dismiss();
+ });
+
+ btnCancelar.setOnClickListener(v -> dialog.dismiss());
+ dialog.show();
+ }
+
+ private void gerarRelatorioCSV(JSONArray transacoes) {
+ StringBuilder csvData = new StringBuilder();
+ csvData.append("Data,Descricao,Categoria,Tipo,Valor(Euros)\n");
+
+ try {
+ for (int i = 0; i < transacoes.length(); i++) {
+ JSONObject obj = transacoes.getJSONObject(i);
+ String data = obj.optString("data", "---");
+ String desc = obj.optString("descricao", "---").replace(",", " ");
+ String cat = obj.optString("categoria", "---");
+ int tipo = obj.optInt("tipo");
+ double valor = obj.optDouble("valor");
+
+ String tipoStr = (tipo == 1) ? "Receita" : "Despesa";
+ csvData.append(data).append(",").append(desc).append(",").append(cat).append(",").append(tipoStr).append(",").append(valor).append("\n");
+ }
+
+ String nomeFicheiro = "Finzora_Export_" + System.currentTimeMillis() + ".csv";
+ File arquivoCsv = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), nomeFicheiro);
+
+ FileOutputStream fos = new FileOutputStream(arquivoCsv);
+ fos.write(csvData.toString().getBytes());
+ fos.close();
+
+ Toast.makeText(this, "Ficheiro Excel guardado com sucesso! 📊", Toast.LENGTH_LONG).show();
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(this, "Erro ao gerar o ficheiro Excel.", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ // 🏆 A JOGADA DE MESTRE: PDF COM DIAGNÓSTICO INTELIGENTE!
+ private void gerarRelatorioPDF(JSONArray transacoes) {
+ PdfDocument pdf = new PdfDocument();
+ int numeroPagina = 1;
+ PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(595, 842, numeroPagina).create();
+ PdfDocument.Page pagina = pdf.startPage(pageInfo);
+ Canvas canvas = pagina.getCanvas();
+ Paint paint = new Paint();
+
+ int totalTransacoes = transacoes.length();
+ double maiorDespesa = 0, maiorReceita = 0, somaDespesas = 0, somaReceitas = 0;
+ double necessidades = 0, desejos = 0;
+ String descMaiorDespesa = "-", descMaiorReceita = "-";
+ int countDespesas = 0;
+
+ try {
+ for (int i = 0; i < totalTransacoes; i++) {
+ JSONObject obj = transacoes.getJSONObject(i);
+ double v = obj.getDouble("valor");
+ int t = obj.getInt("tipo");
+ String d = obj.getString("descricao");
+ String cat = obj.optString("categoria", "").toLowerCase();
+
+ if (t == 1) {
+ somaReceitas += v;
+ if (v > maiorReceita) { maiorReceita = v; descMaiorReceita = d; }
+ } else {
+ somaDespesas += v; countDespesas++;
+ if (v > maiorDespesa) { maiorDespesa = v; descMaiorDespesa = d; }
+
+ if (cat.contains("conta") || cat.contains("alimen") || cat.contains("saúd") || cat.contains("educa") || cat.contains("casa") || cat.contains("transp")) {
+ necessidades += v;
+ } else {
+ desejos += v;
+ }
+ }
+ }
+ } catch (Exception e) { e.printStackTrace(); }
+
+ double mediaDespesa = countDespesas > 0 ? somaDespesas / countDespesas : 0;
+
+ // CABEÇALHO DO RELATÓRIO
+ paint.setColor(Color.parseColor("#00E676"));
+ canvas.drawRect(0, 0, 595, 120, paint);
+
+ paint.setColor(Color.parseColor("#1A202C"));
+ paint.setTextSize(28f);
+ paint.setFakeBoldText(true);
+ canvas.drawText("FINZORA", 40, 65, paint);
+
+ paint.setTextSize(12f);
+ paint.setFakeBoldText(false);
+ canvas.drawText("RELATÓRIO FINANCEIRO E DIAGNÓSTICO", 40, 90, paint);
+
+ SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
+ String nome = prefs.getString("nome_usuario", "Investidor");
+ String email = prefs.getString("email_usuario", "---");
+
+ paint.setTextAlign(Paint.Align.RIGHT);
+ canvas.drawText("Utilizador: " + nome, 555, 60, paint);
+ canvas.drawText("Email: " + email, 555, 80, paint);
+ paint.setTextAlign(Paint.Align.LEFT);
+
+ paint.setColor(Color.parseColor("#F7FAFC"));
+ canvas.drawRoundRect(40, 140, 555, 210, 10, 10, paint);
+
+ paint.setColor(Color.BLACK);
+ paint.setTextSize(12f);
+ paint.setFakeBoldText(true);
+ canvas.drawText("SALDOS ATUAIS", 60, 165, paint);
+
+ paint.setFakeBoldText(false);
+ paint.setTextSize(11f);
+ canvas.drawText("SALDO: " + tvSaldoGeral.getText().toString(), 60, 190, paint);
+ canvas.drawText("RECEITAS: " + tvReceitasGeral.getText().toString(), 250, 190, paint);
+ canvas.drawText("DESPESAS: " + tvDespesasGeral.getText().toString(), 420, 190, paint);
+
+ paint.setColor(Color.parseColor("#F1F5F9"));
+ canvas.drawRoundRect(40, 225, 555, 305, 10, 10, paint);
+
+ paint.setColor(Color.BLACK);
+ paint.setTextSize(12f);
+ paint.setFakeBoldText(true);
+ canvas.drawText("ANÁLISE DE PERFORMANCE", 60, 250, paint);
+
+ paint.setFakeBoldText(false);
+ paint.setTextSize(11f);
+ canvas.drawText("Maior Despesa: " + descMaiorDespesa + " (" + String.format("%.2f €", maiorDespesa) + ")", 60, 275, paint);
+ canvas.drawText("Maior Receita: " + descMaiorReceita + " (" + String.format("%.2f €", maiorReceita) + ")", 60, 295, paint);
+
+ canvas.drawText("Média por Despesa: " + String.format("%.2f €", mediaDespesa), 350, 275, paint);
+ canvas.drawText("Total de Movimentos: " + totalTransacoes, 350, 295, paint);
+
+ paint.setColor(Color.BLACK);
+ paint.setTextSize(12f);
+ paint.setFakeBoldText(true);
+ canvas.drawText("BALANÇO VISUAL", 40, 345, paint);
+
+ double totalDinheiro = somaReceitas + somaDespesas;
+
+ if (totalDinheiro > 0) {
+ RectF areaDoGrafico = new RectF(60, 365, 200, 505);
+ float anguloReceitas = (float) ((somaReceitas / totalDinheiro) * 360f);
+ float anguloDespesas = (float) ((somaDespesas / totalDinheiro) * 360f);
+
+ paint.setColor(Color.parseColor("#00E676"));
+ canvas.drawArc(areaDoGrafico, 0, anguloReceitas, true, paint);
+
+ paint.setColor(Color.parseColor("#FF1744"));
+ canvas.drawArc(areaDoGrafico, anguloReceitas, anguloDespesas, true, paint);
+
+ paint.setTextSize(11f);
+ paint.setFakeBoldText(false);
+ paint.setColor(Color.parseColor("#00E676"));
+ canvas.drawRect(230, 410, 245, 425, paint);
+ paint.setColor(Color.BLACK);
+ canvas.drawText("Receitas (" + String.format("%.0f", (somaReceitas/totalDinheiro)*100) + "%)", 255, 422, paint);
+
+ paint.setColor(Color.parseColor("#FF1744"));
+ canvas.drawRect(230, 440, 245, 455, paint);
+ paint.setColor(Color.BLACK);
+ canvas.drawText("Despesas (" + String.format("%.0f", (somaDespesas/totalDinheiro)*100) + "%)", 255, 452, paint);
+ }
+
+ paint.setFakeBoldText(true);
+ paint.setTextSize(14f);
+ canvas.drawText("HISTÓRICO DETALHADO", 40, 540, paint);
+
+ paint.setColor(Color.parseColor("#EDF2F7"));
+ canvas.drawRect(40, 555, 555, 580, paint);
+ paint.setColor(Color.BLACK);
+ paint.setTextSize(12f);
+ canvas.drawText("DESCRIÇÃO", 50, 572, paint);
+ canvas.drawText("VALOR", 350, 572, paint);
+ canvas.drawText("DATA", 470, 572, paint);
+
+ paint.setFakeBoldText(false);
+ int y = 605;
+
+ try {
+ for (int i = 0; i < transacoes.length(); i++) {
+ if (y > 760) {
+ desenharRodape(canvas, paint, numeroPagina);
+ pdf.finishPage(pagina);
+
+ numeroPagina++;
+ pageInfo = new PdfDocument.PageInfo.Builder(595, 842, numeroPagina).create();
+ pagina = pdf.startPage(pageInfo);
+ canvas = pagina.getCanvas();
+
+ paint.setColor(Color.parseColor("#00E676"));
+ canvas.drawRect(0, 0, 595, 40, paint);
+ paint.setColor(Color.parseColor("#1A202C"));
+ paint.setTextSize(14f);
+ paint.setFakeBoldText(true);
+ canvas.drawText("FINZORA - Continuação do Histórico", 40, 25, paint);
+
+ paint.setColor(Color.parseColor("#EDF2F7"));
+ canvas.drawRect(40, 60, 555, 85, paint);
+ paint.setColor(Color.BLACK);
+ paint.setTextSize(12f);
+ canvas.drawText("DESCRIÇÃO", 50, 77, paint);
+ canvas.drawText("VALOR", 350, 77, paint);
+ canvas.drawText("DATA", 470, 77, paint);
+
+ y = 110;
+ paint.setFakeBoldText(false);
+ }
+
+ JSONObject obj = transacoes.getJSONObject(i);
+ String desc = obj.getString("descricao");
+ double valor = obj.getDouble("valor");
+ String data = obj.optString("data", "---");
+ int tipo = obj.getInt("tipo");
+
+ if (i % 2 == 0) {
+ paint.setColor(Color.parseColor("#F8FAFC"));
+ canvas.drawRect(40, y - 20, 555, y + 10, paint);
+ }
+
+ paint.setColor(Color.BLACK);
+ canvas.drawText(desc, 50, y, paint);
+
+ if (tipo == 1) {
+ paint.setColor(Color.parseColor("#2F855A"));
+ canvas.drawText("+ " + String.format("%.2f €", valor), 350, y, paint);
+ } else {
+ paint.setColor(Color.parseColor("#C53030"));
+ canvas.drawText("- " + String.format("%.2f €", valor), 350, y, paint);
+ }
+
+ paint.setColor(Color.BLACK);
+ canvas.drawText(data, 470, y, paint);
+ y += 35;
+ }
+ } catch (Exception e) { e.printStackTrace(); }
+
+ // 🧠 ----------------- NOVO BLOCO: DIAGNÓSTICO FINANCEIRO COM TEXTO QUEBRADO -----------------
+
+ y += 20;
+
+ // Aumentei o espaço de controlo para a nova caixa que é mais alta (160px)
+ if (y + 160 > 800) {
+ desenharRodape(canvas, paint, numeroPagina);
+ pdf.finishPage(pagina);
+ numeroPagina++;
+ pageInfo = new PdfDocument.PageInfo.Builder(595, 842, numeroPagina).create();
+ pagina = pdf.startPage(pageInfo);
+ canvas = pagina.getCanvas();
+ y = 60;
+ }
+
+ paint.setColor(Color.BLACK);
+ paint.setFakeBoldText(true);
+ paint.setTextSize(14f);
+ canvas.drawText("💡 DIAGNÓSTICO E RECOMENDAÇÕES", 40, y, paint);
+
+ y += 15;
+ // Desenha a caixa azul mais alta
+ paint.setColor(Color.parseColor("#EBF8FF"));
+ canvas.drawRoundRect(40, y, 555, y + 150, 10, 10, paint);
+
+ y += 25;
+ paint.setTextSize(11f);
+ int colunaTitulos = 50;
+ int colunaTexto = 200; // Puxei o texto um bocadinho mais para a esquerda para caber à vontade
+
+ // 1. Regra 50/30/20
+ double percNecessidades = somaReceitas > 0 ? (necessidades / somaReceitas) * 100 : 0;
+ double percDesejos = somaReceitas > 0 ? (desejos / somaReceitas) * 100 : 0;
+
+ paint.setColor(Color.BLACK);
+ paint.setFakeBoldText(true);
+ canvas.drawText("Balanço (Regra 50/30/20):", colunaTitulos, y, paint);
+ paint.setFakeBoldText(false);
+
+ if (somaReceitas > 0) {
+ if (percNecessidades <= 50 && percDesejos <= 30) {
+ canvas.drawText("Excelente! Gastos essenciais e de lazer", colunaTexto, y, paint);
+ canvas.drawText("encontram-se equilibrados.", colunaTexto, y + 15, paint);
+ } else if (percDesejos > 30) {
+ canvas.drawText("Alerta: Gastos em Lazer elevados (" + String.format("%.0f", percDesejos) + "%).", colunaTexto, y, paint);
+ canvas.drawText("Aconselha-se redução para 30%.", colunaTexto, y + 15, paint);
+ } else {
+ canvas.drawText("Aviso: Despesas Fixas elevadas", colunaTexto, y, paint);
+ canvas.drawText("(" + String.format("%.0f", percNecessidades) + "% do rendimento total).", colunaTexto, y + 15, paint);
+ }
+ } else {
+ canvas.drawText("Registe receitas para cálculo fiável.", colunaTexto, y, paint);
+ }
+
+ // 2. Taxa de Poupança
+ y += 35; // Espaço duplo por causa das duas linhas de cima
+ double taxaPoupanca = somaReceitas > 0 ? ((somaReceitas - somaDespesas) / somaReceitas) * 100 : 0;
+ paint.setFakeBoldText(true);
+ canvas.drawText("Taxa de Poupança Global:", colunaTitulos, y, paint);
+ paint.setFakeBoldText(false);
+ canvas.drawText(String.format("%.1f%%", Math.max(taxaPoupanca, 0)) + " do rendimento foi retido.", colunaTexto, y, paint);
+
+ // 3. Projeção Mensal
+ y += 25;
+ Calendar cal = Calendar.getInstance();
+ int diaAtual = cal.get(Calendar.DAY_OF_MONTH);
+ int diasNoMes = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ double mediaDiaria = diaAtual > 0 ? somaDespesas / diaAtual : 0;
+ double previsao = mediaDiaria * diasNoMes;
+
+ paint.setFakeBoldText(true);
+ canvas.drawText("Projeção (Fim do Mês):", colunaTitulos, y, paint);
+ paint.setFakeBoldText(false);
+ canvas.drawText(String.format("Média de %.2f€ diários. Gasto", mediaDiaria), colunaTexto, y, paint);
+ canvas.drawText(String.format("final estimado: %.2f€", previsao), colunaTexto, y + 15, paint);
+
+ // 4. Parecer Final
+ y += 35;
+ paint.setFakeBoldText(true);
+ canvas.drawText("Parecer Final:", colunaTitulos, y, paint);
+ paint.setFakeBoldText(false);
+
+ if (previsao > somaReceitas && somaReceitas > 0) {
+ paint.setColor(Color.parseColor("#C53030")); // Vermelho
+ canvas.drawText("ALTO RISCO: Mantendo esta tendência,", colunaTexto, y, paint);
+ canvas.drawText("irá fechar o mês com saldo negativo.", colunaTexto, y + 15, paint);
+ } else if (somaReceitas == 0) {
+ paint.setColor(Color.DKGRAY);
+ canvas.drawText("A aguardar entrada de receitas para", colunaTexto, y, paint);
+ canvas.drawText("efetuar um diagnóstico final.", colunaTexto, y + 15, paint);
+ } else {
+ paint.setColor(Color.parseColor("#2F855A")); // Verde
+ canvas.drawText("ESTÁVEL: A sua projeção indica que", colunaTexto, y, paint);
+ canvas.drawText("fechará o mês com lucro.", colunaTexto, y + 15, paint);
+ }
+ // -------------------------------------------------------------------------------
+
+ desenharRodape(canvas, paint, numeroPagina);
+ pdf.finishPage(pagina);
+
+ String nomeFicheiro = "Relatorio_Consultoria_Finzora_" + System.currentTimeMillis() + ".pdf";
+ File arquivoPdf = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), nomeFicheiro);
+
+ try {
+ pdf.writeTo(new FileOutputStream(arquivoPdf));
+ Toast.makeText(this, "Relatório de Consultoria gerado com sucesso! 📄📈", Toast.LENGTH_LONG).show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ pdf.close();
+ }
+ }
+
+ private void desenharRodape(Canvas canvas, Paint paint, int paginaActual) {
+ paint.setColor(Color.GRAY);
+ paint.setTextSize(10f);
+ paint.setFakeBoldText(false);
+ canvas.drawLine(40, 800, 555, 800, paint);
+
+ String dataGeracao = java.text.DateFormat.getDateTimeInstance().format(new java.util.Date());
+ canvas.drawText("Emitido por Finzora Consultoria Financeira em: " + dataGeracao, 40, 815, paint);
+ canvas.drawText("Página " + paginaActual, 510, 815, paint);
+ }
+
+ private void carregarFotoPerfil() {
+ ImageView imgLogoPerfil = findViewById(R.id.imgLogo);
+ if (imgLogoPerfil == null) return;
+
+ SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
+ String caminhoFoto = prefs.getString("foto_usuario_path", null);
+
+ if (caminhoFoto != null) {
+ File arquivoFoto = new File(caminhoFoto);
+ if (arquivoFoto.exists()) {
+ imgLogoPerfil.setPadding(0, 0, 0, 0);
+ imgLogoPerfil.setImageTintList(null);
+
+ Glide.with(this)
+ .load(arquivoFoto)
+ .skipMemoryCache(true)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .circleCrop()
+ .into(imgLogoPerfil);
+ }
+ }
}
@Override
protected void onResume() {
super.onResume();
atualizarCartoes();
-
+ carregarFotoPerfil();
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
String nome = prefs.getString("nome_usuario", "Investidor");
- if (tvNomeUsuario != null) {
- tvNomeUsuario.setText("Olá, " + nome);
- }
+ if (tvNomeUsuario != null) tvNomeUsuario.setText("Olá, " + nome);
}
private void configurarAbas() {
ViewPagerAdapter adapter = new ViewPagerAdapter(this);
viewPager.setAdapter(adapter);
-
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
switch (position) {
case 0: tab.setText("Transações"); break;
case 1: tab.setText("Orçamentos"); break;
case 2: tab.setText("Gráficos"); break;
- case 3: tab.setText("Dicas"); break;
+ case 3: tab.setText("Objetivos"); break;
+ case 4: tab.setText("Dicas"); break;
}
}).attach();
}
- // ==========================================================
- // --- CALCULAR O SALDO DIRETAMENTE DO SUPABASE ---
- // ==========================================================
public void atualizarCartoes() {
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
String userId = prefs.getString("user_id", null);
-
if (userId == null) return;
OkHttpClient client = new OkHttpClient();
@@ -138,44 +579,32 @@ public class MainActivity extends AppCompatActivity {
try {
String jsonResposta = response.body().string();
JSONArray jsonArray = new JSONArray(jsonResposta);
+ listaTransacoesGlobal = jsonArray;
float receitas = 0;
float despesas = 0;
- // Percorrer todas as transações da nuvem e somar!
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject obj = jsonArray.getJSONObject(i);
float valor = (float) obj.getDouble("valor");
int tipo = obj.getInt("tipo");
-
- if (tipo == 1) {
- receitas += valor;
- } else if (tipo == 2) {
- despesas += valor;
- }
+ if (tipo == 1) receitas += valor;
+ else if (tipo == 2) despesas += valor;
}
final float totalReceitas = receitas;
final float totalDespesas = despesas;
final float saldo = receitas - despesas;
- // Atualizar o design do ecrã
runOnUiThread(() -> {
if (tvReceitasGeral != null) tvReceitasGeral.setText(String.format("€ %.2f", totalReceitas));
if (tvDespesasGeral != null) tvDespesasGeral.setText(String.format("€ %.2f", totalDespesas));
if (tvSaldoGeral != null) {
tvSaldoGeral.setText(String.format("€ %.2f", saldo));
- if (saldo < 0) {
- tvSaldoGeral.setTextColor(Color.parseColor("#FF1744"));
- } else {
- tvSaldoGeral.setTextColor(getResources().getColor(R.color.texto_principal));
- }
+ tvSaldoGeral.setTextColor(saldo < 0 ? Color.parseColor("#FF1744") : getResources().getColor(R.color.texto_principal));
}
});
-
- } catch (Exception e) {
- e.printStackTrace();
- }
+ } catch (Exception e) { e.printStackTrace(); }
}
}
});
diff --git a/app/src/main/java/com/example/finzora/NovaPasswordActivity.java b/app/src/main/java/com/example/finzora/NovaPasswordActivity.java
new file mode 100644
index 0000000..570ff7c
--- /dev/null
+++ b/app/src/main/java/com/example/finzora/NovaPasswordActivity.java
@@ -0,0 +1,116 @@
+package com.example.finzora;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.widget.Button;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import com.google.android.material.textfield.TextInputEditText;
+
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+public class NovaPasswordActivity extends AppCompatActivity {
+
+ private TextInputEditText editNovaPass, editConfirmaNovaPass;
+ private Button btnGuardar;
+ private String accessTokenParaRecuperar = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_nova_password);
+
+ editNovaPass = findViewById(R.id.editNovaPass);
+ editConfirmaNovaPass = findViewById(R.id.editConfirmaNovaPass);
+ btnGuardar = findViewById(R.id.btnGuardarNovaPass);
+
+ // ⚠️ O RADAR EM AÇÃO: Tentar ler o link mágico com que a app foi aberta
+ Uri uri = getIntent().getData();
+ if (uri != null && uri.getFragment() != null) {
+ // O Supabase manda o token no "fragment" do link (depois do #)
+ String fragmento = uri.getFragment();
+ String[] partes = fragmento.split("&");
+ for (String parte : partes) {
+ if (parte.startsWith("access_token=")) {
+ accessTokenParaRecuperar = parte.substring("access_token=".length());
+ break;
+ }
+ }
+ }
+
+ if (accessTokenParaRecuperar == null) {
+ Toast.makeText(this, "Erro: Link mágico inválido ou expirado.", Toast.LENGTH_LONG).show();
+ finish();
+ }
+
+ btnGuardar.setOnClickListener(v -> validarEAtualizarPassword());
+ }
+
+ private void validarEAtualizarPassword() {
+ String pass1 = editNovaPass.getText().toString().trim();
+ String pass2 = editConfirmaNovaPass.getText().toString().trim();
+
+ if (TextUtils.isEmpty(pass1) || pass1.length() < 6) {
+ editNovaPass.setError("Mínimo de 6 caracteres.");
+ return;
+ }
+ if (!pass1.equals(pass2)) {
+ editConfirmaNovaPass.setError("As senhas não coincidem.");
+ return;
+ }
+
+ btnGuardar.setEnabled(false);
+ btnGuardar.setText("A GUARDAR...");
+
+ // ☁️ Enviar a nova password para a Nuvem
+ OkHttpClient client = new OkHttpClient();
+ String json = "{\"password\":\"" + pass1 + "\"}";
+ RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
+
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/auth/v1/user")
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ // Usamos o token mágico que apanhamos do email para provar quem somos!
+ .addHeader("Authorization", "Bearer " + accessTokenParaRecuperar)
+ .put(body) // Para atualizar os dados do utilizador usa-se PUT
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
+ runOnUiThread(() -> {
+ Toast.makeText(NovaPasswordActivity.this, "Erro de rede!", Toast.LENGTH_SHORT).show();
+ btnGuardar.setEnabled(true);
+ btnGuardar.setText("GUARDAR PALAVRA-PASSE");
+ });
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ runOnUiThread(() -> {
+ if (response.isSuccessful()) {
+ Toast.makeText(NovaPasswordActivity.this, "Password atualizada com sucesso! 🎉", Toast.LENGTH_LONG).show();
+ // Volta para o Ecrã de Login para entrar com a nova pass
+ startActivity(new Intent(NovaPasswordActivity.this, LoginActivity.class));
+ finish();
+ } else {
+ Toast.makeText(NovaPasswordActivity.this, "Erro ao atualizar. Tenta pedir novo link.", Toast.LENGTH_LONG).show();
+ btnGuardar.setEnabled(true);
+ btnGuardar.setText("GUARDAR PALAVRA-PASSE");
+ }
+ });
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/Objetivo.java b/app/src/main/java/com/example/finzora/Objetivo.java
new file mode 100644
index 0000000..670787c
--- /dev/null
+++ b/app/src/main/java/com/example/finzora/Objetivo.java
@@ -0,0 +1,17 @@
+package com.example.finzora;
+
+public class Objetivo {
+ private String id;
+ private String nome;
+ private float valorAlvo;
+
+ public Objetivo(String id, String nome, float valorAlvo) {
+ this.id = id;
+ this.nome = nome;
+ this.valorAlvo = valorAlvo;
+ }
+
+ public String getId() { return id; }
+ public String getNome() { return nome; }
+ public float getValorAlvo() { return valorAlvo; }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/ObjetivosAdapter.java b/app/src/main/java/com/example/finzora/ObjetivosAdapter.java
new file mode 100644
index 0000000..6b56f12
--- /dev/null
+++ b/app/src/main/java/com/example/finzora/ObjetivosAdapter.java
@@ -0,0 +1,119 @@
+package com.example.finzora;
+
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.cardview.widget.CardView;
+import androidx.recyclerview.widget.RecyclerView;
+import java.util.List;
+
+public class ObjetivosAdapter extends RecyclerView.Adapter {
+
+ private List listaObjetivos;
+ private ObjetivosFragment fragment;
+ private float saldoAtual;
+
+ public ObjetivosAdapter(List lista, float saldoAtual, ObjetivosFragment fragment) {
+ this.listaObjetivos = lista;
+ this.saldoAtual = saldoAtual;
+ this.fragment = fragment;
+ }
+
+ @NonNull
+ @Override
+ public ObjetivosViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_objetivo, parent, false);
+ return new ObjetivosViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ObjetivosViewHolder holder, int position) {
+ Objetivo item = listaObjetivos.get(position);
+
+ int percentagem = 0;
+ float valorGuardado = 0;
+
+ if (saldoAtual > 0) {
+ valorGuardado = saldoAtual;
+ if (valorGuardado >= item.getValorAlvo()) {
+ valorGuardado = item.getValorAlvo();
+ percentagem = 100;
+ } else {
+ percentagem = (int) ((valorGuardado / item.getValorAlvo()) * 100);
+ }
+ }
+
+ holder.tvNome.setText(item.getNome());
+ holder.progress.setProgress(percentagem);
+ holder.tvPercentagem.setText(percentagem + "%");
+
+ // 🏆 A MAGIA DA CELEBRAÇÃO QUANDO CHEGAS AOS 100%
+ if (percentagem == 100) {
+ holder.tvPercentagem.setTextColor(Color.parseColor("#00E676"));
+ holder.progress.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#00E676")));
+ holder.imgIcone.setColorFilter(Color.parseColor("#00E676"));
+
+ holder.cardObjetivo.setCardBackgroundColor(Color.parseColor("#1A00E676"));
+
+ holder.tvValores.setText("🎉 Parabéns! Já podes comprar a tua conquista.");
+ holder.tvValores.setTextColor(Color.parseColor("#00E676"));
+
+ holder.btnEditar.setVisibility(View.GONE);
+
+ } else {
+ holder.tvPercentagem.setTextColor(Color.parseColor("#00B8D4"));
+ holder.progress.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#00B8D4")));
+ holder.imgIcone.setColorFilter(Color.parseColor("#00B8D4"));
+
+ holder.cardObjetivo.setCardBackgroundColor(Color.TRANSPARENT);
+ holder.tvValores.setText(String.format("Guardado: € %.2f / Alvo: € %.2f", valorGuardado, item.getValorAlvo()));
+ holder.tvValores.setTextColor(Color.GRAY);
+
+ holder.btnEditar.setVisibility(View.VISIBLE);
+ }
+
+ // ✏️ Ação do NOVO Botão Editar AGORA A FUNCIONAR
+ holder.btnEditar.setOnClickListener(v -> {
+ if (fragment != null) {
+ fragment.editarObjetivo(item); // CHAMA A JANELA DE EDIÇÃO!
+ }
+ });
+
+ // 🗑️ Botão Eliminar
+ holder.btnEliminar.setOnClickListener(v -> {
+ if (fragment != null) {
+ fragment.confirmarExclusaoObjetivo(item);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return listaObjetivos.size();
+ }
+
+ static class ObjetivosViewHolder extends RecyclerView.ViewHolder {
+ CardView cardObjetivo;
+ TextView tvNome, tvValores, tvPercentagem;
+ ProgressBar progress;
+ ImageView btnEliminar, btnEditar, imgIcone;
+
+ public ObjetivosViewHolder(@NonNull View itemView) {
+ super(itemView);
+ cardObjetivo = itemView.findViewById(R.id.cardObjetivo);
+ tvNome = itemView.findViewById(R.id.tvNomeObjetivo);
+ tvValores = itemView.findViewById(R.id.tvValoresObjetivo);
+ tvPercentagem = itemView.findViewById(R.id.tvPercentagemObjetivo);
+ progress = itemView.findViewById(R.id.progressObjetivo);
+ btnEliminar = itemView.findViewById(R.id.btnEliminarObjetivo);
+ btnEditar = itemView.findViewById(R.id.btnEditarObjetivo);
+ imgIcone = itemView.findViewById(R.id.imgIconeObjetivo);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/ObjetivosFragment.java b/app/src/main/java/com/example/finzora/ObjetivosFragment.java
new file mode 100644
index 0000000..5af6ac3
--- /dev/null
+++ b/app/src/main/java/com/example/finzora/ObjetivosFragment.java
@@ -0,0 +1,326 @@
+package com.example.finzora;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+public class ObjetivosFragment extends Fragment {
+
+ private RecyclerView recyclerObjetivos;
+ private ObjetivosAdapter adapter;
+ private View layoutVazio;
+ private float saldoAtualCalculado = 0f;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_objetivos, container, false);
+
+ recyclerObjetivos = view.findViewById(R.id.recyclerObjetivos);
+ recyclerObjetivos.setLayoutManager(new LinearLayoutManager(getActivity()));
+ layoutVazio = view.findViewById(R.id.layoutObjetivosVazios);
+
+ FloatingActionButton fabAdicionar = view.findViewById(R.id.fabAdicionarObjetivo);
+ fabAdicionar.setOnClickListener(v -> mostrarDialogoAdicionar());
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ calcularSaldoECarregarObjetivos();
+ }
+
+ // 🧠 PRIMEIRO: Calculamos o Saldo Total (Receitas - Despesas)
+ private void calcularSaldoECarregarObjetivos() {
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+ if (userId == null) return;
+
+ OkHttpClient client = new OkHttpClient();
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (response.isSuccessful()) {
+ try {
+ JSONArray jsonArray = new JSONArray(response.body().string());
+ float receitas = 0, despesas = 0;
+
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject obj = jsonArray.getJSONObject(i);
+ if (obj.getInt("tipo") == 1) receitas += (float) obj.getDouble("valor");
+ else despesas += (float) obj.getDouble("valor");
+ }
+
+ saldoAtualCalculado = receitas - despesas;
+ if(saldoAtualCalculado < 0) saldoAtualCalculado = 0;
+
+ carregarObjetivosDoSupabase();
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ }
+ });
+ }
+
+ // ☁️ SEGUNDO: Buscar os dados à tabela nova
+ private void carregarObjetivosDoSupabase() {
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+
+ OkHttpClient client = new OkHttpClient();
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/objetivos?user_id=eq." + userId + "&order=created_at.desc")
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (response.isSuccessful()) {
+ try {
+ List lista = new ArrayList<>();
+ JSONArray array = new JSONArray(response.body().string());
+
+ for (int i = 0; i < array.length(); i++) {
+ JSONObject obj = array.getJSONObject(i);
+ lista.add(new Objetivo(
+ obj.getString("id"),
+ obj.getString("nome_objetivo"),
+ (float) obj.getDouble("valor_alvo")
+ ));
+ }
+
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ adapter = new ObjetivosAdapter(lista, saldoAtualCalculado, ObjetivosFragment.this);
+ recyclerObjetivos.setAdapter(adapter);
+
+ if (lista.isEmpty()) {
+ recyclerObjetivos.setVisibility(View.GONE);
+ layoutVazio.setVisibility(View.VISIBLE);
+ } else {
+ recyclerObjetivos.setVisibility(View.VISIBLE);
+ layoutVazio.setVisibility(View.GONE);
+ }
+ });
+ }
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ }
+ });
+ }
+
+ // ➕ TERCEIRO: O Pop-up (AGORA COM DESIGN PREMIUM!)
+ private void mostrarDialogoAdicionar() {
+ View view = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_novo_objetivo, null);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setView(view);
+ AlertDialog dialog = builder.create();
+
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(0));
+ }
+
+ EditText editNome = view.findViewById(R.id.editNomeObjetivo);
+ EditText editValor = view.findViewById(R.id.editValorObjetivo);
+ TextView btnCancelar = view.findViewById(R.id.btnCancelarObjetivo);
+ Button btnGuardar = view.findViewById(R.id.btnGuardarObjetivo);
+
+ btnCancelar.setOnClickListener(v -> dialog.dismiss());
+
+ btnGuardar.setOnClickListener(v -> {
+ String nome = editNome.getText().toString().trim();
+ String valorStr = editValor.getText().toString().trim();
+
+ if (!nome.isEmpty() && !valorStr.isEmpty()) {
+ guardarNoSupabase(nome, Float.parseFloat(valorStr));
+ dialog.dismiss();
+ } else {
+ Toast.makeText(getActivity(), "Preenche todos os campos!", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ dialog.show();
+ }
+
+ private void guardarNoSupabase(String nome, float valorAlvo) {
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+
+ try {
+ JSONObject json = new JSONObject();
+ json.put("user_id", userId);
+ json.put("nome_objetivo", nome);
+ json.put("valor_alvo", valorAlvo);
+
+ RequestBody body = RequestBody.create(json.toString(), MediaType.parse("application/json; charset=utf-8"));
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/objetivos")
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Prefer", "return=minimal")
+ .post(body)
+ .build();
+
+ new OkHttpClient().newCall(request).enqueue(new Callback() {
+ @Override public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+ @Override public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.isSuccessful() && getActivity() != null) {
+ getActivity().runOnUiThread(() -> carregarObjetivosDoSupabase());
+ }
+ }
+ });
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+
+ public void confirmarExclusaoObjetivo(Objetivo obj) {
+ new AlertDialog.Builder(getActivity())
+ .setTitle("Eliminar Objetivo")
+ .setMessage("Queres apagar o objetivo '" + obj.getNome() + "'?")
+ .setPositiveButton("Sim", (dialog, which) -> {
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/objetivos?id=eq." + obj.getId())
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .delete()
+ .build();
+
+ new OkHttpClient().newCall(request).enqueue(new Callback() {
+ @Override public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+ @Override public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> carregarObjetivosDoSupabase());
+ }
+ }
+ });
+ })
+ .setNegativeButton("Não", null)
+ .show();
+ }
+
+ // 🏆 A NOVA JOGADA: FUNÇÃO PARA EDITAR! (Corrigida sem o tvTituloDialogObjetivo)
+ public void editarObjetivo(Objetivo objetivo) {
+ if (getActivity() == null) return;
+
+ // Reutilizamos o design bonito do dialog de adicionar
+ View view = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_novo_objetivo, null);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setView(view);
+ AlertDialog dialog = builder.create();
+
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(0));
+ }
+
+ EditText editNome = view.findViewById(R.id.editNomeObjetivo);
+ EditText editValor = view.findViewById(R.id.editValorObjetivo);
+ TextView btnCancelar = view.findViewById(R.id.btnCancelarObjetivo);
+ Button btnGuardar = view.findViewById(R.id.btnGuardarObjetivo);
+
+ // Preencher com os dados atuais
+ editNome.setText(objetivo.getNome());
+ editValor.setText(String.valueOf(objetivo.getValorAlvo()));
+ btnGuardar.setText("Atualizar");
+
+ btnCancelar.setOnClickListener(v -> dialog.dismiss());
+
+ btnGuardar.setOnClickListener(v -> {
+ String novoNome = editNome.getText().toString().trim();
+ String valorStr = editValor.getText().toString().trim();
+
+ if (!novoNome.isEmpty() && !valorStr.isEmpty()) {
+ double novoValor = Double.parseDouble(valorStr);
+ atualizarObjetivoNoSupabase(objetivo.getId(), novoNome, novoValor);
+ dialog.dismiss();
+ } else {
+ Toast.makeText(getActivity(), "Preenche todos os campos!", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ dialog.show();
+ }
+
+ // 🚀 ENVIA O UPDATE PARA O SUPABASE
+ private void atualizarObjetivoNoSupabase(String idObjetivo, String novoNome, double novoValor) {
+ OkHttpClient client = new OkHttpClient();
+
+ String jsonUpdate = "{\"nome_objetivo\":\"" + novoNome + "\", \"valor_alvo\":" + novoValor + "}";
+ RequestBody body = RequestBody.create(jsonUpdate, MediaType.parse("application/json; charset=utf-8"));
+
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/objetivos?id=eq." + idObjetivo)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .patch(body) // Usamos PATCH para atualizar
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() ->
+ Toast.makeText(getActivity(), "Erro de net ao atualizar.", Toast.LENGTH_SHORT).show());
+ }
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ if (response.isSuccessful()) {
+ Toast.makeText(getActivity(), "Objetivo atualizado! 🚀", Toast.LENGTH_SHORT).show();
+ // Recarregar os objetivos na lista!
+ carregarObjetivosDoSupabase();
+ } else {
+ Toast.makeText(getActivity(), "Erro a guardar.", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/OnboardingActivity.java b/app/src/main/java/com/example/finzora/OnboardingActivity.java
index d3942b2..4fa5d3e 100644
--- a/app/src/main/java/com/example/finzora/OnboardingActivity.java
+++ b/app/src/main/java/com/example/finzora/OnboardingActivity.java
@@ -16,7 +16,7 @@ public class OnboardingActivity extends AppCompatActivity {
private OnboardingAdapter onboardingAdapter;
private Button btnProximo;
- private TextView btnSaltar; // Atenção: no teu XML o Saltar era um TextView
+ private TextView btnSaltar;
private TabLayout tabLayoutIndicator;
@Override
@@ -32,21 +32,28 @@ public class OnboardingActivity extends AppCompatActivity {
// --- 2. CRIAR OS SLIDES ---
List lista = new ArrayList<>();
- // SLIDE 1: Usamos a variável 'nomeRecuperado' aqui
+ // SLIDE 1: Boas-vindas
lista.add(new OnboardingItem(
"Olá, " + nomeRecuperado + "! \uD83D\uDC4B",
"Bem-vindo ao Finzora. A tua gestão financeira pessoal, agora com tecnologia de ponta.",
- R.drawable.ic_wallet // Confirma se tens este ícone, ou usa ic_launcher_foreground
+ R.drawable.ic_wallet
));
- // SLIDE 2
+ // SLIDE 2: Transações
lista.add(new OnboardingItem(
"Controlo Total",
"Regista receitas e despesas num piscar de olhos e mantém o teu saldo sempre atualizado.",
R.drawable.ic_chart
));
- // SLIDE 3
+ // ⚠️ SLIDE 3: A NOSSA NOVA CONTRATAÇÃO (OBJETIVOS)
+ lista.add(new OnboardingItem(
+ "Atinge os teus Objetivos \uD83C\uDFAF",
+ "Tem um alvo a atingir? Cria cofres de poupança e vê a magia acontecer com o cálculo automático de progresso!",
+ R.drawable.ic_lazer // Se tiveres um ícone mais adequado como ic_cofre podes mudar aqui
+ ));
+
+ // SLIDE 4: Inteligência Artificial
lista.add(new OnboardingItem(
"Inteligência Artificial",
"Recebe dicas automáticas baseadas nos teus gastos para poupares mais todos os meses.",
@@ -94,7 +101,6 @@ public class OnboardingActivity extends AppCompatActivity {
}
private void finalizarOnboarding() {
- // Tem de ir para ProfileActivity.class, e NÃO para MainActivity.class
Intent intent = new Intent(OnboardingActivity.this, ProfileActivity.class);
startActivity(intent);
finish();
diff --git a/app/src/main/java/com/example/finzora/OrcamentoAdapter.java b/app/src/main/java/com/example/finzora/OrcamentoAdapter.java
index 6577704..97b76f5 100644
--- a/app/src/main/java/com/example/finzora/OrcamentoAdapter.java
+++ b/app/src/main/java/com/example/finzora/OrcamentoAdapter.java
@@ -5,22 +5,33 @@ import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
-import java.util.Map;
public class OrcamentoAdapter extends RecyclerView.Adapter {
- // Lista de pares: Chave=Categoria, Valor=Limite
- private List> listaOrcamentos;
- private DBHelper dbHelper;
+ public static class OrcamentoItem {
+ String categoria;
+ float limite;
+ float gasto;
- public OrcamentoAdapter(List> lista, DBHelper db) {
+ public OrcamentoItem(String categoria, float limite, float gasto) {
+ this.categoria = categoria;
+ this.limite = limite;
+ this.gasto = gasto;
+ }
+ }
+
+ private List listaOrcamentos;
+ private OrcamentoFragment fragment;
+
+ public OrcamentoAdapter(List lista, OrcamentoFragment fragment) {
this.listaOrcamentos = lista;
- this.dbHelper = db;
+ this.fragment = fragment;
}
@NonNull
@@ -32,30 +43,42 @@ public class OrcamentoAdapter extends RecyclerView.Adapter entry = listaOrcamentos.get(position);
- String categoria = entry.getKey();
- float limite = entry.getValue();
+ OrcamentoItem item = listaOrcamentos.get(position);
- // Calcular quanto já gastou
- float gasto = dbHelper.getGastoPorCategoria(categoria);
- float restante = limite - gasto;
- int percentagem = (int) ((gasto / limite) * 100);
+ int percentagem = (int) ((item.gasto / item.limite) * 100);
if (percentagem > 100) percentagem = 100;
- // Atualizar Textos
- holder.tvCategoria.setText(categoria);
- holder.tvValores.setText(String.format("€ %.2f / € %.2f", gasto, limite));
+ holder.tvCategoria.setText(item.categoria);
+ holder.tvValores.setText(String.format("Gasto: € %.2f / Limite: € %.2f", item.gasto, item.limite));
holder.progress.setProgress(percentagem);
- if (restante >= 0) {
- holder.tvRestante.setText(String.format("Restam € %.2f (%d%%)", restante, 100 - percentagem));
- holder.tvRestante.setTextColor(Color.parseColor("#90A4AE")); // Cinzento
- holder.progress.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#00E676"))); // Verde
+ // ⚠️ A TÁTICA NOVA: Mudar a imagem conforme o texto!
+ holder.imgIcone.setImageResource(obterIconeCategoria(item.categoria));
+
+ // Dica do Mister: Se quiseres que os ícones fiquem todos verdes para combinar com o design,
+ // podes descomentar a linha abaixo tirando as duas barras (//):
+ // holder.imgIcone.setColorFilter(Color.parseColor("#00E676"));
+
+ // A TÁTICA DO SEMÁFORO
+ if (percentagem < 80) {
+ holder.tvPercentagem.setText(percentagem + "%");
+ holder.tvPercentagem.setTextColor(Color.parseColor("#00E676"));
+ holder.progress.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#00E676")));
+ } else if (percentagem >= 80 && percentagem < 100) {
+ holder.tvPercentagem.setText(percentagem + "%");
+ holder.tvPercentagem.setTextColor(Color.parseColor("#ECC94B"));
+ holder.progress.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#ECC94B")));
} else {
- holder.tvRestante.setText(String.format("Ultrapassado por € %.2f!", Math.abs(restante)));
- holder.tvRestante.setTextColor(Color.parseColor("#FF1744")); // Vermelho
- holder.progress.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#FF1744"))); // Vermelho
+ holder.tvPercentagem.setText("100%+");
+ holder.tvPercentagem.setTextColor(Color.parseColor("#FF1744"));
+ holder.progress.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#FF1744")));
}
+
+ holder.btnEliminar.setOnClickListener(v -> {
+ if (fragment != null) {
+ fragment.confirmarExclusaoOrcamento(item);
+ }
+ });
}
@Override
@@ -63,16 +86,39 @@ public class OrcamentoAdapter extends RecyclerView.Adapter mostrarDialogCategorias());
- // Configurações iniciais
- configurarSpinner();
+ btnSalvar.setOnClickListener(v -> salvarOrcamentoNaNuvem());
- // Ação do botão
- btnSalvar.setOnClickListener(v -> salvarOrcamento());
-
- // Mostrar dados
- carregarOrcamentos();
+ carregarOrcamentosDaNuvem();
return view;
}
@@ -60,39 +71,242 @@ public class OrcamentoFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
- carregarOrcamentos();
+ carregarOrcamentosDaNuvem();
}
- // Método corrigido (havia um duplicado antes)
- private void configurarSpinner() {
- String[] categorias = {"Alimentação", "Transporte", "Salário", "Lazer", "Contas", "Saúde", "Outros"};
- ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), R.layout.item_dropdown, categorias);
- spinnerCategoria.setAdapter(adapter);
+ private void mostrarDialogCategorias() {
+ // 1. Criar um Dialog "em branco"
+ android.app.Dialog dialog = new android.app.Dialog(getActivity());
+
+ // 2. MAGIA: Dizer-lhe para usar o TEU design premium!
+ dialog.setContentView(R.layout.dialog_categorias);
+
+ // 3. Fazer o fundo padrão ficar transparente para o teu CardView arredondado brilhar
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
+ dialog.getWindow().setLayout(android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ // 4. Ligar o botão de Cancelar do teu XML
+ TextView btnCancelar = dialog.findViewById(R.id.btnCancelarCategoria);
+ btnCancelar.setOnClickListener(v -> dialog.dismiss());
+
+ // 5. Injetar as categorias no teu LinearLayout vazio (dentro do ScrollView)
+ android.widget.LinearLayout container = dialog.findViewById(R.id.containerCategorias);
+
+ // 🛑 A CORREÇÃO DA TINTA: Usar a nossa própria cor texto_principal!
+ int corTexto = androidx.core.content.ContextCompat.getColor(getActivity(), R.color.texto_principal);
+
+ android.util.TypedValue clickEffect = new android.util.TypedValue();
+ getActivity().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, clickEffect, true);
+
+ for (String categoria : categorias) {
+ TextView tvCat = new TextView(getActivity());
+ tvCat.setText(categoria);
+ tvCat.setTextSize(18f);
+ tvCat.setPadding(32, 32, 32, 32); // Espaçamento elegante
+ tvCat.setTextColor(corTexto);
+
+ // Efeito visual seguro sem fechar a app!
+ tvCat.setBackgroundResource(clickEffect.resourceId);
+ tvCat.setClickable(true);
+
+ // O que acontece quando clicas numa categoria
+ tvCat.setOnClickListener(v -> {
+ categoriaSelecionada = categoria;
+ txtCategoria.setText(categoriaSelecionada);
+ dialog.dismiss();
+ });
+
+ container.addView(tvCat);
+ }
+
+ dialog.show();
}
- private void salvarOrcamento() {
- String limiteStr = editLimite.getText().toString();
+ private void salvarOrcamentoNaNuvem() {
+ String limiteStr = editLimite.getText().toString().replace(",", ".");
+
+ if (categoriaSelecionada.isEmpty()) {
+ Toast.makeText(getActivity(), "Por favor, escolhe uma categoria!", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
if (limiteStr.isEmpty()) {
editLimite.setError("Define um valor");
return;
}
- String categoria = spinnerCategoria.getSelectedItem().toString();
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+ if (userId == null) return;
+
float limite = Float.parseFloat(limiteStr);
- dbHelper.salvarOrcamento(categoria, limite);
+ btnSalvar.setEnabled(false);
+ btnSalvar.setText("A GRAVAR...");
- Toast.makeText(getActivity(), "Orçamento definido!", Toast.LENGTH_SHORT).show();
- editLimite.setText(""); // Limpar campo
+ OkHttpClient client = new OkHttpClient();
+ String json = "{\"user_id\":\"" + userId + "\", \"categoria\":\"" + categoriaSelecionada + "\", \"valor_limite\":" + limite + "}";
+ RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
- carregarOrcamentos(); // Atualizar lista
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/orcamentos?on_conflict=user_id,categoria")
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Prefer", "resolution=merge-duplicates")
+ .post(body)
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {
+ if(getActivity() != null) getActivity().runOnUiThread(() -> {
+ btnSalvar.setEnabled(true); btnSalvar.setText("Definir Orçamento");
+ Toast.makeText(getActivity(), "Erro de internet!", Toast.LENGTH_SHORT).show();
+ });
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if(getActivity() != null) getActivity().runOnUiThread(() -> {
+ btnSalvar.setEnabled(true); btnSalvar.setText("Definir Orçamento");
+ if (response.isSuccessful()) {
+ Toast.makeText(getActivity(), "Orçamento guardado nas nuvens! ☁️", Toast.LENGTH_SHORT).show();
+ editLimite.setText("");
+ categoriaSelecionada = "";
+ txtCategoria.setText("Selecionar Categoria...");
+ carregarOrcamentosDaNuvem();
+ }
+ });
+ }
+ });
}
- private void carregarOrcamentos() {
- Map orcamentosMap = dbHelper.getOrcamentosDefinidos();
- List> lista = new ArrayList<>(orcamentosMap.entrySet());
+ private void carregarOrcamentosDaNuvem() {
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", null);
+ if (userId == null) return;
- OrcamentoAdapter adapter = new OrcamentoAdapter(lista, dbHelper);
- recyclerOrcamentos.setAdapter(adapter);
+ OkHttpClient client = new OkHttpClient();
+
+ Request reqOrcamentos = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/orcamentos?user_id=eq." + userId)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
+
+ client.newCall(reqOrcamentos).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (!response.isSuccessful()) return;
+
+ try {
+ String jsonOrcamentos = response.body().string();
+ JSONArray arrOrcamentos = new JSONArray(jsonOrcamentos);
+ Map mapaLimites = new HashMap<>();
+
+ for (int i = 0; i < arrOrcamentos.length(); i++) {
+ JSONObject obj = arrOrcamentos.getJSONObject(i);
+ mapaLimites.put(obj.getString("categoria"), (float) obj.getDouble("valor_limite"));
+ }
+
+ Request reqDespesas = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?user_id=eq." + userId + "&tipo=eq.2")
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .build();
+
+ client.newCall(reqDespesas).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response2) throws IOException {
+ if (!response2.isSuccessful()) return;
+
+ try {
+ String jsonDespesas = response2.body().string();
+ JSONArray arrDespesas = new JSONArray(jsonDespesas);
+ Map mapaGastos = new HashMap<>();
+
+ for (int i = 0; i < arrDespesas.length(); i++) {
+ JSONObject obj = arrDespesas.getJSONObject(i);
+ String cat = obj.getString("categoria");
+ float valor = (float) obj.getDouble("valor");
+ float atual = mapaGastos.containsKey(cat) ? mapaGastos.get(cat) : 0f;
+ mapaGastos.put(cat, atual + valor);
+ }
+
+ List listaFinal = new ArrayList<>();
+ for (Map.Entry entry : mapaLimites.entrySet()) {
+ String categoria = entry.getKey();
+ float limite = entry.getValue();
+ float gasto = mapaGastos.containsKey(categoria) ? mapaGastos.get(categoria) : 0f;
+ listaFinal.add(new OrcamentoAdapter.OrcamentoItem(categoria, limite, gasto));
+ }
+
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ OrcamentoAdapter adapter = new OrcamentoAdapter(listaFinal, OrcamentoFragment.this);
+ recyclerOrcamentos.setAdapter(adapter);
+
+ if (listaFinal.isEmpty()) {
+ recyclerOrcamentos.setVisibility(View.GONE);
+ layoutEstadoVazio.setVisibility(View.VISIBLE);
+ } else {
+ recyclerOrcamentos.setVisibility(View.VISIBLE);
+ layoutEstadoVazio.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ });
+
+ } catch (Exception e) { e.printStackTrace(); }
+ }
+ });
+ }
+
+ public void confirmarExclusaoOrcamento(OrcamentoAdapter.OrcamentoItem item) {
+ new AlertDialog.Builder(getActivity())
+ .setTitle("Eliminar Orçamento")
+ .setMessage("Queres apagar o limite de orçamento para " + item.categoria + "?")
+ .setPositiveButton("Sim", (dialog, which) -> {
+
+ SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
+ String userId = prefs.getString("user_id", "");
+
+ OkHttpClient client = new OkHttpClient();
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/orcamentos?user_id=eq." + userId + "&categoria=eq." + item.categoria)
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .delete()
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ Toast.makeText(getActivity(), "Orçamento apagado!", Toast.LENGTH_SHORT).show();
+ carregarOrcamentosDaNuvem();
+ });
+ }
+ }
+ });
+ })
+ .setNegativeButton("Não", null)
+ .show();
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/RegisterActivity.java b/app/src/main/java/com/example/finzora/RegisterActivity.java
index 961ba20..1475034 100644
--- a/app/src/main/java/com/example/finzora/RegisterActivity.java
+++ b/app/src/main/java/com/example/finzora/RegisterActivity.java
@@ -35,10 +35,7 @@ public class RegisterActivity extends AppCompatActivity {
inicializarComponentes();
- // Voltar para o Login
txtLogin.setOnClickListener(v -> finish());
-
- // Clicar em Criar Conta
btnCriarConta.setOnClickListener(v -> validarDados());
}
@@ -78,7 +75,6 @@ public class RegisterActivity extends AppCompatActivity {
private void registarNoSupabase(String nome, String email, String password) {
OkHttpClient client = new OkHttpClient();
- // JSON com os dados para o Supabase Auth
String json = "{\"email\":\"" + email + "\", \"password\":\"" + password + "\", \"data\": {\"nome\": \"" + nome + "\"}}";
RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
@@ -93,7 +89,7 @@ public class RegisterActivity extends AppCompatActivity {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
runOnUiThread(() -> {
- Toast.makeText(RegisterActivity.this, "Erro de rede! Verifica a internet.", Toast.LENGTH_SHORT).show();
+ Toast.makeText(RegisterActivity.this, "Erro de rede!", Toast.LENGTH_SHORT).show();
resetBotao();
});
}
@@ -105,34 +101,49 @@ public class RegisterActivity extends AppCompatActivity {
runOnUiThread(() -> {
if (response.isSuccessful()) {
try {
- // Extrair o ID do utilizador da resposta JSON
JSONObject jsonResponse = new JSONObject(responseData);
- String userId = jsonResponse.getString("id");
- // Guardar Nome e ID localmente para usar nas transações
+ // ⚠️ JOGADA DE MESTRE: Tentar ler o ID de forma mais flexível
+ String userId = "";
+ if (jsonResponse.has("user")) {
+ userId = jsonResponse.getJSONObject("user").getString("id");
+ } else if (jsonResponse.has("id")) {
+ userId = jsonResponse.getString("id");
+ }
+
+ // Guardar dados e marcar como logado
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("nome_usuario", nome);
editor.putString("user_id", userId);
+ editor.putString("email_usuario", email);
+ editor.putBoolean("is_logged_in", true);
editor.apply();
- Toast.makeText(RegisterActivity.this, "Sucesso! Bem-vindo à nuvem.", Toast.LENGTH_SHORT).show();
+ Toast.makeText(RegisterActivity.this, "Bem-vindo à Finzora! Verifica o teu email! 📩", Toast.LENGTH_LONG).show();
- // Avançar para o Onboarding
+ // Avançar sem olhar para trás
startActivity(new Intent(RegisterActivity.this, OnboardingActivity.class));
finish();
} catch (Exception e) {
- Toast.makeText(RegisterActivity.this, "Erro ao ler dados da nuvem", Toast.LENGTH_SHORT).show();
+ // Se chegámos aqui, a conta foi criada (isSuccessful), mas o JSON era estranho
+ // Avançamos na mesma para não prender o utilizador
+ startActivity(new Intent(RegisterActivity.this, OnboardingActivity.class));
+ finish();
}
} else {
- // LER O ERRO REAL DO SUPABASE
try {
JSONObject erroObj = new JSONObject(responseData);
- String mensagemErroReal = erroObj.getString("msg");
- Toast.makeText(RegisterActivity.this, "ERRO: " + mensagemErroReal, Toast.LENGTH_LONG).show();
+ String msg = erroObj.optString("msg", "Erro no registo");
+
+ if (msg.contains("already registered")) {
+ Toast.makeText(RegisterActivity.this, "Este email já tem conta!", Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(RegisterActivity.this, "Erro: " + msg, Toast.LENGTH_LONG).show();
+ }
} catch (Exception e) {
- Toast.makeText(RegisterActivity.this, "Erro desconhecido: " + responseData, Toast.LENGTH_LONG).show();
+ Toast.makeText(RegisterActivity.this, "Erro na nuvem", Toast.LENGTH_LONG).show();
}
resetBotao();
}
diff --git a/app/src/main/java/com/example/finzora/Transacao.java b/app/src/main/java/com/example/finzora/Transacao.java
index 884a331..8c16c7e 100644
--- a/app/src/main/java/com/example/finzora/Transacao.java
+++ b/app/src/main/java/com/example/finzora/Transacao.java
@@ -1,14 +1,16 @@
package com.example.finzora;
public class Transacao {
- private int id;
+ private String id;
private float valor;
private String categoria;
private int tipo; // 1 = Receita, 2 = Despesa
private String data;
+ // ⚠️ NOVA JOGADA: Adicionada a descrição!
+ private String descricao;
// --- CONSTRUTOR 1: Para ler da Base de Dados (TEM ID) ---
- public Transacao(int id, float valor, String categoria, int tipo, String data) {
+ public Transacao(String id, float valor, String categoria, int tipo, String data) {
this.id = id;
this.valor = valor;
this.categoria = categoria;
@@ -24,15 +26,19 @@ public class Transacao {
this.data = data;
}
- // --- GETTERS (Para os outros lerem) ---
- public int getId() { return id; }
+ // --- GETTERS ---
+ public String getId() { return id; }
public float getValor() { return valor; }
public String getCategoria() { return categoria; }
public int getTipo() { return tipo; }
public String getData() { return data; }
+ public String getDescricao() { return descricao; } // Getter da descrição
- // --- SETTERS (Opcional, mas evita erros se algum código antigo os chamar) ---
- public void setId(int id) { this.id = id; }
+ // --- SETTERS ---
+ public void setId(String id) { this.id = id; }
public void setValor(float valor) { this.valor = valor; }
public void setCategoria(String categoria) { this.categoria = categoria; }
+ public void setTipo(int tipo) { this.tipo = tipo; }
+ public void setData(String data) { this.data = data; }
+ public void setDescricao(String descricao) { this.descricao = descricao; } // Setter da descrição
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/TransacoesAdapter.java b/app/src/main/java/com/example/finzora/TransacoesAdapter.java
index fedf1a0..145e778 100644
--- a/app/src/main/java/com/example/finzora/TransacoesAdapter.java
+++ b/app/src/main/java/com/example/finzora/TransacoesAdapter.java
@@ -1,6 +1,5 @@
package com.example.finzora;
-import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
@@ -9,71 +8,165 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
import java.util.List;
-public class TransacoesAdapter extends RecyclerView.Adapter {
+public class TransacoesAdapter extends RecyclerView.Adapter {
- private List listaTransacoes;
- private Context context;
- private TransacoesFragment fragment;
+ private List listaOriginal;
+ private List listaVisivel;
+ private OnTransacaoClickListener listener;
- // O teu construtor mantém-se igual!
- public TransacoesAdapter(List lista, Context context, TransacoesFragment fragment) {
- this.listaTransacoes = lista;
- this.context = context;
- this.fragment = fragment;
+ public interface OnTransacaoClickListener {
+ void onTransacaoClick(Transacao t);
+ }
+
+ public TransacoesAdapter(List lista, OnTransacaoClickListener listener) {
+ this.listaOriginal = new ArrayList<>(lista);
+ this.listaVisivel = lista;
+ this.listener = listener;
}
@NonNull
@Override
- public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View itemLista = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_transacao, parent, false);
- return new MyViewHolder(itemLista);
+ public TransacaoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_transacao, parent, false);
+ return new TransacaoViewHolder(view);
}
@Override
- public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
- Transacao transacao = listaTransacoes.get(position);
+ public void onBindViewHolder(@NonNull TransacaoViewHolder holder, int position) {
+ Transacao t = listaVisivel.get(position);
- // --- ATUALIZADO PARA OS NOVOS IDs DO XML ---
- holder.tvDescricao.setText(transacao.getCategoria());
- holder.tvData.setText(transacao.getData());
-
- // Cores e Ícones
- if (transacao.getTipo() == 1) { // Receita
- holder.tvValor.setTextColor(Color.parseColor("#388E3C")); // Verde
- holder.tvValor.setText("+ € " + String.format("%.2f", transacao.getValor()));
- if(holder.imgIcone != null) holder.imgIcone.setImageResource(android.R.drawable.arrow_up_float);
- } else { // Despesa
- holder.tvValor.setTextColor(Color.parseColor("#D32F2F")); // Vermelho
- holder.tvValor.setText("- € " + String.format("%.2f", transacao.getValor()));
- if(holder.imgIcone != null) holder.imgIcone.setImageResource(android.R.drawable.arrow_down_float);
+ String desc = t.getDescricao();
+ if (desc != null && !desc.trim().isEmpty() && !desc.equalsIgnoreCase("Sem descrição")) {
+ holder.tvDescricao.setText(desc);
+ } else {
+ holder.tvDescricao.setText(t.getCategoria());
}
- // O botão de apagar agora chama-se btnEliminar no XML
- holder.btnEliminar.setOnClickListener(v -> {
- if (fragment != null) fragment.confirmarExclusao(transacao);
- });
+ holder.tvData.setText(t.getData());
+
+ if (t.getTipo() == 1) { // Receita
+ holder.tvValor.setText(String.format("+ %.2f €", t.getValor()));
+ holder.tvValor.setTextColor(Color.parseColor("#00E676"));
+ holder.imgIcone.setImageResource(R.drawable.ic_wallet);
+ } else { // Despesa
+ holder.tvValor.setText(String.format("- %.2f €", t.getValor()));
+ holder.tvValor.setTextColor(Color.parseColor("#FF1744"));
+ holder.imgIcone.setImageResource(obterIconeInteligente(t.getCategoria(), t.getDescricao()));
+ }
+
+ holder.itemView.setOnClickListener(v -> listener.onTransacaoClick(t));
}
@Override
public int getItemCount() {
- return listaTransacoes.size();
+ return listaVisivel.size();
}
- public class MyViewHolder extends RecyclerView.ViewHolder {
- // --- AQUI ESTÃO OS NOVOS NOMES DO XML ---
- TextView tvDescricao, tvValor, tvData;
- ImageView imgIcone, btnEliminar;
+ // 🧠 FUNÇÃO INTELIGENTE PARA REMOVER ACENTOS (ex: transformar "água" em "agua")
+ private String removerAcentos(String str) {
+ if (str == null) return "";
+ CharSequence unaccented = java.text.Normalizer.normalize(str, java.text.Normalizer.Form.NFD);
+ return unaccented.toString().replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
+ }
- public MyViewHolder(@NonNull View itemView) {
+ // 🚀 O NOVO MOTOR DE BUSCA TECNOLÓGICO
+ public void filtrarLista(String textoProcura) {
+ listaVisivel.clear();
+
+ if (textoProcura == null || textoProcura.trim().isEmpty()) {
+ listaVisivel.addAll(listaOriginal);
+ } else {
+ // Limpamos o texto: sem espaços extra, em minúsculas e sem acentos!
+ String queryLimpa = removerAcentos(textoProcura.toLowerCase().trim());
+
+ for (Transacao t : listaOriginal) {
+ String descricao = removerAcentos(t.getDescricao() != null ? t.getDescricao().toLowerCase() : "");
+ String categoria = removerAcentos(t.getCategoria() != null ? t.getCategoria().toLowerCase() : "");
+
+ // Transforma o valor e a data em texto para também podermos pesquisar por eles!
+ String valorStr = String.valueOf(t.getValor());
+ String dataStr = t.getData() != null ? t.getData() : "";
+
+ // 1. Pesquisa por Palavras (Ignora letras no meio das palavras)
+ boolean matchDescricao = descricao.startsWith(queryLimpa) || descricao.contains(" " + queryLimpa);
+ boolean matchCategoria = categoria.startsWith(queryLimpa) || categoria.contains(" " + queryLimpa);
+
+ // 2. Pesquisa de Matemática e Calendário
+ boolean matchValor = valorStr.contains(queryLimpa);
+ boolean matchData = dataStr.contains(queryLimpa);
+
+ // Se bater certo com a Descrição, Categoria, Valor OU Data... a transação é mostrada!
+ if (matchDescricao || matchCategoria || matchValor || matchData) {
+ listaVisivel.add(t);
+ }
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ public int obterIconeInteligente(String cat, String desc) {
+ String frase = (cat + " " + (desc != null ? desc : "")).toLowerCase();
+
+ // --- EXCEÇÕES PREMIUM (Ícones Específicos que criaste) ---
+ if (frase.matches(".*(café|snack).*")) return R.drawable.ic_cafe;
+ if (frase.matches(".*(supermercado|pingo doce|continente|lidl).*")) return R.drawable.ic_carrinho;
+ if (frase.matches(".*(renda|casa|condomínio).*")) return R.drawable.ic_casa;
+ if (frase.matches(".*(internet|meo|nos|vodafone|telemóvel).*")) return R.drawable.ic_telemovel;
+ if (frase.matches(".*(ginásio|fitness|yoga).*")) return R.drawable.ic_ginasio;
+
+ if (frase.matches(".*(compras|roupa|sapatilhas|zara|shein|amazon|aliexpress|worten|fnac|shopping|loja|perfume).*")) {
+ return R.drawable.ic_compras;
+ }
+
+ // --- GRUPOS GERAIS DA TUA LISTA ---
+ if (frase.matches(".*(restaurante|almoço|jantar|pizza|burger|sushi).*")) {
+ return R.drawable.ic_alimentacao;
+ }
+ if (frase.matches(".*(luz|edp|água|gás|seguro).*")) {
+ return R.drawable.ic_contas;
+ }
+ if (frase.matches(".*(uber|bolt|gota|gasolina|diesel|repsol|galp|autocarro|comboio|cp|metro|oficina|estacionamento).*")) {
+ return R.drawable.ic_transportes;
+ }
+ if (frase.matches(".*(jogo|steam|ps5|xbox|netflix|disney|spotify|cinema|concerto|bar|festa|estádio|futebol).*")) {
+ return R.drawable.ic_lazer;
+ }
+ if (frase.matches(".*(farmácia|médico|hospital|dentista|exames).*")) {
+ return R.drawable.ic_saude;
+ }
+ if (frase.matches(".*(escola|faculdade|curso|livro|udemy|propina|papelaria).*")) {
+ return R.drawable.ic_educacao;
+ }
+
+ switch (cat.toLowerCase()) {
+ case "alimentação": return R.drawable.ic_alimentacao;
+ case "contas": return R.drawable.ic_contas;
+ case "transportes": return R.drawable.ic_transportes;
+ case "compras": return R.drawable.ic_compras;
+ case "lazer": return R.drawable.ic_lazer;
+ case "educação": return R.drawable.ic_educacao;
+ case "saúde": return R.drawable.ic_saude;
+ case "salário":
+ case "mesada":
+ case "prémios": return R.drawable.ic_wallet;
+ default: return R.drawable.ic_outros;
+ }
+ }
+
+ static class TransacaoViewHolder extends RecyclerView.ViewHolder {
+ TextView tvDescricao, tvData, tvValor;
+ ImageView imgIcone;
+
+ public TransacaoViewHolder(@NonNull View itemView) {
super(itemView);
- // Agora liga perfeitamente ao layout tech!
tvDescricao = itemView.findViewById(R.id.tvDescricao);
tvData = itemView.findViewById(R.id.tvData);
tvValor = itemView.findViewById(R.id.tvValor);
imgIcone = itemView.findViewById(R.id.imgIcone);
- btnEliminar = itemView.findViewById(R.id.btnEliminar);
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/TransacoesFragment.java b/app/src/main/java/com/example/finzora/TransacoesFragment.java
index 01afa5d..d2042d8 100644
--- a/app/src/main/java/com/example/finzora/TransacoesFragment.java
+++ b/app/src/main/java/com/example/finzora/TransacoesFragment.java
@@ -2,11 +2,18 @@ package com.example.finzora;
import android.app.AlertDialog;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -14,6 +21,8 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+
import org.json.JSONArray;
import org.json.JSONObject;
@@ -31,13 +40,30 @@ public class TransacoesFragment extends Fragment {
private RecyclerView recyclerTransacoes;
private TransacoesAdapter adapter;
+ private View layoutEstadoVazio;
+ private EditText editPesquisar;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_transacoes, container, false);
+
recyclerTransacoes = view.findViewById(R.id.recyclerTransacoes);
recyclerTransacoes.setLayoutManager(new LinearLayoutManager(getActivity()));
+ layoutEstadoVazio = view.findViewById(R.id.layoutEstadoVazio);
+ editPesquisar = view.findViewById(R.id.editPesquisar);
+
+ editPesquisar.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ if (adapter != null) { adapter.filtrarLista(s.toString()); }
+ }
+ @Override
+ public void afterTextChanged(Editable s) {}
+ });
+
return view;
}
@@ -47,20 +73,15 @@ public class TransacoesFragment extends Fragment {
carregarDadosDoSupabase();
}
- // ====================================================================
- // BUSCAR AS TRANSAÇÕES À NUVEM (SUPABASE)
- // ====================================================================
public void carregarDadosDoSupabase() {
SharedPreferences prefs = getActivity().getSharedPreferences("DadosUtilizador", Context.MODE_PRIVATE);
String userId = prefs.getString("user_id", null);
- if (userId == null) return; // Se não houver utilizador, não faz nada
+ if (userId == null) return;
OkHttpClient client = new OkHttpClient();
-
- // O URL pede ao Supabase: "Dá-me as transações onde o user_id seja igual ao meu!"
Request request = new Request.Builder()
- .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?user_id=eq." + userId + "&order=id.desc")
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?user_id=eq." + userId + "&order=created_at.desc")
.addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
.addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
.build();
@@ -69,90 +90,158 @@ public class TransacoesFragment extends Fragment {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
if (getActivity() != null) {
- getActivity().runOnUiThread(() ->
- Toast.makeText(getActivity(), "Erro de internet ao carregar transações.", Toast.LENGTH_SHORT).show()
- );
+ getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), "Erro de internet.", Toast.LENGTH_SHORT).show());
}
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
- String jsonResposta = response.body().string();
- List listaNuvem = new ArrayList<>();
-
try {
+ String jsonResposta = response.body().string();
+ List listaNuvem = new ArrayList<>();
JSONArray jsonArray = new JSONArray(jsonResposta);
+
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject obj = jsonArray.getJSONObject(i);
-
- // Traduzir do JSON do Supabase para o teu Java
- int id = obj.getInt("id");
- float valor = (float) obj.getDouble("valor");
- String categoria = obj.getString("categoria");
- int tipo = obj.getInt("tipo");
- String data = obj.getString("data");
+ String id = obj.optString("id", "");
+ float valor = (float) obj.optDouble("valor", 0.0);
+ String categoria = obj.isNull("categoria") ? "Desconhecido" : obj.optString("categoria");
+ int tipo = obj.optInt("tipo", 1);
+ String data = obj.isNull("data") ? "" : obj.optString("data");
+ String descricao = obj.isNull("descricao") ? "Sem descrição" : obj.optString("descricao");
Transacao t = new Transacao(valor, categoria, tipo, data);
- t.setId(id); // Guarda o ID verdadeiro da nuvem para podermos apagar depois!
+ t.setId(id);
+ t.setDescricao(descricao);
listaNuvem.add(t);
}
- // Atualizar o ecrã tem de ser sempre na Thread Principal (runOnUiThread)
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
- adapter = new TransacoesAdapter(listaNuvem, getActivity(), TransacoesFragment.this);
+ adapter = new TransacoesAdapter(listaNuvem, transacao -> abrirCartaoDeslizante(transacao));
recyclerTransacoes.setAdapter(adapter);
+
+ if (listaNuvem.isEmpty()) {
+ recyclerTransacoes.setVisibility(View.GONE);
+ layoutEstadoVazio.setVisibility(View.VISIBLE);
+ editPesquisar.setVisibility(View.GONE);
+ } else {
+ recyclerTransacoes.setVisibility(View.VISIBLE);
+ layoutEstadoVazio.setVisibility(View.GONE);
+ editPesquisar.setVisibility(View.VISIBLE);
+ }
});
}
-
- } catch (Exception e) {
- e.printStackTrace();
- }
+ } catch (Exception e) { e.printStackTrace(); }
}
}
});
}
- // ====================================================================
- // APAGAR TRANSAÇÃO NA NUVEM
- // ====================================================================
+ private void abrirCartaoDeslizante(Transacao t) {
+ if (getActivity() == null) return;
+
+ BottomSheetDialog dialog = new BottomSheetDialog(getActivity());
+ View view = getLayoutInflater().inflate(R.layout.bottom_sheet_detalhe, null);
+
+ ImageView imgIcone = view.findViewById(R.id.imgDetalheIcone);
+ TextView tvTitulo = view.findViewById(R.id.tvDetalheTitulo);
+ TextView tvValor = view.findViewById(R.id.tvDetalheValor);
+ TextView tvDataHora = view.findViewById(R.id.tvDetalheDataHora);
+ TextView tvCategoria = view.findViewById(R.id.tvDetalheCategoria);
+ TextView tvDescricao = view.findViewById(R.id.tvDetalheDescricao);
+
+ imgIcone.setImageResource(adapter.obterIconeInteligente(t.getCategoria(), t.getDescricao()));
+
+ String desc = t.getDescricao();
+ if (desc != null && !desc.trim().isEmpty() && !desc.equalsIgnoreCase("Sem descrição") && !desc.equals("null")) {
+ tvTitulo.setText(desc);
+ } else {
+ tvTitulo.setText(t.getCategoria());
+ }
+
+ tvDataHora.setText(t.getData());
+ tvCategoria.setText(t.getCategoria());
+ tvDescricao.setText(desc != null && !desc.trim().isEmpty() && !desc.equals("null") ? desc : "Sem descrição");
+
+ if (t.getTipo() == 1) {
+ tvValor.setText(String.format("+ %.2f €", t.getValor()));
+ tvValor.setTextColor(android.graphics.Color.parseColor("#00E676"));
+ imgIcone.setColorFilter(android.graphics.Color.parseColor("#00E676"));
+ } else {
+ tvValor.setText(String.format("- %.2f €", t.getValor()));
+ tvValor.setTextColor(android.graphics.Color.parseColor("#FF1744"));
+ imgIcone.setColorFilter(android.graphics.Color.parseColor("#FF1744"));
+ }
+
+ view.findViewById(R.id.btnFecharDetalhe).setOnClickListener(v -> dialog.dismiss());
+
+ view.findViewById(R.id.btnEditarTransacao).setOnClickListener(v -> {
+ dialog.dismiss();
+
+ Intent intent = new Intent(getActivity(), AdicionarTransacaoActivity.class);
+ intent.putExtra("transacao_id", t.getId());
+ intent.putExtra("valor", (double) t.getValor());
+ intent.putExtra("descricao", t.getDescricao());
+ intent.putExtra("categoria", t.getCategoria());
+ intent.putExtra("tipo", t.getTipo());
+ startActivity(intent);
+ });
+
+ view.findViewById(R.id.btnApagarTransacao).setOnClickListener(v -> {
+ dialog.dismiss();
+ confirmarExclusao(t);
+ });
+
+ dialog.setContentView(view);
+ dialog.show();
+ }
+
+ // 🏆 NOVA JOGADA: O Pop-up Premium para Eliminar Transações!
public void confirmarExclusao(Transacao transacao) {
- new AlertDialog.Builder(getActivity())
- .setTitle("Eliminar Transação")
- .setMessage("Apagar " + transacao.getCategoria() + "?")
- .setPositiveButton("Sim", (dialog, which) -> {
+ if (getActivity() == null) return;
- OkHttpClient client = new OkHttpClient();
- Request request = new Request.Builder()
- .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?id=eq." + transacao.getId())
- .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
- .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
- .delete() // O comando mágico para apagar!
- .build();
+ android.app.Dialog dialog = new android.app.Dialog(getActivity());
+ dialog.setContentView(R.layout.dialog_eliminar);
- client.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
+ dialog.getWindow().setLayout(android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
- @Override
- public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
- if (getActivity() != null) {
- getActivity().runOnUiThread(() -> {
- Toast.makeText(getActivity(), "Eliminado das nuvens!", Toast.LENGTH_SHORT).show();
- carregarDadosDoSupabase(); // Recarrega a lista
+ // Se clicar em cancelar, fecha o pop-up
+ dialog.findViewById(R.id.btnCancelarEliminar).setOnClickListener(v -> dialog.dismiss());
- // Atualiza os cartões na MainActivity
- if (getActivity() instanceof MainActivity) {
- ((MainActivity) getActivity()).atualizarCartoes();
- }
- });
+ // Se clicar no botão vermelho "ELIMINAR", faz a magia no Supabase!
+ dialog.findViewById(R.id.btnConfirmarEliminar).setOnClickListener(v -> {
+ dialog.dismiss();
+
+ OkHttpClient client = new OkHttpClient();
+ Request request = new Request.Builder()
+ .url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes?id=eq." + transacao.getId())
+ .addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
+ .addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
+ .delete()
+ .build();
+
+ client.newCall(request).enqueue(new Callback() {
+ @Override public void onFailure(@NonNull Call call, @NonNull IOException e) {}
+ @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(() -> {
+ Toast.makeText(getActivity(), "Eliminado com sucesso!", Toast.LENGTH_SHORT).show();
+ carregarDadosDoSupabase();
+ editPesquisar.setText("");
+ if (getActivity() instanceof MainActivity) {
+ ((MainActivity) getActivity()).atualizarCartoes();
}
- }
- });
+ });
+ }
+ }
+ });
+ });
- })
- .setNegativeButton("Não", null)
- .show();
+ dialog.show();
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/finzora/ViewPagerAdapter.java b/app/src/main/java/com/example/finzora/ViewPagerAdapter.java
index f579958..665b5fd 100644
--- a/app/src/main/java/com/example/finzora/ViewPagerAdapter.java
+++ b/app/src/main/java/com/example/finzora/ViewPagerAdapter.java
@@ -18,13 +18,14 @@ public class ViewPagerAdapter extends FragmentStateAdapter {
case 0: return new TransacoesFragment();
case 1: return new OrcamentoFragment();
case 2: return new GraficosFragment();
- case 3: return new DicasFragment();
+ case 3: return new ObjetivosFragment(); // ⚠️ O NOSSO NOVO JOGADOR ESTÁ AQUI
+ case 4: return new DicasFragment(); // ⚠️ Dicas passou para a posição 4
default: return new TransacoesFragment();
}
}
@Override
public int getItemCount() {
- return 4; // Total de 4 abas
+ return 5; // ⚠️ Total atualizado para 5 abas!
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_bottom_sheet.xml b/app/src/main/res/drawable/bg_bottom_sheet.xml
new file mode 100644
index 0000000..462f1a1
--- /dev/null
+++ b/app/src/main/res/drawable/bg_bottom_sheet.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_coach_input_rect.xml b/app/src/main/res/drawable/bg_coach_input_rect.xml
new file mode 100644
index 0000000..e66745b
--- /dev/null
+++ b/app/src/main/res/drawable/bg_coach_input_rect.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_coach_rect.xml b/app/src/main/res/drawable/bg_coach_rect.xml
new file mode 100644
index 0000000..0e37940
--- /dev/null
+++ b/app/src/main/res/drawable/bg_coach_rect.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_dialog_arredondado.xml b/app/src/main/res/drawable/bg_dialog_arredondado.xml
new file mode 100644
index 0000000..a9849bd
--- /dev/null
+++ b/app/src/main/res/drawable/bg_dialog_arredondado.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_search_bar.xml b/app/src/main/res/drawable/bg_search_bar.xml
new file mode 100644
index 0000000..a5469e6
--- /dev/null
+++ b/app/src/main/res/drawable/bg_search_bar.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_tech_input.xml b/app/src/main/res/drawable/bg_tech_input.xml
new file mode 100644
index 0000000..ad09eac
--- /dev/null
+++ b/app/src/main/res/drawable/bg_tech_input.xml
@@ -0,0 +1,17 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/custom_progress_bar.xml b/app/src/main/res/drawable/custom_progress_bar.xml
new file mode 100644
index 0000000..df4dda1
--- /dev/null
+++ b/app/src/main/res/drawable/custom_progress_bar.xml
@@ -0,0 +1,20 @@
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_alimentacao.xml b/app/src/main/res/drawable/ic_alimentacao.xml
new file mode 100644
index 0000000..1bb0270
--- /dev/null
+++ b/app/src/main/res/drawable/ic_alimentacao.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_cafe.xml b/app/src/main/res/drawable/ic_cafe.xml
new file mode 100644
index 0000000..dca7bbb
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cafe.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_carrinho.xml b/app/src/main/res/drawable/ic_carrinho.xml
new file mode 100644
index 0000000..f2fb67d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_carrinho.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_casa.xml b/app/src/main/res/drawable/ic_casa.xml
new file mode 100644
index 0000000..c1ffbfc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_casa.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_compras.xml b/app/src/main/res/drawable/ic_compras.xml
new file mode 100644
index 0000000..e8a53be
--- /dev/null
+++ b/app/src/main/res/drawable/ic_compras.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_contas.xml b/app/src/main/res/drawable/ic_contas.xml
new file mode 100644
index 0000000..876545b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_contas.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_educacao.xml b/app/src/main/res/drawable/ic_educacao.xml
new file mode 100644
index 0000000..716e991
--- /dev/null
+++ b/app/src/main/res/drawable/ic_educacao.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_ginasio.xml b/app/src/main/res/drawable/ic_ginasio.xml
new file mode 100644
index 0000000..6dcd7b7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_ginasio.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_lazer.xml b/app/src/main/res/drawable/ic_lazer.xml
new file mode 100644
index 0000000..3de073f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_lazer.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_outros.xml b/app/src/main/res/drawable/ic_outros.xml
new file mode 100644
index 0000000..eaf7c3a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_outros.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_salario.xml b/app/src/main/res/drawable/ic_salario.xml
new file mode 100644
index 0000000..778364c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_salario.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_saude.xml b/app/src/main/res/drawable/ic_saude.xml
new file mode 100644
index 0000000..640fad4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_saude.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_telemovel.xml b/app/src/main/res/drawable/ic_telemovel.xml
new file mode 100644
index 0000000..fd710d4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_telemovel.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_transportes.xml b/app/src/main/res/drawable/ic_transportes.xml
new file mode 100644
index 0000000..8f3ae4f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_transportes.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_adicionar_transacao.xml b/app/src/main/res/layout/activity_adicionar_transacao.xml
index 5eb3d1a..ba6ca1d 100644
--- a/app/src/main/res/layout/activity_adicionar_transacao.xml
+++ b/app/src/main/res/layout/activity_adicionar_transacao.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="@drawable/bg_tech_gradient"
+ android:background="@color/bg_dinamico"
android:padding="24dp">
-
+ android:layout_marginBottom="24dp"/>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_definicoes.xml b/app/src/main/res/layout/activity_definicoes.xml
index 97551cd..91b774a 100644
--- a/app/src/main/res/layout/activity_definicoes.xml
+++ b/app/src/main/res/layout/activity_definicoes.xml
@@ -5,32 +5,34 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
- android:background="@color/fundo_app">
+ android:background="@color/fundo_app">
-
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_marginBottom="32dp">
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
+ android:backgroundTint="#424242" />
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_editar_perfil.xml b/app/src/main/res/layout/activity_editar_perfil.xml
index 066e6be..8bcc4c6 100644
--- a/app/src/main/res/layout/activity_editar_perfil.xml
+++ b/app/src/main/res/layout/activity_editar_perfil.xml
@@ -5,48 +5,50 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
- android:background="#1A202C">
+ android:background="@color/bg_dinamico">
-
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_marginBottom="40dp">
-
-
+
+
+
+
@@ -65,7 +67,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Email"
- android:textColor="#B0BEC5"
+ android:textColor="@color/texto_dinamico"
android:textSize="14sp"
android:layout_marginBottom="8dp"/>
@@ -90,4 +92,5 @@
android:textStyle="bold"
android:padding="16dp"
app:cornerRadius="8dp"
- android:backgroundTint="#00E676" />
\ No newline at end of file
+ android:backgroundTint="#00E676" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml
new file mode 100644
index 0000000..610e7c3
--- /dev/null
+++ b/app/src/main/res/layout/activity_lock.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 40d8d1f..c5e0ac0 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -18,14 +18,15 @@
+ app:tint="@color/white"
+ android:scaleType="centerCrop" />
+
+
+
+
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_nova_password.xml b/app/src/main/res/layout/activity_nova_password.xml
new file mode 100644
index 0000000..f3a6c8a
--- /dev/null
+++ b/app/src/main/res/layout/activity_nova_password.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/bottom_sheet_detalhe.xml b/app/src/main/res/layout/bottom_sheet_detalhe.xml
new file mode 100644
index 0000000..5907d23
--- /dev/null
+++ b/app/src/main/res/layout/bottom_sheet_detalhe.xml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_categorias.xml b/app/src/main/res/layout/dialog_categorias.xml
new file mode 100644
index 0000000..e4fc360
--- /dev/null
+++ b/app/src/main/res/layout/dialog_categorias.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_contactar.xml b/app/src/main/res/layout/dialog_contactar.xml
index 6ab745d..5d74ec5 100644
--- a/app/src/main/res/layout/dialog_contactar.xml
+++ b/app/src/main/res/layout/dialog_contactar.xml
@@ -24,9 +24,14 @@
-
-
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_contactos.xml b/app/src/main/res/layout/dialog_contactos.xml
new file mode 100644
index 0000000..5e3b3d7
--- /dev/null
+++ b/app/src/main/res/layout/dialog_contactos.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_eliminar.xml b/app/src/main/res/layout/dialog_eliminar.xml
new file mode 100644
index 0000000..a2f65de
--- /dev/null
+++ b/app/src/main/res/layout/dialog_eliminar.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_exportar.xml b/app/src/main/res/layout/dialog_exportar.xml
new file mode 100644
index 0000000..f17daa5
--- /dev/null
+++ b/app/src/main/res/layout/dialog_exportar.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_faq.xml b/app/src/main/res/layout/dialog_faq.xml
index c3acd21..05abc81 100644
--- a/app/src/main/res/layout/dialog_faq.xml
+++ b/app/src/main/res/layout/dialog_faq.xml
@@ -42,6 +42,12 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_novo_objetivo.xml b/app/src/main/res/layout/dialog_novo_objetivo.xml
new file mode 100644
index 0000000..961c715
--- /dev/null
+++ b/app/src/main/res/layout/dialog_novo_objetivo.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_tipo_transacao.xml b/app/src/main/res/layout/dialog_tipo_transacao.xml
new file mode 100644
index 0000000..7f692bd
--- /dev/null
+++ b/app/src/main/res/layout/dialog_tipo_transacao.xml
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_tutorial.xml b/app/src/main/res/layout/dialog_tutorial.xml
index 456f6e3..fd325db 100644
--- a/app/src/main/res/layout/dialog_tutorial.xml
+++ b/app/src/main/res/layout/dialog_tutorial.xml
@@ -42,8 +42,14 @@
+
-
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_dicas.xml b/app/src/main/res/layout/fragment_dicas.xml
index ba65bc4..0fae662 100644
--- a/app/src/main/res/layout/fragment_dicas.xml
+++ b/app/src/main/res/layout/fragment_dicas.xml
@@ -1,395 +1,313 @@
-
+ android:background="?android:attr/windowBackground">
+ android:visibility="gone">
+ android:text="📊"
+ android:textSize="60sp" />
-
+
+
+
+
+
+
+
+ android:orientation="vertical">
-
+ android:layout_marginBottom="16dp"
+ app:cardBackgroundColor="?android:attr/colorBackgroundFloating"
+ app:cardCornerRadius="20dp"
+ app:cardElevation="8dp">
+ android:orientation="vertical"
+ android:padding="20dp">
+
-
-
+ android:text="🤖 Finzora AI Coach"
+ android:textColor="@color/tech_accent_cyan"
+ android:textSize="18sp"
+ android:textStyle="bold" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:orientation="vertical"
+ android:padding="16dp">
+ android:orientation="horizontal">
+
+ android:text="0%"
+ android:textColor="#00E676"
+ android:textStyle="bold" />
+
+
+
+
+
-
-
-
-
+ android:textStyle="bold" />
+ android:textStyle="bold" />
-
-
+
-
-
-
-
-
-
-
-
+ android:layout_marginBottom="12dp"
+ app:cardBackgroundColor="?android:attr/colorBackgroundFloating"
+ app:cardCornerRadius="16dp"
+ app:cardElevation="2dp">
-
-
+
+
+
+
+
+
+
-
-
-
-
+ android:layout_marginBottom="12dp"
+ app:cardBackgroundColor="?android:attr/colorBackgroundFloating"
+ app:cardCornerRadius="16dp"
+ app:cardElevation="2dp">
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+ android:orientation="vertical" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_graficos.xml b/app/src/main/res/layout/fragment_graficos.xml
index 5b7de45..2556d29 100644
--- a/app/src/main/res/layout/fragment_graficos.xml
+++ b/app/src/main/res/layout/fragment_graficos.xml
@@ -1,88 +1,147 @@
-
+ android:background="@color/bg_dinamico">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:gravity="center"
+ android:visibility="gone"
+ android:padding="32dp">
+
+
-
-
-
-
-
+ android:textColor="@color/texto_principal"
+ android:layout_marginTop="16dp"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:text="Regista as tuas primeiras transações para veres a magia dos gráficos acontecer."
+ android:textSize="15sp"
+ android:textAlignment="center"
+ android:textColor="?android:attr/textColorSecondary"
+ android:layout_marginTop="8dp"/>
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_objetivos.xml b/app/src/main/res/layout/fragment_objetivos.xml
new file mode 100644
index 0000000..78abc31
--- /dev/null
+++ b/app/src/main/res/layout/fragment_objetivos.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_orcamento.xml b/app/src/main/res/layout/fragment_orcamento.xml
index 9c16cc7..2a6424f 100644
--- a/app/src/main/res/layout/fragment_orcamento.xml
+++ b/app/src/main/res/layout/fragment_orcamento.xml
@@ -1,29 +1,28 @@
+ android:background="?android:attr/windowBackground"
+ android:padding="16dp">
+ android:textStyle="bold"
+ android:layout_marginBottom="12dp"/>
+ app:cardBackgroundColor="?android:attr/colorBackgroundFloating"
+ app:cardCornerRadius="16dp"
+ app:cardElevation="2dp"
+ android:layout_marginBottom="16dp">
-
-
-
+ android:orientation="horizontal"
+ android:baselineAligned="false">
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+ android:textColor="#1A202C"
+ android:textStyle="bold"
+ android:layout_marginTop="16dp"
+ app:cornerRadius="8dp"
+ app:backgroundTint="@color/tech_accent_cyan" />
@@ -80,14 +110,55 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Orçamentos Ativos"
- android:textColor="@color/white"
- android:textStyle="bold"
+ android:textColor="?android:attr/textColorPrimary"
android:textSize="18sp"
+ android:textStyle="bold"
android:layout_marginBottom="8dp"/>
-
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_transacoes.xml b/app/src/main/res/layout/fragment_transacoes.xml
index badfb98..1847d1f 100644
--- a/app/src/main/res/layout/fragment_transacoes.xml
+++ b/app/src/main/res/layout/fragment_transacoes.xml
@@ -1,14 +1,88 @@
+ android:background="@color/bg_dinamico">
-
\ No newline at end of file
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="20dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp"
+ android:background="@drawable/bg_search_bar"
+ android:orientation="horizontal"
+ android:padding="12dp"
+ android:gravity="center_vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_dropdown.xml b/app/src/main/res/layout/item_dropdown.xml
index 7b804b7..6e4c36b 100644
--- a/app/src/main/res/layout/item_dropdown.xml
+++ b/app/src/main/res/layout/item_dropdown.xml
@@ -3,10 +3,7 @@
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingHorizontal="16dp"
- android:paddingVertical="12dp"
+ android:padding="16dp"
android:textSize="16sp"
- android:textColor="#FFFFFF"
- android:background="#1A202C"
- android:ellipsize="end"
- android:singleLine="true"/>
\ No newline at end of file
+ android:textColor="@color/texto_principal"
+ android:background="@color/bg_dinamico" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_objetivo.xml b/app/src/main/res/layout/item_objetivo.xml
new file mode 100644
index 0000000..57ed497
--- /dev/null
+++ b/app/src/main/res/layout/item_objetivo.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_orcamento.xml b/app/src/main/res/layout/item_orcamento.xml
index bc5ad3c..c1cbc67 100644
--- a/app/src/main/res/layout/item_orcamento.xml
+++ b/app/src/main/res/layout/item_orcamento.xml
@@ -3,10 +3,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="12dp"
- app:cardCornerRadius="12dp"
- app:cardBackgroundColor="#2C5364"
- app:cardElevation="2dp">
+ android:layout_marginHorizontal="16dp"
+ android:layout_marginVertical="8dp"
+ app:cardBackgroundColor="?android:attr/colorBackgroundFloating"
+ app:cardCornerRadius="16dp"
+ app:cardElevation="4dp">
+ android:gravity="center_vertical">
+
+
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="18sp"
+ android:textStyle="bold" />
+
+
+ android:text="75%"
+ android:textColor="@color/tech_accent_cyan"
+ android:textStyle="bold" />
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp"
+ android:progress="75"
+ android:progressDrawable="@drawable/custom_progress_bar" />
-
+ android:textAlignment="viewEnd" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_transacao.xml b/app/src/main/res/layout/item_transacao.xml
index 7fbf9ff..17b60fa 100644
--- a/app/src/main/res/layout/item_transacao.xml
+++ b/app/src/main/res/layout/item_transacao.xml
@@ -5,10 +5,9 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="8dp"
- app:cardBackgroundColor="#FFFFFF"
+ app:cardBackgroundColor="@color/fundo_cartao"
app:cardCornerRadius="12dp"
app:cardElevation="2dp">
-
@@ -45,7 +44,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Data"
- android:textColor="#718096"
+ android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:layout_marginTop="4dp"/>
diff --git a/app/src/main/res/raw/anim_dicas_vazio.json b/app/src/main/res/raw/anim_dicas_vazio.json
new file mode 100644
index 0000000..2ee6e4b
--- /dev/null
+++ b/app/src/main/res/raw/anim_dicas_vazio.json
@@ -0,0 +1 @@
+{"nm":"Comp 1","ddd":0,"h":1080,"w":1080,"meta":{"g":"@lottiefiles/toolkit-js 0.33.2"},"layers":[{"ty":0,"nm":"Pre-comp 1","sr":0.9,"st":0,"op":81,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[568,540,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":1080,"h":1080,"refId":"comp_0","ind":1}],"v":"5.7.0","fr":30,"op":81,"ip":0,"assets":[{"nm":"","id":"comp_0","layers":[{"ty":4,"nm":"ÃÂÃÂúð 2","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-221.715,95.051,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[180.285,91.051,0],"t":0,"ti":[-0.167,0.333,0],"to":[-0.5,2.333,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[177.285,105.051,0],"t":28,"ti":[-0.667,4,0],"to":[0.167,-0.333,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[181.285,89.051,0],"t":49,"ti":[0.167,-0.333,0],"to":[0.667,-4,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[181.285,81.051,0],"t":67,"ti":[0.167,-1.667,0],"to":[-0.167,0.333,0]},{"s":[180.285,91.051,0],"t":89}],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[190],"t":7},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1.019},"s":[191],"t":28},{"o":{"x":0.333,"y":0.034},"i":{"x":0.667,"y":1},"s":[198.741],"t":45},{"s":[190],"t":79}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[4.87,-6.025],[-8,-27],[-10.721,6.14],[-0.455,5.253],[-4,11],[8.822,3.516]],"o":[[-59,73],[4.497,15.177],[8.352,-4.783],[9,-104],[1.863,-5.123],[-10.121,-4.034]],"v":[[-242.5,83.5],[-273,271],[-238.265,285.283],[-229,261],[-201,97],[-210.355,75.636]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-283.121,160.699],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.9215686274509803,0.9568627450980393,0.9882352941176471,0.655,0.8666666666666667,0.8823529411764706,0.9019607843137255,1,0.8117647058823529,0.8117647058823529,0.8117647058823529],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-235.688,173.874],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1,"parent":7},{"ty":4,"nm":"ÃÂÃÂúð 1","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-223.715,93.051,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-223.715,93.051,0],"t":3,"ti":[0,0,0],"to":[0,4.333,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-223.715,119.051,0],"t":31,"ti":[0,4.333,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-223.715,93.051,0],"t":52,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-223.715,76.051,0],"t":70,"ti":[0,0,0],"to":[0,0,0]},{"s":[-223.715,93.051,0],"t":89}],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.667,"y":1},"s":[0],"t":3},{"o":{"x":0.296,"y":0},"i":{"x":0.665,"y":0.832},"s":[-11],"t":40},{"o":{"x":0.428,"y":-0.7},"i":{"x":0.811,"y":1.256},"s":[3.659],"t":67},{"s":[0],"t":89}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[4.87,-6.025],[-8,-27],[-10.721,6.14],[-0.455,5.253],[-4,11],[8.822,3.516]],"o":[[-59,73],[4.497,15.177],[8.352,-4.783],[9,-104],[1.863,-5.123],[-10.121,-4.034]],"v":[[-242.5,83.5],[-273,271],[-238.265,285.283],[-229,261],[-201,97],[-210.355,75.636]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-227.438,178.621],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.9215686274509803,0.9568627450980393,0.9882352941176471,0.655,0.8666666666666667,0.8823529411764706,0.9019607843137255,1,0.8117647058823529,0.8117647058823529,0.8117647058823529],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-271.536,175.151],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2,"parent":7},{"ty":4,"nm":"óûð÷ 2","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-158,-120.5,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.667,"y":1},"s":[100,100,100],"t":36},{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[100,0,100],"t":41},{"s":[100,100,100],"t":46}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[120,-120.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":20,"ix":4},"s":{"a":0,"k":[36,93],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4941,0.5412,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-158,-120.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3,"parent":5},{"ty":4,"nm":"óûð÷ 1","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-158,-120.5,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.667,"y":1},"s":[100,100,100],"t":36},{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[100,0,100],"t":41},{"s":[100,100,100],"t":46}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[-158,-120.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":20,"ix":4},"s":{"a":0,"k":[36,93],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4941,0.5412,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-158,-120.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4,"parent":5},{"ty":4,"nm":"ÃÂúÃÂðý","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-16.492,-119,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-16.492,-119,0],"t":7,"ti":[-6.794,3.569,0],"to":[6.794,-3.569,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[24.269,-140.413,0],"t":45,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[24.269,-140.413,0],"t":53,"ti":[6.794,-3.569,0],"to":[-6.794,3.569,0]},{"s":[-16.492,-119,0],"t":79}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 2","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[0,0.5],[80.5,-54],[0,0],[-3,82.5],[43.5,1]],"o":[[-1.383,5.531],[0,0],[0,0],[1.508,-41.476],[-43.5,-1]],"v":[[9,-199.75],[-65,-40.5],[158,-40.5],[210,-121],[152.25,-199.5]]}],"t":7},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[{"c":true,"i":[[0,0.5],[80.5,-54],[0,0],[-3,82.5],[43.5,1]],"o":[[-1.383,5.531],[0,0],[0,0],[1.508,-41.476],[-43.5,-1]],"v":[[31.083,-199.182],[-42.917,-39.932],[158,-40.5],[210,-121],[152.25,-199.5]]}],"t":45},{"o":{"x":0.333,"y":0},"i":{"x":0.833,"y":0.833},"s":[{"c":true,"i":[[0,0.5],[80.5,-54],[0,0],[-3,82.5],[43.5,1]],"o":[[-1.383,5.531],[0,0],[0,0],[1.508,-41.476],[-43.5,-1]],"v":[[31.083,-199.182],[-42.917,-39.932],[158,-40.5],[210,-121],[152.25,-199.5]]}],"t":53},{"s":[{"c":true,"i":[[0,0.5],[80.5,-54],[0,0],[-3,82.5],[43.5,1]],"o":[[-1.383,5.531],[0,0],[0,0],[1.508,-41.476],[-43.5,-1]],"v":[[9,-199.75],[-65,-40.5],[158,-40.5],[210,-121],[152.25,-199.5]]}],"t":79}],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.2275,0.2275,0.2275],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":2,"cix":2,"np":3,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":64,"ix":4},"s":{"a":0,"k":[441,162],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1333,0.1333,0.1333],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[102.759,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-16.5,-119],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":5,"parent":6},{"ty":4,"nm":"óþûþòð","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-19.719,9.59,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-19.719,9.59,0],"t":7,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[-19.719,45.59,0],"t":47,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-19.719,45.59,0],"t":53,"ti":[0,0,0],"to":[0,0,0]},{"s":[-19.719,9.59,0],"t":79}],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":7},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-9],"t":28},{"s":[0],"t":79}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[42.021,64.812],[78,-156],[-182,-20],[0,0],[-35.843,52.428]],"o":[[-118,-182],[-28.425,56.851],[34.319,3.771],[0,0],[26.995,-39.487]],"v":[[242,-226],[-284,-218],[-178,14],[140,16],[250.406,-40.457]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[198,-58],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.9215686274509803,0.9568627450980393,0.9882352941176471,0.655,0.8666666666666667,0.8823529411764706,0.9019607843137255,1,0.8117647058823529,0.8117647058823529,0.8117647058823529],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-296,-60],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":6,"parent":7},{"ty":4,"nm":"ÃÂõûþ","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-20.704,175.322,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[519.296,715.322,0],"t":0,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[519.296,635.322,0],"t":45,"ti":[0,0,0],"to":[0,0,0]},{"s":[519.296,715.322,0],"t":90}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[227,65],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"c":{"a":0,"k":[0.5765,0.5765,0.5765],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8196,0.8196,0.8196],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-23.5,68.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[51,0],[15.678,-82.948],[-49.731,-49.359],[-13.15,-0.292],[-37.729,38.101],[10.258,44.845]],"o":[[-12.374,0],[-8.179,43.273],[38.555,38.266],[13.544,0.301],[45.84,-46.291],[-15.181,-66.366]],"v":[[-27,31],[-179.678,102.948],[-132.269,283.359],[-25,320],[91.483,284.497],[137.742,101.155]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[86,146],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.9215686274509803,0.9568627450980393,0.9882352941176471,0.655,0.8666666666666667,0.8823529411764706,0.9019607843137255,1,0.8117647058823529,0.8117647058823529,0.8117647058823529],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-166,148],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":7},{"ty":4,"nm":"ÃÂÃÂ
þ 2","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-305.5,-118,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[263.5,-118,0],"t":7,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[247.381,-117.593,0],"t":49,"ti":[0,0,0],"to":[0,0,0]},{"s":[263.5,-118,0],"t":79}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[111,138],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8824,0.8824,0.8824],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-305.5,-118],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":8,"parent":6},{"ty":4,"nm":"ÃÂÃÂ
þ 1","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-305.5,-118,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-305.5,-118,0],"t":7,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-321.619,-117.593,0],"t":49,"ti":[0,0,0],"to":[0,0,0]},{"s":[-305.5,-118,0],"t":79}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[111,138],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.8824,0.8824,0.8824],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-305.5,-118],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":9,"parent":6},{"ty":4,"nm":"ÃÂõýÃÂ","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-17.5,380.5,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100,100,100],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[108,108,100],"t":45},{"s":[100,100,100],"t":90}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[523.5,919.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":60,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[249,59],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.7686,0.7686,0.7686],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-17.5,380.5],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":10}]}]}
\ No newline at end of file
diff --git a/app/src/main/res/raw/anim_grafico_vazio.json b/app/src/main/res/raw/anim_grafico_vazio.json
new file mode 100644
index 0000000..9d71a07
--- /dev/null
+++ b/app/src/main/res/raw/anim_grafico_vazio.json
@@ -0,0 +1 @@
+{"nm":"Comp 1","mn":"","layers":[{"ty":3,"nm":"Null 1","mn":"","sr":1,"st":0,"op":121,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[250,245,0],"t":0,"ti":[0,0,0],"to":[0,1.667,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[250,255,0],"t":60,"ti":[0,1.667,0],"to":[0,0,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[250,245,0],"t":120}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":0,"ix":11}},"ef":[],"ind":1},{"ty":4,"nm":"face","mn":"","sr":1,"st":0,"op":121,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.748,"y":0},"i":{"x":0.667,"y":1},"s":[0,0,0],"t":30,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.178,"y":0},"i":{"x":0.352,"y":1},"s":[8,0,0],"t":45,"ti":[0,0,0],"to":[0,0,0]},{"o":{"x":0.077,"y":0},"i":{"x":0.491,"y":1},"s":[-8,0,0],"t":60,"ti":[-1.333,0,0],"to":[0,0,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.491,"y":1},"s":[3,0,0],"t":73,"ti":[0.5,0,0],"to":[1.333,0,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0,0],"t":84}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":1,"it":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0.964,0],[0.926,1.269],[-2.109,1.537],[-12.421,0],[-9.638,-6.507],[1.46,-2.163],[2.164,1.463],[9.78,0],[8.382,-6.108]],"o":[[-1.46,0],[-1.537,-2.109],[10.009,-7.294],[11.67,0],[2.163,1.46],[-1.458,2.163],[-8.071,-5.448],[-10.408,0],[-0.84,0.612]],"v":[[-30.712,9.847],[-34.536,7.904],[-33.499,1.302],[0.788,-9.847],[33.361,0.099],[34.633,6.66],[28.073,7.932],[0.788,-0.396],[-27.934,8.941]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4275,0.3176,0.8157],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[250,273.902],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,-5.22],[5.22,0],[0,5.22],[-5.22,0]],"o":[[0,5.22],[-5.22,0],[0,-5.22],[5.22,0]],"v":[[9.451,0],[0,9.451],[-9.451,0],[0,-9.451]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4275,0.3176,0.8157],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[212.197,225.702],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,-5.22],[5.22,0],[0,5.22],[-5.22,0]],"o":[[0,5.22],[-5.22,0],[0,-5.22],[5.22,0]],"v":[[9.451,0],[0,9.451],[-9.451,0],[0,-9.451]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4275,0.3176,0.8157],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[287.803,225.702],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tr","a":{"a":0,"k":[287.803,225.702],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[287.803,225.702],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2,"parent":1},{"ty":4,"nm":"magnification glass","mn":"","sr":1,"st":0,"op":121,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[27,24,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[14.17,-15.84],[0.87,-0.89],[25.14,0],[0,49.35],[-49.35,0],[0,-49.35]],"o":[[-0.82,0.93],[-16.27,16.75],[-49.35,0],[0,-49.35],[49.35,0],[0,22.88]],"v":[[39.949,35.36],[37.419,38.08],[-26.751,65.25],[-116.251,-24.25],[-26.751,-113.75],[62.749,-24.25]]},"ix":2}},{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 2","ix":2,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,25.41],[60.93,0],[0,-60.93],[-60.93,0],[-19.48,17.2]],"o":[[14.48,-18.69],[0,-60.93],[-60.93,0],[0,60.93],[27.96,0],[0,0]],"v":[[60.639,43.3],[83.749,-24.25],[-26.751,-134.75],[-137.251,-24.25],[-26.751,86.25],[46.269,58.61]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.4275,0.3176,0.8157],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3,"parent":1}],"ddd":0,"h":500,"w":500,"meta":{"a":"","k":"","d":"","g":"LottieFiles AE 3.2.2","tc":"#000000"},"v":"4.8.0","fr":60,"op":121,"ip":0,"assets":[]}
\ No newline at end of file
diff --git a/app/src/main/res/raw/anim_vazio.json b/app/src/main/res/raw/anim_vazio.json
new file mode 100644
index 0000000..1e44f7f
--- /dev/null
+++ b/app/src/main/res/raw/anim_vazio.json
@@ -0,0 +1 @@
+{"v":"4.8.0","meta":{"g":"LottieFiles AE 1.1.0","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":77.0000031362743,"w":1000,"h":1000,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[480,506,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[41,-30],[34,-78],[-36.607,20.098],[40,-43],[-71,185],[26,-28]],"o":[[0,0],[-30.454,22.283],[-34,78],[51,-28],[-61.995,66.645],[70.57,-183.88],[-26,28]],"v":[[-142,44],[-139,140],[-352,106],[-171,308],[-204,227],[-313,168],[-413,67]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"d","nm":"dash","v":{"a":0,"k":30,"ix":1}},{"n":"g","nm":"gap","v":{"a":0,"k":1369,"ix":2}},{"n":"o","nm":"offset","v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32,"s":[103]},{"t":70.0000028511585,"s":[-1303]}],"ix":7}}],"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":22.0000008960784,"op":1810.00007372281,"st":12.00000048877,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[41,-30],[34,-78],[-36.607,20.098],[40,-43],[-71,185],[26,-28]],"o":[[0,0],[-30.454,22.283],[-34,78],[51,-28],[-61.995,66.645],[70.57,-183.88],[-26,28]],"v":[[-142,44],[-139,140],[-352,106],[-171,308],[-204,227],[-313,168],[-413,67]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"d","nm":"dash","v":{"a":0,"k":12,"ix":1}},{"n":"g","nm":"gap","v":{"a":0,"k":1369,"ix":2}},{"n":"o","nm":"offset","v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[103]},{"t":58.0000023623884,"s":[-1303]}],"ix":7}}],"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":22.0000008960784,"op":1798.00007323404,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"head/boxgirl2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":45,"s":[-5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":50,"s":[-5]},{"t":75.0000030548126,"s":[0]}],"ix":10},"p":{"a":0,"k":[504.173,279.4,0],"ix":2},"a":{"a":0,"k":[615.874,302.163,0],"ix":1},"s":{"a":0,"k":[83,83,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.203,0],[0,0],[0.188,-0.112],[0.266,-0.265],[0.11,-0.275],[-0.203,0],[0,0],[-0.188,0.113],[-0.266,0.266],[-0.11,0.274]],"o":[[0,0],[-0.231,0],[-0.322,0.194],[-0.206,0.206],[-0.041,0.103],[0,0],[0.232,0],[0.322,-0.194],[0.206,-0.205],[0.041,-0.104]],"v":[[3.115,-1.01],[-1.033,-1.01],[-1.711,-0.745],[-2.621,-0.032],[-3.193,0.682],[-3.115,1.01],[1.032,1.01],[1.711,0.745],[2.621,0.031],[3.193,-0.682]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[592.024,140.01],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.441,0],[0,0],[0.377,-0.376],[-0.441,0],[0,0],[-0.377,0.376]],"o":[[0,0],[-0.571,0],[-0.086,0.085],[0,0],[0.572,0],[0.085,-0.085]],"v":[[4.643,-1.01],[-2.561,-1.01],[-4.149,-0.032],[-4.643,1.01],[2.56,1.01],[4.149,0.032]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[589.932,134.862],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.203,0],[0,0],[0.187,-0.112],[0.266,-0.265],[0.109,-0.275],[-0.203,0],[0,0],[-0.188,0.113],[-0.266,0.265],[-0.11,0.274]],"o":[[0,0],[-0.232,0],[-0.323,0.194],[-0.206,0.206],[-0.042,0.103],[0,0],[0.232,0],[0.322,-0.193],[0.206,-0.206],[0.041,-0.104]],"v":[[2.569,-1.01],[-0.487,-1.01],[-1.165,-0.745],[-2.076,-0.032],[-2.647,0.682],[-2.57,1.01],[0.486,1.01],[1.165,0.745],[2.075,0.032],[2.647,-0.682]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[594.189,130.405],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.697,0.13],[1.381,-1.402],[-1.277,-1.162],[-0.539,-2.933],[1.291,0.371],[0.599,1.294],[-0.397,-0.857],[-2.347,3.604],[1.566,1.335],[1.014,0.42],[0.075,0.558],[-0.756,-0.141]],"o":[[-1.872,-0.349],[-1.032,1.047],[1.762,1.602],[0.338,1.839],[-1.421,-0.409],[-0.212,-0.458],[1.982,4.281],[1.124,-1.727],[-0.848,-0.723],[-0.383,-0.158],[-0.118,-0.875],[0.82,0.152]],"v":[[2.104,-7.117],[-2.682,-5.892],[-3.188,-2.149],[2.92,2.141],[-0.213,3.831],[-3.175,1.063],[-5.296,3.185],[4.569,3.198],[3.753,-1.848],[0.801,-3.374],[-1.262,-4.469],[0.261,-4.868]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[602.895,136.398],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-5.784,2.336]],"o":[[0,0],[0,0]],"v":[[-6.716,-0.848],[6.716,-1.168]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.6509803921568628,0.6,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[605.021,201.209],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-5.784,2.336]],"o":[[0,0],[0,0]],"v":[[-6.716,-2.008],[6.716,-0.328]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.6509803921568628,0.6,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[582.244,200.369],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.901,0.241],[8.078,-4.259],[-3.323,-5.913],[-6.748,-0.677],[-6.62,1.477],[0,0]],"o":[[-0.963,-0.223],[-21.25,-5.699],[-6,3.163],[3.324,5.912],[6.748,0.678],[0,0],[0,0]],"v":[[22.043,0.722],[19.248,0.025],[-14.734,-11.997],[-18.914,5.764],[-1.981,15.578],[22.237,12.775],[19.248,0.025]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.6509803921568628,0.6,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[543.515,174.404],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-27.722,-3.24],[2.66,-27.783],[19.547,5.242],[8.078,-4.258],[-3.324,-5.912],[-6.748,-0.677],[-6.62,1.476],[0,0]],"o":[[0,0],[3.818,-27.648],[27.722,3.24],[-63.214,18.371],[-21.25,-5.699],[-6,3.163],[3.323,5.912],[6.748,0.678],[0,0],[0,0]],"v":[[-72.745,16.895],[-29.706,4.359],[30.081,-41.908],[77.588,16.899],[-38.763,28.917],[-72.745,16.895],[-76.924,34.656],[-59.992,44.47],[-35.774,41.668],[-38.763,28.917]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.050980392156862744,0.7764705882352941,0.7215686274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[601.526,145.512],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":3,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.734,-12.941],[-15.624,-0.096],[-3.417,15.247]],"o":[[2.734,12.941],[3.23,15.288],[15.625,0.095],[0,0]],"v":[[-36.33,-25.893],[-31.201,-1.615],[2.397,25.799],[36.33,-1.201]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[602.33,214.252],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[6.091,-7.297]],"o":[[-0.924,12.992],[0,0]],"v":[[5.451,-19.487],[-5.451,19.487]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[594.411,259.908],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[1.404,-13.995],[-7.319,-22.945],[-22.358,-8.953],[-13.565,19.901],[22.678,8.11],[-0.689,25.725],[5.686,1.105]],"o":[[7.301,7.834],[-2.405,23.964],[7.32,22.944],[22.358,8.953],[13.565,-19.9],[-24.231,-8.665],[0.155,-5.79],[-5.686,-1.106]],"v":[[-67.624,-74.46],[-60.836,-50.411],[-55.453,11.567],[-9.899,64.62],[54.789,47.744],[37.918,-12.56],[2.322,-69.611],[-7.677,-82.077]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[0,0],[1.404,-13.995],[-7.319,-22.945],[-22.358,-8.953],[-13.565,19.901],[22.678,8.11],[-0.689,25.725],[5.686,1.105]],"o":[[7.301,7.834],[-2.405,23.964],[7.32,22.944],[22.358,8.953],[13.565,-19.9],[-24.231,-8.665],[0.155,-5.79],[-5.686,-1.106]],"v":[[-67.624,-74.46],[-60.836,-50.411],[-56.182,21.178],[-10.628,74.23],[54.06,57.355],[37.189,-2.949],[2.322,-69.611],[-7.677,-82.077]],"c":true}]},{"t":74.0000030140818,"s":[{"i":[[0,0],[1.404,-13.995],[-7.319,-22.945],[-22.358,-8.953],[-13.565,19.901],[22.678,8.11],[-0.689,25.725],[5.686,1.105]],"o":[[7.301,7.834],[-2.405,23.964],[7.32,22.944],[22.358,8.953],[13.565,-19.9],[-24.231,-8.665],[0.155,-5.79],[-5.686,-1.106]],"v":[[-67.624,-74.46],[-60.836,-50.411],[-55.453,11.567],[-9.899,64.62],[54.789,47.744],[37.918,-12.56],[2.322,-69.611],[-7.677,-82.077]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.35294117647058826,0.37254901960784315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[692.302,244.019],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":3,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1798.00007323404,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"arms/boxgirl2 Outlines","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":7,"s":[-18]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13.824,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":21,"s":[-18]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":27,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":37,"s":[-18]},{"t":54.0000021994651,"s":[-18]}],"ix":10},"p":{"a":0,"k":[506.439,444.242,0],"ix":2},"a":{"a":0,"k":[534.439,398.222,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-3.407],[3.407,0],[0,3.407],[-3.407,0]],"o":[[0,3.407],[-3.407,0],[0,-3.407],[3.407,0]],"v":[[6.169,0],[0,6.169],[-6.168,0],[0,-6.169]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[632.081,461.246],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":7,"s":[{"i":[[0,0],[-22.517,-0.724],[-10.788,37.86]],"o":[[25.288,-7.398],[36.098,3.218],[0,0]],"v":[[-38.207,26.791],[38.078,30.823],[87.513,-6.807]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":14,"s":[{"i":[[0,0],[-22.888,-13.053],[-2.749,18.008]],"o":[[25.288,-7.398],[-17.303,-28.448],[0,0]],"v":[[-38.207,26.791],[38.207,35.761],[21.086,-17.93]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":21,"s":[{"i":[[0,0],[-22.517,-0.724],[-10.788,37.86]],"o":[[25.288,-7.398],[36.098,3.218],[0,0]],"v":[[-38.207,26.791],[38.078,30.823],[87.513,-6.807]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":27.176,"s":[{"i":[[0,0],[-22.888,-13.053],[-2.749,18.008]],"o":[[25.288,-7.398],[-17.303,-28.448],[0,0]],"v":[[-38.207,26.791],[38.207,35.761],[21.086,-17.93]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":37,"s":[{"i":[[0,0],[-22.517,-0.724],[-10.788,37.86]],"o":[[25.288,-7.398],[36.098,3.218],[0,0]],"v":[[-38.207,26.791],[38.078,30.823],[87.513,-6.807]],"c":false}]},{"t":54.0000021994651,"s":[{"i":[[0,0],[-22.517,-0.724],[-10.788,37.86]],"o":[[25.288,-7.398],[36.098,3.218],[0,0]],"v":[[-38.207,26.791],[38.078,30.823],[87.513,-6.807]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[636.453,358.075],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.991,-1.935],[1.393,-9.397],[3.254,-8.925],[-1.049,-10.002],[0,0],[-11.042,-10.598],[20.008,35.512]],"o":[[3.543,-0.536],[7.976,5.16],[-1.392,9.397],[-3.253,8.925],[0,0],[0.418,10.558],[7.218,-35.73],[0,0]],"v":[[-34.042,-55.116],[-23.763,-53.142],[-14.661,-27.77],[-23.79,-0.843],[-27.997,28.159],[-28.105,26.975],[6.752,55.652],[14.035,-54.499]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.050980392156862744,0.7764705882352941,0.7215686274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[635.71,439.352],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-19.251,-20.715],[19.252,-11.749],[12.525,20.715],[-9.779,13.975]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":3,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40784313725490196,0.9529411764705882,0.9098039215686274,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[617.497,415.331],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.037,-6.22],[4.993,-23.691],[-34.033,29.139],[0,0]],"o":[[0,0],[0,0],[-4.999,-3.701],[0,0],[-3.982,18.892],[0,0],[0,0]],"v":[[55.805,-7.473],[11.559,-17.777],[-4.366,-29.569],[-16.488,-23.507],[-52.904,-14.08],[1.026,8.632],[56.886,25.512]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078431372549,1,0.984313725490196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[554.414,404.877],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-7.976,-5.16],[1.393,-9.397],[3.254,-8.925],[-1.049,-10.001],[2.982,11.029]],"o":[[0.571,-9.482],[7.975,5.161],[-1.393,9.396],[-3.254,8.925],[-2.982,-11.029],[0,0]],"v":[[-16.19,-29.242],[5.695,-38.07],[14.797,-12.698],[5.668,14.229],[1.461,43.23],[-8.484,4.143]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.6509803921568628,0.6,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[606.252,424.28],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":7,"s":[{"i":[[0,0],[76.287,8.852],[17.248,1.993]],"o":[[77.45,65.728],[-17.247,-2.001],[0,0]],"v":[[63.279,-99.083],[-7.091,82.071],[-65.557,72.343]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":14,"s":[{"i":[[0,0],[84.904,4.88],[17.248,1.993]],"o":[[83.039,26.063],[-17.334,-0.997],[0,0]],"v":[[-26.996,-99.139],[-19.347,87.726],[-65.557,72.343]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":21,"s":[{"i":[[0,0],[76.287,8.852],[17.248,1.993]],"o":[[77.45,65.728],[-17.247,-2.001],[0,0]],"v":[[63.279,-99.083],[-7.091,82.071],[-65.557,72.343]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":27,"s":[{"i":[[0,0],[84.904,4.88],[17.248,1.993]],"o":[[88.231,6.819],[-17.334,-0.997],[0,0]],"v":[[-46.273,-107.574],[-19.347,87.726],[-65.557,72.343]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":37,"s":[{"i":[[0,0],[76.287,8.852],[17.248,1.993]],"o":[[77.45,65.728],[-17.247,-2.001],[0,0]],"v":[[63.279,-99.083],[-7.091,82.071],[-65.557,72.343]],"c":false}]},{"t":54.0000021994651,"s":[{"i":[[0,0],[76.287,8.852],[17.248,1.993]],"o":[[77.45,65.728],[-17.247,-2.001],[0,0]],"v":[[63.279,-99.083],[-7.091,82.071],[-65.557,72.343]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.050980392156862744,0.7764705882352941,0.7215686274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[717.313,386.909],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1798.00007323404,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"BOX/boxgirl2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":7,"s":[-11]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14.961,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":21.216,"s":[-11]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":26.903,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":36.569,"s":[-11]},{"t":76.0000030955435,"s":[-11]}],"ix":10},"p":{"s":true,"x":{"a":0,"k":497.232,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.525],"y":[0.999]},"o":{"x":[0.167],"y":[0.012]},"t":7,"s":[534.782]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.714],"y":[-0.001]},"t":14.961,"s":[473.393]},{"i":{"x":[0.086],"y":[1.001]},"o":{"x":[0.333],"y":[0]},"t":20.648,"s":[554.791]},{"i":{"x":[0.345],"y":[1.257]},"o":{"x":[0.475],"y":[0.001]},"t":26.903,"s":[484.088]},{"i":{"x":[0.058],"y":[8.039]},"o":{"x":[0.207],"y":[-15.641]},"t":37.138,"s":[534.782]},{"t":76.0000030955435,"s":[534.782]}],"ix":4}},"a":{"a":0,"k":[572.5,586.5,0],"ix":1},"s":{"a":0,"k":[83,83,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.345,-40.918],[0,0],[0,0],[0,0]],"o":[[0,0],[5.794,-40.405],[0,0],[0,0]],"v":[[2.264,43.63],[-8.285,42.896],[-2.262,-43.63],[8.286,-42.896]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[307.842,426.297],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.221,1.428]],"o":[[0,0],[0,0]],"v":[[0.841,-83.44],[-2.611,82.013]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[306.142,427.058],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-69.992,1.014]],"o":[[69.993,-1.014],[0,0]],"v":[[-112.026,3.123],[112.026,-3.123]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[477.19,351.902],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-38.969,-5.99],[1.82,-62.867]],"o":[[41.66,9.085],[-1.821,62.868],[0,0]],"v":[[-56.581,-95.724],[56.581,-72.475],[52.313,95.724]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.866666726505,0.737254901961,0.549019607843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[308.254,427.051],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-43.799,3.2],[-73.661,-9.244],[-9.976,-73.578],[68.598,2.806],[42.988,11.764],[6.219,42.049]],"o":[[64.703,16],[-14.075,53.761],[-67.608,11.939],[-42.988,-11.764],[7.574,-63.979],[43.799,-3.2]],"v":[[-15.044,-99.102],[169.981,-81.65],[161.058,90.364],[-59.207,94.101],[-169.981,65.062],[-167.562,-99.102]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.866666726505,0.737254901961,0.549019607843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[419.236,430.43],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,0],[4.29,-26.25],[54.595,20.476],[-5.348,26.556]],"o":[[-5.881,28.24],[-48.63,-17.126],[7.005,-28.32],[0,0]],"v":[[81.443,-11.521],[72.476,56.222],[-39.877,9.497],[-29.331,-40.56]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":17.235,"s":[{"i":[[0,0],[4.29,-26.25],[54.595,20.475],[-5.348,26.556]],"o":[[-5.881,28.24],[-48.63,-17.126],[7.005,-28.32],[0,0]],"v":[[81.443,-11.521],[72.476,56.222],[-39.877,9.497],[-29.331,-40.56]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[18.337,-0.432],[53.327,11.084],[-23.981,4.669]],"o":[[-25.774,-4.099],[-48.63,-17.126],[24.904,-3.29],[0,0]],"v":[[81.443,-11.521],[31.961,-12.457],[-76.764,-40.908],[-29.331,-40.56]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":22.921,"s":[{"i":[[0,0],[4.29,-26.25],[54.595,20.476],[-5.348,26.556]],"o":[[-5.881,28.24],[-48.63,-17.126],[7.005,-28.32],[0,0]],"v":[[81.443,-11.521],[72.476,56.222],[-39.877,9.497],[-29.331,-40.56]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28.608,"s":[{"i":[[0,0],[4.29,-26.25],[54.595,20.476],[-5.348,26.556]],"o":[[-5.881,28.24],[-48.63,-17.126],[7.005,-28.32],[0,0]],"v":[[81.443,-11.521],[72.476,56.222],[-39.877,9.497],[-29.331,-40.56]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[18.337,-0.432],[53.327,11.084],[-23.981,4.669]],"o":[[-25.774,-4.099],[-48.63,-17.126],[24.904,-3.29],[0,0]],"v":[[81.443,-11.521],[31.961,-12.457],[-76.764,-40.909],[-29.331,-40.56]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":46,"s":[{"i":[[0,0],[4.29,-26.25],[54.595,20.476],[-5.348,26.556]],"o":[[-5.881,28.24],[-48.63,-17.126],[7.005,-28.32],[0,0]],"v":[[81.443,-11.521],[72.476,56.222],[-39.877,9.497],[-29.331,-40.56]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62,"s":[{"i":[[0,0],[5.295,-25.538],[54.595,20.476],[-5.348,26.556]],"o":[[-5.881,28.24],[-48.63,-17.126],[7.005,-28.32],[0,0]],"v":[[81.443,-11.521],[68.394,55.122],[-39.877,9.497],[-29.331,-40.56]],"c":false}]},{"t":77.0000031362743,"s":[{"i":[[0,0],[4.29,-26.25],[54.595,20.476],[-5.348,26.556]],"o":[[-5.881,28.24],[-48.63,-17.126],[7.005,-28.32],[0,0]],"v":[[81.443,-11.521],[72.476,56.222],[-39.877,9.497],[-29.331,-40.56]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.866666726505,0.737254901961,0.549019607843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[278.586,536.051],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,0],[-3.518,-24.757],[85.647,-5.976],[4.373,25.634]],"o":[[1.938,21.197],[-80.626,8.376],[-2.305,-27.236],[0,0]],"v":[[96.352,-34.341],[106.656,24.809],[-115.038,40.968],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[-7.408,-22.677],[85.647,-5.975],[4.373,25.634]],"o":[[1.938,21.197],[-80.626,8.376],[-2.305,-27.236],[0,0]],"v":[[96.353,-34.341],[110.216,25.436],[-110.292,41.803],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":16.666,"s":[{"i":[[0,0],[-3.518,-24.757],[85.647,-5.975],[4.373,25.634]],"o":[[1.938,21.197],[-80.626,8.376],[-2.305,-27.236],[0,0]],"v":[[96.353,-34.341],[106.656,24.809],[-115.038,40.968],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[-29.356,-13.941],[84.442,-5.729],[19.387,14.399]],"o":[[13.559,13.713],[-83.638,8.019],[-32.035,-13.191],[0,0]],"v":[[96.353,-34.341],[136.329,-10.87],[-68.498,3.481],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":22.921,"s":[{"i":[[0,0],[-3.518,-24.757],[85.647,-5.976],[4.373,25.634]],"o":[[1.938,21.197],[-80.626,8.376],[-2.305,-27.236],[0,0]],"v":[[96.352,-34.341],[106.656,24.809],[-115.038,40.968],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28.608,"s":[{"i":[[0,0],[-3.518,-24.757],[85.647,-5.976],[4.373,25.634]],"o":[[1.938,21.197],[-80.626,8.376],[-2.305,-27.236],[0,0]],"v":[[96.352,-34.341],[106.656,24.809],[-115.038,40.968],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[-29.356,-13.941],[84.442,-5.729],[19.387,14.399]],"o":[[13.559,13.713],[-83.638,8.019],[-32.035,-13.191],[0,0]],"v":[[96.353,-34.341],[136.329,-10.87],[-68.498,3.481],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":46,"s":[{"i":[[0,0],[-3.518,-24.757],[85.647,-5.976],[4.373,25.634]],"o":[[1.938,21.197],[-80.626,8.376],[-2.305,-27.236],[0,0]],"v":[[96.352,-34.341],[106.656,24.809],[-115.038,40.968],[-118.704,-27.565]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":52,"s":[{"i":[[0,0],[4.341,-23.172],[85.647,-5.976],[-5.305,25.866]],"o":[[-4.364,20.108],[-80.626,8.376],[3.168,-26.192],[0,0]],"v":[[96.352,-34.341],[83.496,23.989],[-128.163,39.03],[-118.704,-27.565]],"c":false}]},{"t":77.0000031362743,"s":[{"i":[[0,0],[-3.518,-24.757],[85.647,-5.976],[4.373,25.634]],"o":[[1.938,21.197],[-80.626,8.376],[-2.305,-27.236],[0,0]],"v":[[96.352,-34.341],[106.656,24.809],[-115.038,40.968],[-118.704,-27.565]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.866666726505,0.737254901961,0.549019607843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[483.54,552.116],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1798.00007323404,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Legs/boxgirl2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[477.232,513.782,0],"ix":2},"a":{"a":0,"k":[572.5,586.5,0],"ix":1},"s":{"a":0,"k":[83,83,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.742,-7.41],[-14.493,-3.584],[-25.452,2.565]],"o":[[-5.443,5.57],[2.029,20.261],[13.455,3.327],[0,0]],"v":[[-48.474,-30.191],[-57.779,-10.254],[-0.625,23.639],[58.521,27.626]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078431372549,1,0.984313725490196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[647.342,989.901],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-8.064,5.222]],"o":[[0,0],[0,0]],"v":[[-11.624,-6.953],[11.624,1.731]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078431372549,1,0.984313725490196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[618.704,1014.636],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,3.45],[0,0],[0,0],[0,0],[0,0],[3.45,0]],"o":[[-3.45,0],[0,0],[0,0],[0,0],[0,0],[0,3.45],[0,0]],"v":[[-66.997,10.39],[-73.244,4.143],[-73.244,-10.39],[-70.416,-10.39],[73.244,-10.39],[73.244,4.143],[66.997,10.39]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078431372549,1,0.984313725490196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[630.328,1057.988],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.745,2.543],[-7.412,2.56],[0,0],[0,0]],"o":[[0,0],[0,0],[-2.561,-2.245],[-2.206,-7.525],[0,0],[0,0],[0,0]],"v":[[74.419,-1.574],[75.315,23.542],[-68.346,23.542],[-73.109,16.217],[-63.465,-1.762],[-21.177,-16.372],[-11.369,-23.542]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078431372549,1,0.984313725490196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[628.258,1024.055],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.406,-7.312],[-14.756,-2.265],[-25.117,4.846]],"o":[[0,20.776],[3.845,19.996],[13.701,2.102],[0,0]],"v":[[-30.309,-32.271],[-58.915,0.168],[1.056,28.779],[60.321,27.425]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[561.487,975.205],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-7.561,5.927]],"o":[[0,0],[0,0]],"v":[[-11.967,-6.242],[11.968,0.315]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[519.21,1005.327],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-3.437,0.31]],"o":[[0,0],[0,0],[0,0],[0.311,3.436],[0,0]],"v":[[74.051,5],[71.843,-16.903],[-74.051,-3.715],[-72.448,10.933],[-65.663,16.593]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[535.094,1047.055],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.577,12.468],[-1.576,-12.468]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[605.36,1017.685],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.874,-7.296],[-2.753,-2.005]],"o":[[0,0],[0,0],[-7.151,3.218],[0.971,2.467],[0,0]],"v":[[30.393,-26.012],[21.269,-17.987],[-19.532,0.37],[-27.519,19.143],[-22.114,26.012]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[485.974,1017.073],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10.98,"s":[{"i":[[0,0],[-3.442,-105.571]],"o":[[-18.704,112.217],[0,0]],"v":[[-9.493,-158.357],[9.494,158.357]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14.393,"s":[{"i":[[0,0],[-3.442,-105.571]],"o":[[3.442,105.571],[0,0]],"v":[[-9.493,-158.357],[9.494,158.357]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20.648,"s":[{"i":[[0,0],[-3.442,-105.571]],"o":[[-18.704,112.217],[0,0]],"v":[[-9.493,-158.357],[9.494,158.357]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25.765,"s":[{"i":[[0,0],[-3.442,-105.571]],"o":[[3.442,105.571],[0,0]],"v":[[-9.493,-158.357],[9.494,158.357]],"c":false}]},{"t":32.588751327367,"s":[{"i":[[0,0],[-3.442,-105.571]],"o":[[-18.704,112.217],[0,0]],"v":[[-9.493,-158.357],[9.494,158.357]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[519.695,753.384],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10.98,"s":[{"i":[[0,0],[3.081,-104.652]],"o":[[-11.201,108.609],[0,0]],"v":[[2.06,-160.308],[6.06,160.309]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14.393,"s":[{"i":[[0,0],[3.081,-104.652]],"o":[[5.242,106.157],[0,0]],"v":[[2.06,-160.308],[6.06,160.309]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20.648,"s":[{"i":[[0,0],[3.081,-104.652]],"o":[[-11.201,108.609],[0,0]],"v":[[2.06,-160.308],[6.06,160.309]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25.765,"s":[{"i":[[0,0],[3.081,-104.652]],"o":[[5.242,106.156],[0,0]],"v":[[2.06,-160.308],[6.06,160.309]],"c":false}]},{"t":32.588751327367,"s":[{"i":[[0,0],[3.081,-104.652]],"o":[[-11.201,108.609],[0,0]],"v":[[2.06,-160.308],[6.06,160.309]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[591.424,810.794],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 11","np":2,"cix":2,"bm":0,"ix":11,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10.98,"s":[{"i":[[0,0],[-21.928,-106.278]],"o":[[-33.777,91.486],[0,0]],"v":[[12.646,-187.238],[21.131,187.238]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14.393,"s":[{"i":[[0,0],[-10.66,-108.482]],"o":[[-4.585,92.5],[0,0]],"v":[[12.646,-187.238],[21.131,187.238]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20.648,"s":[{"i":[[0,0],[-21.928,-106.278]],"o":[[-33.777,91.486],[0,0]],"v":[[12.646,-187.238],[21.131,187.238]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25.765,"s":[{"i":[[0,0],[-10.66,-108.482]],"o":[[-4.585,92.5],[0,0]],"v":[[12.646,-187.238],[21.131,187.238]],"c":false}]},{"t":32.588751327367,"s":[{"i":[[0,0],[-21.928,-106.278]],"o":[[-33.777,91.486],[0,0]],"v":[[12.646,-187.238],[21.131,187.238]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[685.846,756.199],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 12","np":2,"cix":2,"bm":0,"ix":12,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1798.00007323404,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Body/boxgirl2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10.98,"s":[477.232,513.782,0],"to":[0,-1.167,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14.393,"s":[477.232,506.782,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20.648,"s":[477.232,513.782,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25.765,"s":[477.232,506.782,0],"to":[0,0,0],"ti":[0,-1.167,0]},{"t":32.588751327367,"s":[477.232,513.782,0]}],"ix":2},"a":{"a":0,"k":[572.5,586.5,0],"ix":1},"s":{"a":0,"k":[83,83,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[-1.842,-18.593],[20.162,4.991],[50.908,0.473],[-108.373,217.759],[-16.355,0],[0,0],[0,0],[-16.432,-8.614]],"o":[[6.366,18.406],[2.049,20.67],[-36.237,-8.969],[0,0],[10.336,-20.768],[0,0],[0,0],[18.522,-12.748],[0,0]],"v":[[132.53,-11.826],[161.147,106.417],[106.529,126.902],[-0.019,112.697],[-47.61,-79.325],[-1.019,-144.415],[23.696,-102.197],[71.999,-130.707],[125.265,-134.421]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[0,0],[-1.842,-18.593],[20.162,4.991],[50.908,0.473],[-104.743,241.812],[-16.355,0],[0,0],[0,0],[-16.432,-8.614]],"o":[[6.366,18.406],[2.049,20.67],[-36.237,-8.969],[0,0],[9.221,-21.286],[0,0],[0,0],[18.522,-12.748],[0,0]],"v":[[132.53,-11.826],[161.147,106.417],[106.529,126.902],[-0.019,112.697],[-75.32,-116.675],[-1.019,-144.415],[23.696,-102.197],[71.999,-130.707],[125.265,-134.421]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[-1.842,-18.593],[20.162,4.991],[50.908,0.473],[-108.373,217.759],[-16.355,0],[0,0],[0,0],[-16.432,-8.614]],"o":[[6.366,18.406],[2.049,20.67],[-36.237,-8.969],[0,0],[10.336,-20.768],[0,0],[0,0],[18.522,-12.748],[0,0]],"v":[[132.53,-11.826],[161.147,106.417],[106.529,126.902],[-0.019,112.697],[-47.61,-88.964],[-1.019,-144.415],[23.696,-102.197],[71.999,-130.707],[125.265,-134.421]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[-1.842,-18.593],[20.162,4.991],[50.908,0.473],[-108.373,217.759],[-16.355,0],[0,0],[0,0],[-16.432,-8.614]],"o":[[6.366,18.406],[2.049,20.67],[-36.237,-8.969],[0,0],[10.336,-20.768],[0,0],[0,0],[18.522,-12.748],[0,0]],"v":[[132.53,-11.826],[161.147,106.417],[106.529,126.902],[-0.019,112.697],[-63.272,-105.831],[-1.019,-144.415],[23.696,-102.197],[71.999,-130.707],[125.265,-134.421]],"c":false}]},{"t":33.0000013441176,"s":[{"i":[[0,0],[-1.842,-18.593],[20.162,4.991],[50.908,0.473],[-108.373,217.759],[-16.355,0],[0,0],[0,0],[-16.432,-8.614]],"o":[[6.366,18.406],[2.049,20.67],[-36.237,-8.969],[0,0],[10.336,-20.768],[0,0],[0,0],[18.522,-12.748],[0,0]],"v":[[132.53,-11.826],[161.147,106.417],[106.529,126.902],[-0.019,112.697],[-47.61,-79.325],[-1.019,-144.415],[23.696,-102.197],[71.999,-130.707],[125.265,-134.421]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.050980392156862744,0.7764705882352941,0.7215686274509804,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[559.529,428.725],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1798.00007323404,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Layer 8/boxgirl2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[477.232,513.782,0],"ix":2},"a":{"a":0,"k":[572.5,586.5,0],"ix":1},"s":{"a":0,"k":[83,83,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[47.878,-14.752],[3.234,-71.293],[-5.895,-75.093],[33.038,-81.93],[-85.728,-107.22],[-68.517,107.681],[-69.828,139.983],[-38.029,115.117],[46.807,57.257],[162.699,89.342]],"o":[[-55.539,17.112],[-3.198,70.52],[6.233,79.398],[-49.442,122.608],[99.596,124.565],[62.205,-97.761],[57.996,-116.263],[25.413,-76.926],[-71.866,-87.91],[-72.635,-39.886]],"v":[[-164.114,-536.141],[-273.101,-369.18],[-225.408,-209.28],[-297.757,-3.278],[-280.381,426.328],[113.978,424.615],[140.88,74.349],[340.696,-142.095],[303.58,-375.651],[-19.035,-471.694]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078431372549,1,0.984313725490196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[655.005,614.237],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1798.00007323404,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index d52d5ed..5a543e4 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -4,4 +4,5 @@
#FFFFFF
#2D3748
#2D3748
-
\ No newline at end of file
+
+ #1A202C #FFFFFF
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 2062299..a01075d 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,14 +3,16 @@
#FF000000
#FFFFFFFF
#00E676
- #2D3748
- #A0AEC0
#48BB78
- #ECC94B #F56565
- #1A202C
+ #ECC94B
+ #F56565
- #FFFFFF
- #1A202C
- #F7FAFC
+ #F3F4F6
+ #FFFFFF #FFFFFF
+ #F8F9FA
+
+ #1A202C #718096
#E2E8F0
+ #F3F4F6
+ #1A202C
\ No newline at end of file
diff --git a/app/src/main/res/values/refs.xml b/app/src/main/res/values/refs.xml
deleted file mode 100644
index 55344e5..0000000
--- a/app/src/main/res/values/refs.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 273bbb9..3e03466 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -7,6 +7,7 @@ appcompat = "1.7.1"
material = "1.13.0"
activity = "1.12.2"
constraintlayout = "2.2.1"
+generativeai = "0.9.0"
[libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -16,6 +17,7 @@ appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "a
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+generativeai = { group = "com.google.ai.client.generativeai", name = "generativeai", version.ref = "generativeai" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }