Melhoramentos nos designs todos e correção de erros

This commit is contained in:
2026-03-10 15:42:51 +00:00
parent 05308a944e
commit 347b916c53
72 changed files with 4467 additions and 415 deletions

13
.idea/deviceManager.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

View File

@@ -4,9 +4,7 @@ plugins {
android {
namespace = "com.example.finzora"
compileSdk {
version = release(36)
}
compileSdk = 36
defaultConfig {
applicationId = "com.example.finzora"
@@ -34,8 +32,12 @@ android {
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.13.0")
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
implementation("androidx.viewpager2:viewpager2:1.1.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.code.gson:gson:2.10.1")
implementation(libs.activity)
implementation(libs.constraintlayout)
testImplementation(libs.junit)

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools"
package="com.example.finzora">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
@@ -10,23 +13,33 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Finzora">
android:theme="@style/Theme.Finzora"
tools:targetApi="31">
<activity
android:name=".LoginActivity"
android:exported="true">
<intent-filter>
android:exported="true"
android:windowSoftInputMode="adjustResize"> <intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
<activity android:name=".RegisterActivity" />
<activity
android:name=".RegisterActivity"
android:name=".DefinicoesActivity"
android:exported="false" />
<activity
android:name=".EditarPerfilActivity"
android:exported="false" />
<activity android:name=".RecuperarPasswordActivity" android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="false" />
<activity android:name=".OnboardingActivity" android:theme="@style/Theme.AppCompat.NoActionBar"/>
<activity android:name=".AdicionarTransacaoActivity" />
<activity android:name=".ProfileActivity" />
</application>
</manifest>

View File

@@ -0,0 +1,178 @@
package com.example.finzora;
import android.app.AlertDialog;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class AdicionarTransacaoActivity extends AppCompatActivity {
// Declaração dos componentes do ecrã (Sem os RadioButtons!)
private EditText editValor;
private Spinner spinnerCategoria;
private Button btnGuardar;
private ImageView btnVoltar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_adicionar_transacao);
inicializarComponentes();
// --- ENCHER O SPINNER COM AS CATEGORIAS ---
String[] categorias = {"Alimentação", "Transportes", "Lazer", "Educação", "Saúde", "Salário", "Mesada", "Prémios", "Outros"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(
this,
R.layout.item_dropdown,
categorias
);
adapter.setDropDownViewResource(R.layout.item_dropdown);
spinnerCategoria.setAdapter(adapter);
// Botão para voltar para trás
if (btnVoltar != null) {
btnVoltar.setOnClickListener(v -> finish());
}
// AGORA O BOTÃO GUARDAR CHAMA O POP-UP!
btnGuardar.setOnClickListener(v -> perguntarTipoTransacao());
}
// ====================================================================
// A MAGIA DO POP-UP
// ====================================================================
private void perguntarTipoTransacao() {
String valorStr = editValor.getText().toString().trim();
// 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;
}
// 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);
if (userId == null) {
android.widget.Toast.makeText(this, "Erro: Utilizador não identificado. Faz login novamente.", android.widget.Toast.LENGTH_LONG).show();
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();
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...");
// 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 + "\""
+ "}";
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
okhttp3.Request request = new okhttp3.Request.Builder()
.url(SupabaseConfig.SUPABASE_URL + "/rest/v1/transacoes")
.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)
.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 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();
}
});
}
});
} catch (Exception e) {
android.widget.Toast.makeText(this, "ERRO LOCAL: " + e.getMessage(), android.widget.Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
private void inicializarComponentes() {
editValor = findViewById(R.id.editValor);
spinnerCategoria = findViewById(R.id.spinnerCategoria);
btnGuardar = findViewById(R.id.btnGuardar);
btnVoltar = findViewById(R.id.btnVoltar);
}
}

View File

@@ -0,0 +1,154 @@
package com.example.finzora;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DBHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "finzora.db";
private static final int DATABASE_VERSION = 2; // Versão 2 para incluir Orçamentos
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// Criação da Tabela de Transações
db.execSQL("CREATE TABLE transacoes (id INTEGER PRIMARY KEY AUTOINCREMENT, valor REAL, categoria TEXT, tipo INTEGER, data TEXT)");
// Criação da Tabela de Orçamentos
db.execSQL("CREATE TABLE orcamentos (categoria TEXT PRIMARY KEY, valor_limite REAL)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS transacoes");
db.execSQL("DROP TABLE IF EXISTS orcamentos");
onCreate(db);
}
// ==========================================
// MÉTODOS PARA AS TRANSAÇÕES
// ==========================================
public void adicionarTransacao(Transacao transacao) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("valor", transacao.getValor());
values.put("categoria", transacao.getCategoria());
values.put("tipo", transacao.getTipo());
values.put("data", transacao.getData());
db.insert("transacoes", null, values);
}
public List<Transacao> listarTransacoes() {
List<Transacao> lista = new ArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM transacoes ORDER BY id DESC", null);
if (cursor.moveToFirst()) {
do {
lista.add(new Transacao(
cursor.getInt(0), // id
cursor.getFloat(1), // valor
cursor.getString(2),// categoria
cursor.getInt(3), // tipo
cursor.getString(4) // data
));
} while (cursor.moveToNext());
}
cursor.close();
return lista;
}
public void eliminarTransacao(int id) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete("transacoes", "id=?", new String[]{String.valueOf(id)});
}
// ==========================================
// MÉTODOS PARA OS ORÇAMENTOS
// ==========================================
public void salvarOrcamento(String categoria, float limite) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("categoria", categoria);
values.put("valor_limite", limite);
// CONFLICT_REPLACE garante que se a categoria já existir, ele só atualiza o limite
db.insertWithOnConflict("orcamentos", null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
public Map<String, Float> getOrcamentosDefinidos() {
Map<String, Float> orcamentos = new HashMap<>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM orcamentos", null);
if (cursor.moveToFirst()) {
do {
orcamentos.put(cursor.getString(0), cursor.getFloat(1));
} while (cursor.moveToNext());
}
cursor.close();
return orcamentos;
}
public float getGastoPorCategoria(String categoria) {
SQLiteDatabase db = this.getReadableDatabase();
// Soma todas as transações que sejam Despesa (tipo=2) na categoria escolhida
Cursor cursor = db.rawQuery("SELECT SUM(valor) FROM transacoes WHERE tipo=2 AND categoria=?", new String[]{categoria});
float total = 0;
if (cursor.moveToFirst()) {
total = cursor.getFloat(0);
}
cursor.close();
return total;
}
// ==========================================
// MATEMÁTICA PARA OS CARTÕES E GRÁFICOS
// ==========================================
// Calcula todo o dinheiro que entrou (Receitas, tipo = 1)
public float getTotalReceitas() {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT SUM(valor) FROM transacoes WHERE tipo=1", null);
float total = 0;
if (cursor.moveToFirst()) total = cursor.getFloat(0);
cursor.close();
return total;
}
// Calcula todo o dinheiro que saiu (Despesas, tipo = 2)
public float getTotalDespesas() {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT SUM(valor) FROM transacoes WHERE tipo=2", null);
float total = 0;
if (cursor.moveToFirst()) total = cursor.getFloat(0);
cursor.close();
return total;
}
// Prepara os dados para a aba dos Gráficos (Soma os gastos de cada categoria)
public HashMap<String, Float> getDespesasPorCategoria() {
HashMap<String, Float> mapa = new HashMap<>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT categoria, SUM(valor) FROM transacoes WHERE tipo=2 GROUP BY categoria", null);
if (cursor.moveToFirst()) {
do {
mapa.put(cursor.getString(0), cursor.getFloat(1));
} while (cursor.moveToNext());
}
cursor.close();
return mapa;
}
}

View File

@@ -0,0 +1,175 @@
package com.example.finzora;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
public class DefinicoesActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
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);
TextView btnSuporte = findViewById(R.id.btnSuporte);
Button btnTerminarSessao = findViewById(R.id.btnTerminarSessao);
// --- 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));
});
// --- 2. MAGIA DO MODO ESCURO / CLARO ---
SharedPreferences prefsTema = getSharedPreferences("TemaApp", MODE_PRIVATE);
boolean isModoEscuro = prefsTema.getBoolean("modo_escuro", true);
switchModoEscuro.setChecked(isModoEscuro);
switchModoEscuro.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefsTema.edit().putBoolean("modo_escuro", isChecked).apply();
if (isChecked) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
Toast.makeText(this, "Modo Escuro Ativado 🌙", Toast.LENGTH_SHORT).show();
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
Toast.makeText(this, "Modo Claro Ativado ☀️", Toast.LENGTH_SHORT).show();
}
});
// --- 3. NOTIFICAÇÕES ---
switchNotificacoes.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
Toast.makeText(this, "Notificações Ligadas 🔔", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Notificações Desligadas 🔕", Toast.LENGTH_SHORT).show();
}
});
// --- 4. CENTRO DE SUPORTE (POP-UP) ---
btnSuporte.setOnClickListener(v -> mostrarDialogSuporte());
// --- 5. TERMINAR SESSÃO ---
btnTerminarSessao.setOnClickListener(v -> terminarSessao());
}
private void mostrarDialogSuporte() {
Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_suporte);
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.findViewById(R.id.cardTutorial).setOnClickListener(v -> {
dialog.dismiss();
mostrarDialogTutorial();
});
dialog.findViewById(R.id.cardMensagem).setOnClickListener(v -> {
dialog.dismiss();
mostrarDialogContactar();
});
dialog.findViewById(R.id.cardContactos).setOnClickListener(v -> {
Toast.makeText(this, "Email: suporte@finzora.pt\nTel: +351 800 123 456", Toast.LENGTH_LONG).show();
});
dialog.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();
}
private void mostrarDialogTutorial() {
Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_tutorial);
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();
}
private void terminarSessao() {
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
prefs.edit().clear().apply();
Toast.makeText(this, "Sessão terminada com sucesso!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
}

View File

@@ -1,6 +1,163 @@
package com.example.finzora;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import java.util.HashMap;
import java.util.Map;
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 LinearLayout layoutDistribuicao;
private DBHelper dbHelper;
@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);
progressPoupanca = view.findViewById(R.id.progressPoupanca);
tvTituloDica1 = view.findViewById(R.id.tvTituloDica1);
tvDescDica1 = view.findViewById(R.id.tvDescDica1);
tvTituloDica2 = view.findViewById(R.id.tvTituloDica2);
tvDescDica2 = view.findViewById(R.id.tvDescDica2);
layoutDistribuicao = view.findViewById(R.id.layoutDistribuicao);
dbHelper = new DBHelper(getActivity());
return view;
}
@Override
public void onResume() {
super.onResume();
analisarFinancas();
}
private void analisarFinancas() {
float receitas = dbHelper.getTotalReceitas();
float despesas = dbHelper.getTotalDespesas();
// 1. Atualizar Textos Iniciais
tvDicasReceitas.setText(String.format("€ %.2f", receitas));
tvDicasDespesas.setText(String.format("€ %.2f", despesas));
// 2. Calcular Taxa de Poupança
float taxaPoupanca = 0;
if (receitas > 0) {
taxaPoupanca = ((receitas - despesas) / receitas) * 100;
}
// Se gastou mais do que ganhou, a taxa é 0
if (taxaPoupanca < 0) taxaPoupanca = 0;
tvTaxaPoupanca.setText(String.format("%.1f%%", taxaPoupanca));
progressPoupanca.setProgress((int) taxaPoupanca);
// Cores consoante a saúde financeira
if (taxaPoupanca >= 20) {
tvTaxaPoupanca.setTextColor(Color.parseColor("#00E676")); // Verde
progressPoupanca.setProgressTintList(ColorStateList.valueOf(Color.parseColor("#00E676")));
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")));
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.");
} 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!");
}
// 3. Descobrir a Categoria mais gasta
HashMap<String, Float> gastosPorCategoria = dbHelper.getDespesasPorCategoria();
String piorCategoria = "Nenhuma";
float maiorGasto = 0;
for (Map.Entry<String, Float> entry : gastosPorCategoria.entrySet()) {
if (entry.getValue() > maiorGasto) {
maiorGasto = entry.getValue();
piorCategoria = entry.getKey();
}
}
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!");
} else {
tvTituloDica2.setText("Tudo Controlado ✅");
tvTituloDica2.setTextColor(Color.parseColor("#00E676"));
tvDescDica2.setText("Ainda não tens despesas suficientes para analisarmos. Continua o bom trabalho!");
}
// 4. Construir as barras de Distribuição de Gastos magicamente
layoutDistribuicao.removeAllViews(); // Limpa as barras antigas
if (despesas > 0) {
for (Map.Entry<String, Float> entry : gastosPorCategoria.entrySet()) {
float valorCat = entry.getValue();
if (valorCat > 0) {
float percentagem = (valorCat / despesas) * 100;
// 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
// 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);
}
}
} else {
TextView semDespesas = new TextView(getActivity());
semDespesas.setText("Ainda não existem despesas registadas.");
semDespesas.setTextColor(Color.parseColor("#B0BEC5"));
layoutDistribuicao.addView(semDespesas);
}
}
}

View File

@@ -0,0 +1,59 @@
package com.example.finzora;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class EditarPerfilActivity extends AppCompatActivity {
private EditText editNomePerfil;
private EditText editEmailPerfil;
@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);
// Voltar para as definições
btnVoltar.setOnClickListener(v -> finish());
// 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(nomeAtual);
editEmailPerfil.setText(emailAtual);
// 2. GUARDAR OS DADOS NOVOS
btnGuardarPerfil.setOnClickListener(v -> {
String novoNome = editNomePerfil.getText().toString().trim();
String novoEmail = editEmailPerfil.getText().toString().trim();
if (novoNome.isEmpty()) {
editNomePerfil.setError("O nome não pode estar vazio!");
return;
}
// Grava na memória (SharedPreferences)
SharedPreferences.Editor editor = prefs.edit();
editor.putString("nome_usuario", novoNome);
editor.putString("email_usuario", novoEmail);
editor.apply();
Toast.makeText(this, "Perfil atualizado com sucesso! 🎉", Toast.LENGTH_SHORT).show();
finish(); // Fecha o ecrã e volta atrás
});
}
}

View File

@@ -1,6 +1,190 @@
package com.example.finzora;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
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 java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class GraficosFragment extends Fragment {
private PieChart pieChartDespesas;
private BarChart barChartOrcamento;
private BarChart barChartTendencia;
private DBHelper dbHelper;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_graficos, container, false);
pieChartDespesas = view.findViewById(R.id.pieChartDespesas);
barChartOrcamento = view.findViewById(R.id.barChartOrcamento);
barChartTendencia = view.findViewById(R.id.barChartTendencia);
dbHelper = new DBHelper(getActivity());
return view;
}
@Override
public void onResume() {
super.onResume();
carregarPieChart();
carregarBarChartOrcamento();
carregarBarChartTendencia();
}
// ==========================================
// 1. GRÁFICO CIRCULAR (Despesas por Categoria)
// ==========================================
private void carregarPieChart() {
pieChartDespesas.getDescription().setEnabled(false);
pieChartDespesas.setHoleColor(Color.parseColor("#2C5364"));
pieChartDespesas.getLegend().setTextColor(Color.WHITE);
pieChartDespesas.setCenterText("Despesas");
pieChartDespesas.setCenterTextColor(Color.WHITE);
pieChartDespesas.setEntryLabelColor(Color.WHITE);
HashMap<String, Float> despesas = dbHelper.getDespesasPorCategoria();
ArrayList<PieEntry> entradas = new ArrayList<>();
for (Map.Entry<String, Float> entry : despesas.entrySet()) {
if (entry.getValue() > 0) entradas.add(new PieEntry(entry.getValue(), entry.getKey()));
}
if (entradas.isEmpty()) { pieChartDespesas.clear(); return; }
PieDataSet dataSet = new PieDataSet(entradas, "");
dataSet.setColors(new int[]{Color.parseColor("#7C4DFF"), Color.parseColor("#00E5FF"), Color.parseColor("#FFD600"), Color.parseColor("#FF4081")});
PieData data = new PieData(dataSet);
data.setValueTextSize(14f);
data.setValueTextColor(Color.WHITE);
pieChartDespesas.setData(data);
pieChartDespesas.animateY(1000);
}
// ==========================================
// 2. GRÁFICO DE BARRAS (Orçamento vs Gastos)
// ==========================================
private void carregarBarChartOrcamento() {
configurarEstiloBarChart(barChartOrcamento);
Map<String, Float> orcamentos = dbHelper.getOrcamentosDefinidos();
ArrayList<BarEntry> gastosEntries = new ArrayList<>();
ArrayList<BarEntry> orcamentoEntries = new ArrayList<>();
ArrayList<String> categorias = new ArrayList<>();
int index = 0;
for (Map.Entry<String, Float> entry : orcamentos.entrySet()) {
String categoria = entry.getKey();
float limite = entry.getValue();
float gasto = dbHelper.getGastoPorCategoria(categoria);
categorias.add(categoria);
gastosEntries.add(new BarEntry(index, gasto));
orcamentoEntries.add(new BarEntry(index, limite));
index++;
}
if (categorias.isEmpty()) { barChartOrcamento.clear(); return; }
BarDataSet setGastos = new BarDataSet(gastosEntries, "Gastos Reais");
setGastos.setColor(Color.parseColor("#FF4081")); // Rosa (Figma)
setGastos.setValueTextColor(Color.WHITE);
BarDataSet setOrcamento = new BarDataSet(orcamentoEntries, "Orçamento");
setOrcamento.setColor(Color.parseColor("#00E5FF")); // Azul (Figma)
setOrcamento.setValueTextColor(Color.WHITE);
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;
data.setBarWidth(barWidth);
barChartOrcamento.setData(data);
barChartOrcamento.groupBars(-0.5f, groupSpace, barSpace);
// Labels no Eixo X
XAxis xAxis = barChartOrcamento.getXAxis();
xAxis.setValueFormatter(new IndexAxisValueFormatter(categorias));
xAxis.setAxisMinimum(-0.5f);
xAxis.setAxisMaximum(categorias.size() - 0.5f);
barChartOrcamento.animateY(1000);
}
// ==========================================
// 3. GRÁFICO DE BARRAS (Tendência Mensal Geral)
// ==========================================
private void carregarBarChartTendencia() {
configurarEstiloBarChart(barChartTendencia);
float totalReceitas = dbHelper.getTotalReceitas();
float totalDespesas = dbHelper.getTotalDespesas();
ArrayList<BarEntry> despesaEntry = new ArrayList<>();
ArrayList<BarEntry> receitaEntry = new ArrayList<>();
despesaEntry.add(new BarEntry(0, totalDespesas));
receitaEntry.add(new BarEntry(0, totalReceitas));
BarDataSet setDespesas = new BarDataSet(despesaEntry, "Despesas");
setDespesas.setColor(Color.parseColor("#FF1744")); // Vermelho
setDespesas.setValueTextColor(Color.WHITE);
BarDataSet setReceitas = new BarDataSet(receitaEntry, "Receitas");
setReceitas.setColor(Color.parseColor("#00E676")); // Verde
setReceitas.setValueTextColor(Color.WHITE);
BarData data = new BarData(setDespesas, setReceitas);
float groupSpace = 0.3f; float barSpace = 0.05f; float barWidth = 0.3f;
data.setBarWidth(barWidth);
barChartTendencia.setData(data);
barChartTendencia.groupBars(-0.5f, groupSpace, barSpace);
ArrayList<String> labelMes = new ArrayList<>();
labelMes.add("Atual");
XAxis xAxis = barChartTendencia.getXAxis();
xAxis.setValueFormatter(new IndexAxisValueFormatter(labelMes));
xAxis.setAxisMinimum(-0.5f);
xAxis.setAxisMaximum(0.5f);
barChartTendencia.animateY(1000);
}
// 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.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisLeft().setDrawGridLines(true);
chart.getAxisLeft().setGridColor(Color.parseColor("#455A64")); // Linhas de fundo subtis
XAxis xAxis = chart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setTextColor(Color.WHITE);
xAxis.setDrawGridLines(false);
xAxis.setGranularity(1f);
xAxis.setCenterAxisLabels(true);
}
}

View File

@@ -1,36 +1,66 @@
package com.example.finzora;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.textfield.TextInputEditText;
import org.json.JSONObject;
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 LoginActivity extends AppCompatActivity {
private TextInputEditText editEmail, editPassword;
private Button btnEntrar;
private TextView txtRegistrar;
private TextView txtRegistrar, txtEsqueciPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
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
}
setContentView(R.layout.activity_login);
// 1. Inicializar os componentes da interface
inicializarComponentes();
// 2. Configurar o clique no botão "Entrar"
// Clique no botão "Entrar" -> Agora faz Login de verdade!
btnEntrar.setOnClickListener(v -> validarDados());
// 3. Configurar o clique para ir para a tela de Registo
// 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));
});
}
private void validarDados() {
@@ -44,20 +74,81 @@ public class LoginActivity extends AppCompatActivity {
editPassword.setError("Introduza a sua palavra-passe");
editPassword.requestFocus();
} else {
// Se tudo estiver OK, por enquanto vamos apenas simular o login
// e saltar para o Dashboard (que será a nossa próxima fase)
Toast.makeText(this, "Login efetuado com sucesso!", Toast.LENGTH_SHORT).show();
// Aqui é onde irás validar com a Base de Dados ou Firebase no futuro
irParaDashboard();
// Desativa o botão enquanto pensa
btnEntrar.setEnabled(false);
btnEntrar.setText("A VERIFICAR DADOS...");
fazerLoginNoSupabase(email, password);
}
}
private void fazerLoginNoSupabase(String email, String password) {
OkHttpClient client = new OkHttpClient();
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)
.addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
runOnUiThread(() -> {
Toast.makeText(LoginActivity.this, "Erro de ligação à internet!", Toast.LENGTH_SHORT).show();
btnEntrar.setEnabled(true);
btnEntrar.setText("INICIAR SESSÃO");
});
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
final String responseData = response.body().string();
runOnUiThread(() -> {
btnEntrar.setEnabled(true);
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");
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.apply();
Toast.makeText(LoginActivity.this, "Bem-vindo de volta!", Toast.LENGTH_SHORT).show();
irParaDashboard();
} 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
}
});
}
});
}
private void irParaDashboard() {
// Altera 'MainActivity' para o nome da tua classe do Dashboard
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish(); // Fecha o login para o utilizador não voltar atrás ao carregar no botão 'back'
finish();
}
private void inicializarComponentes() {
@@ -65,5 +156,6 @@ public class LoginActivity extends AppCompatActivity {
editPassword = findViewById(R.id.editPassword);
btnEntrar = findViewById(R.id.btnEntrar);
txtRegistrar = findViewById(R.id.txtRegistrar);
txtEsqueciPassword = findViewById(R.id.txtEsqueciPassword);
}
}

View File

@@ -1,59 +1,101 @@
package com.example.finzora;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private Button btnSair;
private TabLayout tabLayout;
private ViewPager2 viewPager;
private FloatingActionButton fabAdicionar;
private TextView tvNomeUsuario;
private Button btnSair;
private TextView tvSaldoGeral, tvReceitasGeral, tvDespesasGeral;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences prefsTema = getSharedPreferences("TemaApp", MODE_PRIVATE);
boolean isModoEscuro = prefsTema.getBoolean("modo_escuro", true);
if (isModoEscuro) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
setContentView(R.layout.activity_main);
// 1. PRIMEIRO PASSO OBRIGATÓRIO: Ligar as variáveis ao XML
inicializarComponentes();
// 2. Agora sim, podemos tentar mudar o nome com segurança
try {
String nomeRecebido = getIntent().getStringExtra("CHAVE_NOME");
// Só altera o texto se realmente tivermos recebido um nome (ex: vindo do Registo)
if (nomeRecebido != null) {
tvNomeUsuario.setText("Olá, " + nomeRecebido + "!");
}
} catch (Exception e) {
e.printStackTrace(); // Se der erro, ignora e mantém o texto original
}
// 3. Configurar os botões e abas
configurarBotaoSair();
configurarAbas();
}
private void inicializarComponentes() {
btnSair = findViewById(R.id.btnSair);
tabLayout = findViewById(R.id.tabLayoutDashboard);
viewPager = findViewById(R.id.viewPager);
tvNomeUsuario = findViewById(R.id.tvNomeUsuario); // Certifica-te que este ID existe no XML
}
fabAdicionar = findViewById(R.id.fabAdicionar);
tvNomeUsuario = findViewById(R.id.tvNomeUsuario);
btnSair = findViewById(R.id.btnSair);
tvSaldoGeral = findViewById(R.id.tvSaldoGeral);
tvReceitasGeral = findViewById(R.id.tvReceitasGeral);
tvDespesasGeral = findViewById(R.id.tvDespesasGeral);
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
String nome = prefs.getString("nome_usuario", "Investidor");
tvNomeUsuario.setText("Olá, " + nome);
private void configurarBotaoSair() {
btnSair.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
prefs.edit().clear().apply();
startActivity(new Intent(this, LoginActivity.class));
finish();
});
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));
});
}
configurarAbas();
atualizarCartoes(); // Chama a nova função ligada à net!
}
@Override
protected void onResume() {
super.onResume();
atualizarCartoes();
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
String nome = prefs.getString("nome_usuario", "Investidor");
if (tvNomeUsuario != null) {
tvNomeUsuario.setText("Olá, " + nome);
}
}
private void configurarAbas() {
@@ -62,16 +104,80 @@ public class MainActivity extends AppCompatActivity {
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
switch (position) {
case 0:
tab.setText("Transações");
break;
case 1:
tab.setText("Gráficos");
break;
case 2:
tab.setText("Dicas");
break;
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;
}
}).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();
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 {
String jsonResposta = response.body().string();
JSONArray jsonArray = new JSONArray(jsonResposta);
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;
}
}
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));
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
}

View File

@@ -0,0 +1,102 @@
package com.example.finzora;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
import java.util.List;
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 TabLayout tabLayoutIndicator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_onboarding);
// --- 1. RECUPERAR O NOME DA MEMÓRIA ---
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
// Tenta ler "nome_usuario". Se não existir, mete "Visitante"
String nomeRecuperado = prefs.getString("nome_usuario", "Visitante");
// --- 2. CRIAR OS SLIDES ---
List<OnboardingItem> lista = new ArrayList<>();
// SLIDE 1: Usamos a variável 'nomeRecuperado' aqui
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
));
// SLIDE 2
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
lista.add(new OnboardingItem(
"Inteligência Artificial",
"Recebe dicas automáticas baseadas nos teus gastos para poupares mais todos os meses.",
R.drawable.ic_idea
));
// --- 3. CONFIGURAR VIEWPAGER E BOTÕES ---
ViewPager2 viewPager = findViewById(R.id.viewPagerOnboarding);
tabLayoutIndicator = findViewById(R.id.tabLayoutIndicator);
btnProximo = findViewById(R.id.btnProximo);
btnSaltar = findViewById(R.id.btnSaltar);
onboardingAdapter = new OnboardingAdapter(lista);
viewPager.setAdapter(onboardingAdapter);
// Ligar as barras de progresso (TabLayout)
new TabLayoutMediator(tabLayoutIndicator, viewPager, (tab, position) -> {
// Deixar vazio (só queremos as barras, sem texto)
}).attach();
// Lógica do Botão Próximo
btnProximo.setOnClickListener(v -> {
if (viewPager.getCurrentItem() + 1 < onboardingAdapter.getItemCount()) {
viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
} else {
finalizarOnboarding();
}
});
// Lógica do Botão Saltar
btnSaltar.setOnClickListener(v -> finalizarOnboarding());
// Mudar texto "Próximo" para "Começar" na última página
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if (position == onboardingAdapter.getItemCount() - 1) {
btnProximo.setText("Começar");
} else {
btnProximo.setText("Próximo");
}
}
});
}
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();
}
}

View File

@@ -0,0 +1,55 @@
package com.example.finzora;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class OnboardingAdapter extends RecyclerView.Adapter<OnboardingAdapter.OnboardingViewHolder> {
private List<OnboardingItem> onboardingItems;
public OnboardingAdapter(List<OnboardingItem> onboardingItems) {
this.onboardingItems = onboardingItems;
}
@NonNull
@Override
public OnboardingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_onboarding, parent, false);
return new OnboardingViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull OnboardingViewHolder holder, int position) {
holder.setOnboardingData(onboardingItems.get(position));
}
@Override
public int getItemCount() {
return onboardingItems.size();
}
class OnboardingViewHolder extends RecyclerView.ViewHolder {
private TextView textTitulo;
private TextView textDescricao;
private ImageView imageOnboarding;
OnboardingViewHolder(@NonNull View itemView) {
super(itemView);
textTitulo = itemView.findViewById(R.id.textTitulo);
textDescricao = itemView.findViewById(R.id.textDescricao);
imageOnboarding = itemView.findViewById(R.id.imgOnboarding);
}
void setOnboardingData(OnboardingItem item) {
textTitulo.setText(item.getTitulo());
textDescricao.setText(item.getDescricao());
imageOnboarding.setImageResource(item.getImagem());
}
}
}

View File

@@ -0,0 +1,16 @@
package com.example.finzora;
public class OnboardingItem {
private String titulo, descricao;
private int imagem;
public OnboardingItem(String titulo, String descricao, int imagem) {
this.titulo = titulo;
this.descricao = descricao;
this.imagem = imagem;
}
public String getTitulo() { return titulo; }
public String getDescricao() { return descricao; }
public int getImagem() { return imagem; }
}

View File

@@ -0,0 +1,78 @@
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.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<OrcamentoAdapter.OrcamentoViewHolder> {
// Lista de pares: Chave=Categoria, Valor=Limite
private List<Map.Entry<String, Float>> listaOrcamentos;
private DBHelper dbHelper;
public OrcamentoAdapter(List<Map.Entry<String, Float>> lista, DBHelper db) {
this.listaOrcamentos = lista;
this.dbHelper = db;
}
@NonNull
@Override
public OrcamentoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_orcamento, parent, false);
return new OrcamentoViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull OrcamentoViewHolder holder, int position) {
Map.Entry<String, Float> entry = listaOrcamentos.get(position);
String categoria = entry.getKey();
float limite = entry.getValue();
// Calcular quanto já gastou
float gasto = dbHelper.getGastoPorCategoria(categoria);
float restante = limite - gasto;
int percentagem = (int) ((gasto / limite) * 100);
if (percentagem > 100) percentagem = 100;
// Atualizar Textos
holder.tvCategoria.setText(categoria);
holder.tvValores.setText(String.format("€ %.2f / € %.2f", gasto, 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
} 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
}
}
@Override
public int getItemCount() {
return listaOrcamentos.size();
}
static class OrcamentoViewHolder extends RecyclerView.ViewHolder {
TextView tvCategoria, tvValores, tvRestante;
ProgressBar progress;
public OrcamentoViewHolder(@NonNull View itemView) {
super(itemView);
tvCategoria = itemView.findViewById(R.id.tvCatOrcamento);
tvValores = itemView.findViewById(R.id.tvValoresOrcamento);
tvRestante = itemView.findViewById(R.id.tvRestante);
progress = itemView.findViewById(R.id.progressOrcamento);
}
}
}

View File

@@ -0,0 +1,98 @@
package com.example.finzora;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class OrcamentoFragment extends Fragment {
private Spinner spinnerCategoria;
private EditText editLimite;
private Button btnSalvar;
private RecyclerView recyclerOrcamentos;
private DBHelper dbHelper;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// Carrega o layout XML correto
View view = inflater.inflate(R.layout.fragment_orcamento, container, false);
// Liga os componentes do Java aos IDs do XML
// SE AQUI FICAR VERMELHO, É PORQUE O XML NÃO FOI GUARDADO
spinnerCategoria = view.findViewById(R.id.spinnerOrcamento);
editLimite = view.findViewById(R.id.editLimite);
btnSalvar = view.findViewById(R.id.btnDefinirOrcamento);
recyclerOrcamentos = view.findViewById(R.id.recyclerOrcamentos);
// Configura a lista
recyclerOrcamentos.setLayoutManager(new LinearLayoutManager(getActivity()));
// Inicia a base de dados
dbHelper = new DBHelper(getActivity());
// Configurações iniciais
configurarSpinner();
// Ação do botão
btnSalvar.setOnClickListener(v -> salvarOrcamento());
// Mostrar dados
carregarOrcamentos();
return view;
}
@Override
public void onResume() {
super.onResume();
carregarOrcamentos();
}
// Método corrigido (havia um duplicado antes)
private void configurarSpinner() {
String[] categorias = {"Alimentação", "Transporte", "Salário", "Lazer", "Contas", "Saúde", "Outros"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), R.layout.item_dropdown, categorias);
spinnerCategoria.setAdapter(adapter);
}
private void salvarOrcamento() {
String limiteStr = editLimite.getText().toString();
if (limiteStr.isEmpty()) {
editLimite.setError("Define um valor");
return;
}
String categoria = spinnerCategoria.getSelectedItem().toString();
float limite = Float.parseFloat(limiteStr);
dbHelper.salvarOrcamento(categoria, limite);
Toast.makeText(getActivity(), "Orçamento definido!", Toast.LENGTH_SHORT).show();
editLimite.setText(""); // Limpar campo
carregarOrcamentos(); // Atualizar lista
}
private void carregarOrcamentos() {
Map<String, Float> orcamentosMap = dbHelper.getOrcamentosDefinidos();
List<Map.Entry<String, Float>> lista = new ArrayList<>(orcamentosMap.entrySet());
OrcamentoAdapter adapter = new OrcamentoAdapter(lista, dbHelper);
recyclerOrcamentos.setAdapter(adapter);
}
}

View File

@@ -0,0 +1,151 @@
package com.example.finzora;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Button;
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.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.textfield.TextInputEditText;
import java.util.Random;
public class ProfileActivity extends AppCompatActivity {
private ImageView imgPerfil;
private TextInputEditText editNomePerfil, editBio;
private Button btnEscolherFoto, btnGuardarPerfil;
private TextView btnSaltarPerfil;
private Uri imagemUriSelecionada = null;
// Variável de controlo
private boolean vindoDoMenu = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
// Verifica de onde viemos
vindoDoMenu = getIntent().getBooleanExtra("origem_menu", false);
inicializarComponentes();
adaptarInterface(); // Ajusta os textos dos botões
carregarDadosAtuais();
// 1. Escolher Foto
ActivityResultLauncher<String> galleryLauncher = registerForActivityResult(
new ActivityResultContracts.GetContent(),
uri -> {
if (uri != null) {
imagemUriSelecionada = uri;
imgPerfil.setImageURI(uri);
imgPerfil.setColorFilter(null);
}
}
);
btnEscolherFoto.setOnClickListener(v -> galleryLauncher.launch("image/*"));
// 2. Guardar
btnGuardarPerfil.setOnClickListener(v -> salvarPerfil());
// 3. Botão Saltar / Cancelar
btnSaltarPerfil.setOnClickListener(v -> {
if (vindoDoMenu) {
// Se veio do menu, "Saltar" funciona como "Cancelar"
finish();
} else {
// Se veio do Tutorial, salta mas mostra as boas-vindas
mostrarMensagemBoasVindas("Investidor");
}
});
}
private void inicializarComponentes() {
imgPerfil = findViewById(R.id.imgPerfil);
editNomePerfil = findViewById(R.id.editNomePerfil);
editBio = findViewById(R.id.editBio);
btnEscolherFoto = findViewById(R.id.btnEscolherFoto);
btnGuardarPerfil = findViewById(R.id.btnGuardarPerfil);
btnSaltarPerfil = findViewById(R.id.btnSaltarPerfil);
}
private void adaptarInterface() {
if (vindoDoMenu) {
// Se já estamos na app
btnSaltarPerfil.setText("Cancelar");
btnGuardarPerfil.setText("Atualizar Perfil");
} else {
// Se é o tutorial inicial
btnSaltarPerfil.setText("Saltar por agora");
btnGuardarPerfil.setText("Guardar e Entrar");
}
}
private void carregarDadosAtuais() {
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
editNomePerfil.setText(prefs.getString("nome_usuario", ""));
editBio.setText(prefs.getString("bio_usuario", ""));
String fotoStr = prefs.getString("foto_usuario", null);
if (fotoStr != null) {
imgPerfil.setImageURI(Uri.parse(fotoStr));
imgPerfil.setColorFilter(null);
}
}
private void salvarPerfil() {
String novoNome = editNomePerfil.getText().toString().trim();
String novaBio = editBio.getText().toString().trim();
if (novoNome.isEmpty()) {
editNomePerfil.setError("O nome não pode estar vazio");
return;
}
SharedPreferences.Editor editor = getSharedPreferences("DadosUtilizador", MODE_PRIVATE).edit();
editor.putString("nome_usuario", novoNome);
editor.putString("bio_usuario", novaBio);
if (imagemUriSelecionada != null) {
editor.putString("foto_usuario", imagemUriSelecionada.toString());
}
editor.apply();
// --- LÓGICA DE SAÍDA ---
if (vindoDoMenu) {
// Edição simples: avisa e volta para trás
Toast.makeText(this, "Perfil atualizado!", Toast.LENGTH_SHORT).show();
finish();
} else {
// Primeira vez: Mostra a mensagem inspiradora
mostrarMensagemBoasVindas(novoNome);
}
}
private void mostrarMensagemBoasVindas(String nome) {
String[] frases = {
"\"Não poupes o que resta depois de gastar, mas gasta o que resta depois de poupar.\" Warren Buffett",
"\"O segredo para ficar à frente é começar.\" Mark Twain",
"\"A tua liberdade financeira começa hoje!\" 🚀"
};
String fraseDoDia = frases[new Random().nextInt(frases.length)];
new AlertDialog.Builder(this)
.setTitle("Bem-vindo(a), " + nome + "!")
.setMessage(fraseDoDia + "\n\nO teu perfil está pronto. Vamos dominar as tuas finanças?")
.setPositiveButton("VAMOS LÁ!", (dialog, which) -> {
Intent intent = new Intent(ProfileActivity.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
})
.setCancelable(false)
.show();
}
}

View File

@@ -0,0 +1,90 @@
package com.example.finzora;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.ImageView;
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 RecuperarPasswordActivity extends AppCompatActivity {
private TextInputEditText editEmailRecuperar;
private Button btnEnviarEmail;
private ImageView btnVoltar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recuperar_password);
editEmailRecuperar = findViewById(R.id.editEmailRecuperar);
btnEnviarEmail = findViewById(R.id.btnEnviarEmail);
btnVoltar = findViewById(R.id.btnVoltarRecuperar);
// Voltar para trás
btnVoltar.setOnClickListener(v -> finish());
btnEnviarEmail.setOnClickListener(v -> {
String email = editEmailRecuperar.getText().toString().trim();
if (TextUtils.isEmpty(email)) {
editEmailRecuperar.setError("Introduz o teu email");
editEmailRecuperar.requestFocus();
} else {
btnEnviarEmail.setEnabled(false);
btnEnviarEmail.setText("A ENVIAR...");
pedirRecuperacaoPassword(email);
}
});
}
private void pedirRecuperacaoPassword(String email) {
OkHttpClient client = new OkHttpClient();
String json = "{\"email\":\"" + email + "\"}";
RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
Request request = new Request.Builder()
.url(SupabaseConfig.SUPABASE_URL + "/auth/v1/recover")
.addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
.addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
runOnUiThread(() -> {
Toast.makeText(RecuperarPasswordActivity.this, "Erro de ligação à internet!", Toast.LENGTH_SHORT).show();
btnEnviarEmail.setEnabled(true);
btnEnviarEmail.setText("ENVIAR LINK MÁGICO");
});
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
runOnUiThread(() -> {
btnEnviarEmail.setEnabled(true);
btnEnviarEmail.setText("ENVIAR LINK MÁGICO");
if (response.isSuccessful()) {
Toast.makeText(RecuperarPasswordActivity.this, "Email enviado! Verifica a caixa de entrada.", Toast.LENGTH_LONG).show();
finish(); // Fecha esta tela e volta ao Login
} else {
Toast.makeText(RecuperarPasswordActivity.this, "Erro: Verifica se o email está correto ou se a conta existe.", Toast.LENGTH_LONG).show();
}
});
}
});
}
}

View File

@@ -1,17 +1,30 @@
package com.example.finzora;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.textfield.TextInputEditText;
import org.json.JSONObject;
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 RegisterActivity extends AppCompatActivity {
private TextInputEditText editNome, editEmail, editPassword, editConfirmPassword;
private TextInputEditText editNome, editEmailRegister, editPassRegister, editConfirmPass;
private Button btnCriarConta;
private TextView txtLogin;
@@ -22,44 +35,122 @@ public class RegisterActivity extends AppCompatActivity {
inicializarComponentes();
// Volta para a tela de Login
// Voltar para o Login
txtLogin.setOnClickListener(v -> finish());
// Valida e cria a conta
// Clicar em Criar Conta
btnCriarConta.setOnClickListener(v -> validarDados());
}
private void validarDados() {
String nome = editNome.getText().toString().trim();
String email = editEmail.getText().toString().trim();
String password = editPassword.getText().toString().trim();
String confirmPass = editConfirmPassword.getText().toString().trim();
String email = editEmailRegister.getText().toString().trim();
String password = editPassRegister.getText().toString().trim();
String confirmPass = editConfirmPass.getText().toString().trim();
if (TextUtils.isEmpty(nome)) {
editNome.setError("Introduza o seu nome");
} else if (TextUtils.isEmpty(email)) {
editEmail.setError("Introduza um email válido");
} else if (TextUtils.isEmpty(password)) {
editPassword.setError("Defina uma palavra-passe");
} else if (password.length() < 6) {
editPassword.setError("A senha deve ter pelo menos 6 caracteres");
} else if (!password.equals(confirmPass)) {
editConfirmPassword.setError("As palavras-passe não coincidem");
} else {
Toast.makeText(this, "Conta criada com sucesso!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(RegisterActivity.this, MainActivity.class);
intent.putExtra("CHAVE_NOME", nome);
startActivity(intent);
finish();
return;
}
if (TextUtils.isEmpty(email)) {
editEmailRegister.setError("Introduza um email válido");
return;
}
if (TextUtils.isEmpty(password)) {
editPassRegister.setError("Defina uma palavra-passe");
return;
}
if (password.length() < 6) {
editPassRegister.setError("Mínimo 6 caracteres");
return;
}
if (!password.equals(confirmPass)) {
editConfirmPass.setError("As senhas não coincidem");
return;
}
btnCriarConta.setEnabled(false);
btnCriarConta.setText("A criar conta na nuvem...");
registarNoSupabase(nome, email, password);
}
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"));
Request request = new Request.Builder()
.url(SupabaseConfig.SUPABASE_URL + "/auth/v1/signup")
.addHeader("apikey", SupabaseConfig.SUPABASE_KEY)
.addHeader("Authorization", "Bearer " + SupabaseConfig.SUPABASE_KEY)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@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();
resetBotao();
});
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
final String responseData = response.body().string();
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
SharedPreferences prefs = getSharedPreferences("DadosUtilizador", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("nome_usuario", nome);
editor.putString("user_id", userId);
editor.apply();
Toast.makeText(RegisterActivity.this, "Sucesso! Bem-vindo à nuvem.", Toast.LENGTH_SHORT).show();
// Avançar para o Onboarding
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();
}
} 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();
} catch (Exception e) {
Toast.makeText(RegisterActivity.this, "Erro desconhecido: " + responseData, Toast.LENGTH_LONG).show();
}
resetBotao();
}
});
}
});
}
private void resetBotao() {
btnCriarConta.setEnabled(true);
btnCriarConta.setText("CRIAR CONTA");
}
private void inicializarComponentes() {
editNome = findViewById(R.id.editNome);
editEmail = findViewById(R.id.editEmailRegister);
editPassword = findViewById(R.id.editPassRegister);
editConfirmPassword = findViewById(R.id.editConfirmPass);
editEmailRegister = findViewById(R.id.editEmailRegister);
editPassRegister = findViewById(R.id.editPassRegister);
editConfirmPass = findViewById(R.id.editConfirmPass);
btnCriarConta = findViewById(R.id.btnCriarConta);
txtLogin = findViewById(R.id.txtLogin);
}

View File

@@ -0,0 +1,8 @@
package com.example.finzora;
public class SupabaseConfig {
public static final String SUPABASE_URL = "https://cnxbsherdagpdpjhbtae.supabase.co";
public static final String SUPABASE_KEY = "sb_secret_bJg45XiAMz-KTyXw2FmMMA_HF97o_aV";
}

View File

@@ -0,0 +1,38 @@
package com.example.finzora;
public class Transacao {
private int id;
private float valor;
private String categoria;
private int tipo; // 1 = Receita, 2 = Despesa
private String data;
// --- CONSTRUTOR 1: Para ler da Base de Dados (TEM ID) ---
public Transacao(int id, float valor, String categoria, int tipo, String data) {
this.id = id;
this.valor = valor;
this.categoria = categoria;
this.tipo = tipo;
this.data = data;
}
// --- CONSTRUTOR 2: Para criar nova transação (NÃO TEM ID) ---
public Transacao(float valor, String categoria, int tipo, String data) {
this.valor = valor;
this.categoria = categoria;
this.tipo = tipo;
this.data = data;
}
// --- GETTERS (Para os outros lerem) ---
public int getId() { return id; }
public float getValor() { return valor; }
public String getCategoria() { return categoria; }
public int getTipo() { return tipo; }
public String getData() { return data; }
// --- SETTERS (Opcional, mas evita erros se algum código antigo os chamar) ---
public void setId(int id) { this.id = id; }
public void setValor(float valor) { this.valor = valor; }
public void setCategoria(String categoria) { this.categoria = categoria; }
}

View File

@@ -0,0 +1,79 @@
package com.example.finzora;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class TransacoesAdapter extends RecyclerView.Adapter<TransacoesAdapter.MyViewHolder> {
private List<Transacao> listaTransacoes;
private Context context;
private TransacoesFragment fragment;
// O teu construtor mantém-se igual!
public TransacoesAdapter(List<Transacao> lista, Context context, TransacoesFragment fragment) {
this.listaTransacoes = lista;
this.context = context;
this.fragment = fragment;
}
@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);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Transacao transacao = listaTransacoes.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);
}
// O botão de apagar agora chama-se btnEliminar no XML
holder.btnEliminar.setOnClickListener(v -> {
if (fragment != null) fragment.confirmarExclusao(transacao);
});
}
@Override
public int getItemCount() {
return listaTransacoes.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
// --- AQUI ESTÃO OS NOVOS NOMES DO XML ---
TextView tvDescricao, tvValor, tvData;
ImageView imgIcone, btnEliminar;
public MyViewHolder(@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);
}
}
}

View File

@@ -1,6 +1,158 @@
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.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 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.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class TransacoesFragment extends Fragment {
private RecyclerView recyclerTransacoes;
private TransacoesAdapter adapter;
@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()));
return view;
}
@Override
public void onResume() {
super.onResume();
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
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")
.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) {
if (getActivity() != null) {
getActivity().runOnUiThread(() ->
Toast.makeText(getActivity(), "Erro de internet ao carregar transações.", 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<Transacao> listaNuvem = new ArrayList<>();
try {
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");
Transacao t = new Transacao(valor, categoria, tipo, data);
t.setId(id); // Guarda o ID verdadeiro da nuvem para podermos apagar depois!
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);
recyclerTransacoes.setAdapter(adapter);
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
// ====================================================================
// APAGAR TRANSAÇÃO NA NUVEM
// ====================================================================
public void confirmarExclusao(Transacao transacao) {
new AlertDialog.Builder(getActivity())
.setTitle("Eliminar Transação")
.setMessage("Apagar " + transacao.getCategoria() + "?")
.setPositiveButton("Sim", (dialog, which) -> {
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();
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 das nuvens!", Toast.LENGTH_SHORT).show();
carregarDadosDoSupabase(); // Recarrega a lista
// Atualiza os cartões na MainActivity
if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).atualizarCartoes();
}
});
}
}
});
})
.setNegativeButton("Não", null)
.show();
}
}

View File

@@ -14,17 +14,17 @@ public class ViewPagerAdapter extends FragmentStateAdapter {
@NonNull
@Override
public Fragment createFragment(int position) {
// Aqui definimos a ordem das abas
switch (position) {
case 0: return new TransacoesFragment(); // Aba 1
case 1: return new GraficosFragment(); // Aba 2
case 2: return new DicasFragment(); // Aba 3
case 0: return new TransacoesFragment();
case 1: return new OrcamentoFragment();
case 2: return new GraficosFragment();
case 3: return new DicasFragment();
default: return new TransacoesFragment();
}
}
@Override
public int getItemCount() {
return 3; // Temos 3 abas no total
return 4; // Total de 4 abas
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#1AFFFFFF"/>
<corners android:radius="16dp"/>
<stroke android:width="1dp" android:color="#30FFFFFF"/>
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#2C5364"/>
<stroke android:width="1dp" android:color="@color/tech_accent_cyan"/>
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#E8F5E9" />
<size android:width="48dp" android:height="48dp"/>
</shape>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#0F2027"
android:centerColor="#203A43"
android:endColor="#2C5364"
android:angle="45"
android:type="linear"/>
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
</vector>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#673AB7"
android:pathData="M21,7.28V5c0,-1.1 -0.9,-2 -2,-2H5C3.89,3 3,3.9 3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-2.28A2,2 0 0,0 22,15V9C22,8.14 21.46,7.5 21,7.28zM5,19V5h14v2h-6c-1.1,0 -2,0.9 -2,2v6c0,1.1 0.9,2 2,2h6v2H5z" />
<path
android:fillColor="#4527A0"
android:pathData="M20,9v6h-7V9H20z" />
<path
android:fillColor="#00E676"
android:pathData="M16.5,10.5 m-1.5,0 a1.5,1.5 0,1 1,3 0 a1.5,1.5 0,1 1,-3 0" />
<path
android:strokeColor="#00E676"
android:strokeWidth="0.8"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="M5,8 h3 v3 M5,16 h4" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FF1744">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zm4,0h-2v-3h2v3zm4,0h-2v-5h2v5z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFD600">
<path
android:fillColor="@android:color/white"
android:pathData="M9,21c0,0.55 0.45,1 1,1h4c0.55,0 1,-0.45 1,-1v-1H9v1zm3,-19C8.14,2 5,5.14 5,9c0,2.38 1.19,4.47 3,5.74V17c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1v-2.26c1.81,-1.27 3,-3.36 3,-5.74 0,-3.86 -3.14,-7 -7,-7zm2.85,11.1l-0.85,0.6V16h-4v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9c0,-2.76 2.24,-5 5,-5s5,2.24 5,5c0,1.63 -0.8,3.16 -2.15,4.1z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#00E676">
<path
android:fillColor="@android:color/white"
android:pathData="M3,13h2.28l1.45,-4.79l2.77,8.83L12,5l3.5,12.04L17.28,13H21v-2h-4.72l-1.45,4.79l-2.77,-8.83L9.5,19L6,6.96L4.22,11H3v2z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"> <path
android:fillColor="@android:color/white"
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.33 -0.02,-0.64 -0.06,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.13,5.91 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.82,11.67 4.82,12c0,0.33 0.02,0.64 0.06,0.94L2.86,14.52c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5s3.5,1.57 3.5,3.5S13.93,15.5 12,15.5z"/>
</vector>

View File

@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="/Users/230402/wallet-glyph-style-blue-colour_78370-7159.avif" />
</selector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/tech_accent_green"
android:pathData="M21,18v1c0,1.1 -0.9,2 -2,2H5c-1.11,0 -2,-0.9 -2,-2V5c0,-1.1 0.89,-2 2,-2h14c1.1,0 2,0.9 2,2v1h-9c-1.11,0 -2,0.9 -2,2v8c0,1.1 0.89,2 2,2h9zm-9,-2h10V8H12v8zm4,-2.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
</vector>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="10dp"/>
<solid android:color="#263238"/>
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="10dp"/>
<solid android:color="@color/tech_accent_cyan"/>
</shape>
</clip>
</item>
</layer-list>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dp"/>
<solid android:color="#37474F"/>
</shape>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape>
<corners android:radius="5dp"/>
<solid android:color="#00E676"/> </shape>
</scale>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/bg_tech_gradient"
android:padding="24dp">
<ImageView
android:id="@+id/btnVoltar"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@android:drawable/ic_menu_close_clear_cancel"
app:tint="@color/white"
android:layout_marginBottom="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nova Transação"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="@color/white"
android:layout_marginBottom="32dp"/>
<EditText
android:id="@+id/editValor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Valor (€)"
android:textColorHint="#B0BEC5"
android:textColor="@color/white"
android:inputType="numberDecimal"
android:backgroundTint="@color/tech_accent_cyan"
android:textSize="20sp"
android:layout_marginBottom="24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Categoria"
android:textColor="#B0BEC5"
android:layout_marginBottom="8dp"/>
<Spinner
android:id="@+id/spinnerCategoria"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/bg_card_tech"
android:layout_marginBottom="40dp"/> <Button
android:id="@+id/btnGuardar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Guardar Transação"
android:textColor="@color/black"
android:padding="12dp"
android:textStyle="bold"
app:backgroundTint="@color/tech_accent_cyan"/>
</LinearLayout>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
android:background="@color/fundo_app"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="32dp">
<TextView
android:id="@+id/btnVoltarDefinicoes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="←"
android:textColor="@color/texto_principal"
android:textSize="32sp"
android:textStyle="bold"
android:paddingEnd="16dp"
android:background="?attr/selectableItemBackgroundBorderless"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Definições"
android:textColor="@color/texto_principal"
android:textSize="28sp"
android:textStyle="bold"/>
</LinearLayout>
<TextView
android:id="@+id/btnEditarPerfil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Editar Perfil"
android:textColor="@color/texto_principal"
android:textSize="18sp"
android:paddingVertical="16dp"
android:background="?attr/selectableItemBackground"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/linha_separadora" />
<Switch
android:id="@+id/switchModoEscuro"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Modo Escuro"
android:textColor="@color/texto_principal"
android:textSize="18sp"
android:paddingVertical="16dp" />
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/linha_separadora" />
<Switch
android:id="@+id/switchNotificacoes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ativar Notificações"
android:textColor="@color/texto_principal"
android:textSize="18sp"
android:paddingVertical="16dp" />
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/linha_separadora" />
<TextView
android:id="@+id/btnSuporte"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Centro de Suporte"
android:textColor="@color/texto_principal"
android:textSize="18sp"
android:paddingVertical="16dp"
android:background="?attr/selectableItemBackground"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/linha_separadora" android:layout_marginBottom="32dp" />
<Button
android:id="@+id/btnTerminarSessao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Terminar Sessão"
android:textColor="#FFFFFF"
android:textStyle="bold"
android:padding="16dp"
app:cornerRadius="8dp"
android:backgroundTint="#FF1744" /> </LinearLayout>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
android:background="#1A202C"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="40dp">
<TextView
android:id="@+id/btnVoltarEditarPerfil"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="←"
android:textColor="#FFFFFF"
android:textSize="32sp"
android:textStyle="bold"
android:paddingEnd="16dp"
android:background="?attr/selectableItemBackgroundBorderless"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Editar Perfil"
android:textColor="#FFFFFF"
android:textSize="28sp"
android:textStyle="bold"/>
</LinearLayout>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:background="@drawable/bg_circle_icon"
android:src="@android:drawable/ic_menu_camera"
app:tint="#FFFFFF"
android:padding="24dp"
android:layout_marginBottom="32dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nome"
android:textColor="#B0BEC5"
android:textSize="14sp"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/editNomePerfil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#2D3748"
android:textColor="#FFFFFF"
android:padding="16dp"
android:hint="O teu nome"
android:textColorHint="#718096"
android:layout_marginBottom="24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Email"
android:textColor="#B0BEC5"
android:textSize="14sp"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/editEmailPerfil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#2D3748"
android:textColor="#FFFFFF"
android:padding="16dp"
android:hint="O teu email"
android:textColorHint="#718096"
android:inputType="textEmailAddress"
android:layout_marginBottom="40dp"/>
<Button
android:id="@+id/btnGuardarPerfil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Guardar Alterações"
android:textColor="#1A202C"
android:textStyle="bold"
android:padding="16dp"
app:cornerRadius="8dp"
android:backgroundTint="#00E676" /> </LinearLayout>

View File

@@ -1,95 +1,145 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F7FA"
android:background="@drawable/bg_tech_gradient"
android:padding="24dp">
<com.google.android.material.card.MaterialCardView
<LinearLayout
android:id="@+id/headerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
app:cardCornerRadius="16dp"
app:cardElevation="8dp">
android:orientation="vertical"
android:gravity="center"
android:layout_marginTop="60dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.cardview.widget.CardView
android:layout_width="100dp"
android:layout_height="100dp"
app:cardCornerRadius="20dp"
app:cardBackgroundColor="#1AFFFFFF"
app:cardElevation="0dp">
<ImageView
android:id="@+id/imgLogoLogin"
android:layout_width="100dp" android:layout_height="100dp"
android:src="@drawable/ic_carteira_tech"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="24dp"
android:background="@android:color/transparent"/>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FINZORA"
android:textSize="32sp"
android:textStyle="bold"
android:textColor="@color/white"
android:letterSpacing="0.1"
android:layout_marginTop="24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Acesso ao Sistema"
android:textSize="16sp"
android:textColor="#B0BEC5"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center"
android:src="@drawable/imagem_convertida"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Finzora"
android:textSize="28sp"
android:textStyle="bold"
android:textColor="#333333"
android:layout_marginTop="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Entre na sua conta para gerir as suas despesas"
android:textColor="#666666"
android:layout_marginBottom="24dp"/>
android:layout_marginTop="48dp"
app:layout_constraintTop_toBottomOf="@id/headerLayout">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
app:boxCornerRadiusBottomEnd="8dp"
app:boxCornerRadiusTopStart="8dp">
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_cyan"
app:boxStrokeWidth="1dp"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_cyan">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress" />
android:hint="Email"
android:inputType="textEmailAddress"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="Palavra-passe"
app:passwordToggleEnabled="true">
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_marginTop="16dp"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_cyan"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_cyan"
app:endIconMode="password_toggle"
app:endIconTint="#B0BEC5">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
android:hint="Password"
android:inputType="textPassword"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/txtEsqueciPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="12dp"
android:text="Esqueci-me da palavra-passe"
android:textColor="@color/tech_accent_cyan"
android:textSize="14sp"
android:textStyle="bold"
android:background="?attr/selectableItemBackground"
android:padding="4dp"/>
<Button
android:id="@+id/btnEntrar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_height="60dp"
android:layout_marginTop="24dp"
android:text="Entrar"
android:backgroundTint="#4A47E0"
app:cornerRadius="8dp" />
android:text="INICIAR SESSÃO"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/black"
app:backgroundTint="@color/tech_accent_cyan"
app:cornerRadius="12dp"/>
<TextView
android:id="@+id/txtRegistrar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:text="Não tem conta? Registar agora"
android:textColor="#4A47E0" />
android:layout_marginTop="24dp"
android:text="Não tens conta? Criar agora"
android:textColor="@color/white"
android:textSize="14sp"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,9 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F7FA"
android:background="@color/fundo_app">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
@@ -11,76 +16,68 @@
android:layout_height="wrap_content"
android:padding="20dp">
<com.google.android.material.card.MaterialCardView
android:id="@+id/avatarCard"
android:layout_width="48dp"
android:layout_height="48dp"
app:cardBackgroundColor="#4A47E0"
app:cardCornerRadius="24dp"
app:cardElevation="0dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="J"
android:textColor="#FFFFFF"
android:textSize="20sp"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
<ImageView
android:id="@+id/imgLogo"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:background="@drawable/bg_circle_icon"
android:padding="8dp"
android:src="@android:drawable/ic_menu_gallery"
app:tint="@color/white" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_toEndOf="@id/avatarCard"
android:orientation="vertical">
android:orientation="vertical"
android:layout_toEndOf="@id/imgLogo"
android:layout_centerVertical="true"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Finzora"
android:textColor="#333333"
android:text="FINZORA"
android:textColor="@color/tech_accent_cyan"
android:textSize="18sp"
android:textStyle="bold" />
android:textStyle="bold"/>
<TextView
android:id="@+id/tvNomeUsuario"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Olá, Jorge!"
android:textColor="#666666"/>
android:text="Olá, Investidor"
android:textColor="@color/texto_principal"
android:textSize="14sp"/>
</LinearLayout>
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/btnSair"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:text="Sair"
android:textAllCaps="false"
app:cornerRadius="8dp"
app:strokeColor="#DDDDDD" />
android:textColor="#FFFFFF"
android:backgroundTint="#FF1744"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
app:cornerRadius="8dp"/>
</RelativeLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingEnd="16dp">
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:clipToPadding="false">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<include layout="@layout/card_saldo_total" />
<include layout="@layout/card_receitas" />
<include layout="@layout/card_despesas" />
</LinearLayout>
</HorizontalScrollView>
@@ -88,17 +85,41 @@
android:id="@+id/tabLayoutDashboard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"
app:tabIndicatorColor="#4A47E0"
app:tabSelectedTextColor="#4A47E0"
android:contentDescription="Menu de navegação principal" />
android:layout_marginTop="16dp"
android:background="@android:color/transparent"
app:tabTextColor="@color/texto_principal"
app:tabSelectedTextColor="@color/tech_accent_cyan"
app:tabIndicatorColor="@color/tech_accent_cyan"
app:tabMode="scrollable"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAdicionar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="24dp"
android:src="@android:drawable/ic_input_add"
app:backgroundTint="@color/tech_accent_cyan"
app:tint="@color/black"
app:elevation="6dp"/>
<ImageView
android:id="@+id/btnAbrirDefinicoes"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_gravity="bottom|start"
android:layout_margin="24dp"
android:padding="12dp"
android:src="@drawable/ic_settings_pap"
app:tint="@color/texto_principal"
android:elevation="6dp"
android:background="?attr/selectableItemBackgroundBorderless"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="HardcodedText"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_tech_gradient">
<com.google.android.material.tabs.TabLayout
android:contentDescription="Indicadores de página"
android:id="@+id/tabLayoutIndicator"
android:layout_width="200dp"
android:layout_height="10dp"
android:layout_marginTop="48dp"
android:background="@drawable/bg_card_tech"
app:tabIndicatorColor="@color/tech_accent_cyan"
app:tabIndicatorHeight="4dp"
app:tabIndicatorFullWidth="false"
app:tabGravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPagerOnboarding"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@+id/tabLayoutIndicator"
app:layout_constraintBottom_toTopOf="@+id/layoutBotoes" />
<LinearLayout
android:id="@+id/layoutBotoes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="32dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/btnSaltar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Saltar"
android:textSize="16sp"
android:textColor="#90A4AE"
android:background="?attr/selectableItemBackground"
android:padding="8dp"/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:id="@+id/btnProximo"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="Próximo"
android:textColor="@color/black"
android:textStyle="bold"
app:backgroundTint="@color/tech_accent_cyan"
app:cornerRadius="25dp"
android:paddingHorizontal="32dp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_tech_gradient"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp"
android:gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="O Teu Perfil"
android:textSize="28sp"
android:textStyle="bold"
android:textColor="@color/white"
android:layout_marginTop="40dp"
android:layout_marginBottom="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Personaliza a tua identidade no Finzora"
android:textColor="#B0BEC5"
android:textSize="14sp"
android:layout_marginBottom="40dp"/>
<androidx.cardview.widget.CardView
android:layout_width="120dp"
android:layout_height="120dp"
app:cardCornerRadius="60dp"
app:cardBackgroundColor="#20FFFFFF"
app:cardElevation="0dp">
<ImageView
android:id="@+id/imgPerfil"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher_foreground"
android:scaleType="centerCrop"
app:tint="@color/tech_accent_cyan"/>
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/btnEscolherFoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Escolher Foto"
android:layout_marginTop="16dp"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:textColor="@color/tech_accent_cyan"/>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_cyan"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_cyan">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editNomePerfil"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Nome Completo"
android:inputType="textPersonName"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_cyan"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_cyan">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editBio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Sobre Mim (Opcional)"
android:inputType="textMultiLine"
android:lines="3"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:id="@+id/btnGuardarPerfil"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Guardar Perfil"
android:textStyle="bold"
android:textColor="@color/black"
app:backgroundTint="@color/tech_accent_cyan"
app:cornerRadius="12dp"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/btnSaltarPerfil"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Saltar por agora"
android:textColor="#B0BEC5"
android:textSize="14sp"
android:padding="8dp"/>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_tech_gradient"
android:padding="24dp">
<ImageView
android:id="@+id/btnVoltarRecuperar"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@android:drawable/ic_menu_close_clear_cancel"
app:tint="@color/white"
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tvTituloRecuperar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Recuperar Palavra-passe"
android:textSize="28sp"
android:textStyle="bold"
android:textColor="@color/white"
android:layout_marginTop="40dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnVoltarRecuperar"/>
<TextView
android:id="@+id/tvSubtituloRecuperar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Insere o teu email registado. Vamos enviar-te um link mágico para redefinires a tua palavra-passe."
android:textSize="16sp"
android:textColor="#B0BEC5"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/tvTituloRecuperar"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="40dp"
app:layout_constraintTop_toBottomOf="@id/tvSubtituloRecuperar">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_cyan"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_cyan">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editEmailRecuperar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="O teu Email"
android:inputType="textEmailAddress"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btnEnviarEmail"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="32dp"
android:text="ENVIAR LINK MÁGICO"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/black"
app:backgroundTint="@color/tech_accent_cyan"
app:cornerRadius="12dp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,131 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F7FA"
android:background="@drawable/bg_tech_gradient"
android:fillViewport="true">
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="24dp">
<com.google.android.material.card.MaterialCardView
android:layout_width="339dp"
android:layout_height="603dp"
android:layout_centerInParent="true"
app:cardCornerRadius="16dp"
app:cardElevation="8dp">
<TextView
android:id="@+id/tvTituloRegisto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nova Conta"
android:textSize="32sp"
android:textStyle="bold"
android:textColor="@color/white"
android:layout_marginTop="40dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tvSubtitulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Junta-te ao futuro das finanças."
android:textSize="16sp"
android:textColor="#B0BEC5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvTituloRegisto"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="634dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center"
android:src="@drawable/imagem_convertida" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:text="Criar Conta"
android:textColor="#333333"
android:textSize="28sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="24dp"
android:text="Junte-se ao Finzora e comece a gerir as suas finanças"
android:textAlignment="center"
android:textColor="#666666" />
android:layout_marginTop="40dp"
app:layout_constraintTop_toBottomOf="@id/tvSubtitulo">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Nome"
app:boxCornerRadiusBottomEnd="8dp"
app:boxCornerRadiusTopStart="8dp">
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_green"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_green">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editNome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName" />
android:hint="Nome Completo"
android:inputType="textPersonName"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="Email">
android:layout_marginTop="16dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_green"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_green">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editEmailRegister"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress" />
android:hint="Email"
android:inputType="textEmailAddress"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="Palavra-passe"
app:passwordToggleEnabled="true">
android:layout_marginTop="16dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_green"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_green"
app:endIconMode="password_toggle"
app:endIconTint="#B0BEC5">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editPassRegister"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
android:hint="Password"
android:inputType="textPassword"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="Confirmar Palavra-passe"
app:passwordToggleEnabled="true">
android:layout_marginTop="16dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:textColorHint="#90A4AE"
app:boxStrokeColor="@color/tech_accent_green"
app:boxBackgroundColor="#10FFFFFF"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomStart="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:hintTextColor="@color/tech_accent_green"
app:endIconMode="password_toggle"
app:endIconTint="#B0BEC5">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editConfirmPass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
android:hint="Confirmar Password"
android:inputType="textPassword"
android:textColor="@color/white"/>
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btnCriarConta"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginTop="24dp"
android:backgroundTint="#4A47E0"
android:text="Criar Conta"
app:cornerRadius="8dp" />
android:layout_height="60dp"
android:layout_marginTop="32dp"
android:text="CRIAR CONTA"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/black"
app:backgroundTint="@color/tech_accent_green"
app:cornerRadius="12dp"/>
<TextView
android:id="@+id/txtLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:text="Já tem conta? Entrar"
android:textColor="#4A47E0" />
android:layout_marginTop="24dp"
android:text="Já tens conta? Entrar"
android:textColor="@color/white"
android:textSize="14sp"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@@ -1,41 +1,49 @@
<com.google.android.material.card.MaterialCardView
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="160dp"
android:layout_height="100dp"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="4dp">
app:cardElevation="4dp"
app:cardBackgroundColor="@color/fundo_cartao">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:padding="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Despesas"
android:textColor="#666666" />
android:textColor="@color/texto_principal"
android:textSize="14sp"
android:textStyle="bold" />
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:src="@drawable/redarrow" />
android:src="@android:drawable/arrow_down_float"
app:tint="@color/tech_accent_red" />
</RelativeLayout>
<TextView
android:id="@+id/tvDespesasValor"
android:id="@+id/tvDespesasGeral"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="€150.50"
android:textColor="#D32F2F"
android:textSize="22sp"
android:layout_marginTop="12dp"
android:text="€ 0.00"
android:textColor="@color/tech_accent_red"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.cardview.widget.CardView>

View File

@@ -1,41 +1,49 @@
<com.google.android.material.card.MaterialCardView
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="160dp"
android:layout_height="100dp"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="4dp">
app:cardElevation="4dp"
app:cardBackgroundColor="@color/fundo_cartao">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:padding="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Receitas"
android:textColor="#666666" />
android:textColor="@color/texto_principal"
android:textSize="14sp"
android:textStyle="bold" />
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:src="@drawable/greenarrow" />
android:src="@android:drawable/arrow_up_float"
app:tint="@color/tech_accent_green" />
</RelativeLayout>
<TextView
android:id="@+id/tvReceitasValor"
android:id="@+id/tvReceitasGeral"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="€1500.00"
android:textColor="#2E7D32"
android:textSize="22sp"
android:layout_marginTop="12dp"
android:text="€ 0.00"
android:textColor="@color/tech_accent_green"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.cardview.widget.CardView>

View File

@@ -1,32 +1,45 @@
<com.google.android.material.card.MaterialCardView
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="160dp"
android:layout_height="100dp"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="4dp">
<LinearLayout
app:cardElevation="4dp"
app:cardBackgroundColor="@color/fundo_cartao"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:padding="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Saldo Total"
android:textColor="#666666" />
android:textColor="@color/texto_principal"
android:textSize="14sp"
android:textStyle="bold" /> <ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:src="@android:drawable/ic_menu_save"
app:tint="@color/tech_accent_cyan" />
</RelativeLayout>
<TextView
android:id="@+id/tvSaldoValor"
android:id="@+id/tvSaldoGeral"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="€1349.50"
android:textColor="#2E7D32"
android:textSize="22sp"
android:layout_marginTop="12dp"
android:text="€ 0.00"
android:textColor="@color/tech_accent_green"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:padding="16dp">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="20dp"
app:cardBackgroundColor="#FFFFFF"
app:cardElevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="24dp">
<TextView android:id="@+id/btnVoltarContactar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="← Voltar" android:textColor="#2D3748" android:textStyle="bold" android:background="?attr/selectableItemBackground" android:paddingEnd="16dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Contactar Suporte" android:textSize="18sp" android:textStyle="bold" android:textColor="#1A202C"/>
</LinearLayout>
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="O seu email" android:background="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp"/>
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Assunto" android:background="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp"/>
<EditText android:layout_width="match_parent" android:layout_height="120dp" android:hint="Descreva o seu problema..." android:background="#F7FAFC" android:padding="16dp" android:gravity="top|start" android:layout_marginBottom="24dp"/>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="end">
<Button android:id="@+id/btnCancelarContacto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Cancelar" android:backgroundTint="#FFFFFF" android:textColor="#2D3748" android:layout_marginEnd="8dp"/>
<Button android:id="@+id/btnEnviarMensagem" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Enviar Mensagem" android:backgroundTint="#1A202C" android:textColor="#FFFFFF"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:padding="16dp">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="500dp"
app:cardCornerRadius="20dp"
app:cardBackgroundColor="#FFFFFF"
app:cardElevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="8dp">
<TextView android:id="@+id/btnVoltarFAQ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="← Voltar" android:textColor="#2D3748" android:textStyle="bold" android:background="?attr/selectableItemBackground" android:paddingEnd="16dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Perguntas Frequentes" android:textSize="18sp" android:textStyle="bold" android:textColor="#1A202C"/>
</LinearLayout>
<ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="8dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="16dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Como adiciono uma transação?" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Vá ao separador 'Adicionar', selecione o tipo (Receita ou Despesa), preencha os campos e clique em 'Adicionar Transação'." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Como posso definir um orçamento?" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="No separador 'Orçamentos', escolha a categoria, defina o limite de gastos e clique em 'Definir Orçamento'." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Como visualizo os meus gastos por categoria?" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Aceda ao separador 'Gráficos' para ver representações visuais dos seus gastos organizados por categoria e período." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="O que são as dicas financeiras?" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="No separador 'Dicas', encontrará recomendações personalizadas baseadas nos seus padrões de gastos para melhorar a sua saúde financeira." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Como edito o meu perfil?" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Clique no ícone de definições ao lado do nome 'Finzora' ou na sua foto de perfil para aceder às configurações." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Os meus dados estão seguros?" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Sim! Todos os seus dados são armazenados localmente no seu dispositivo e não são partilhados com terceiros." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/transparent"
android:padding="16dp">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="20dp"
app:cardBackgroundColor="#FFFFFF"
app:cardElevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Centro de Suporte"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#1A202C"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Como podemos ajudá-lo hoje?"
android:textSize="14sp"
android:textColor="#718096"
android:layout_marginBottom="24dp"/>
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:rowCount="2">
<LinearLayout
android:id="@+id/cardFAQ"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:orientation="vertical"
android:padding="16dp"
android:layout_margin="8dp"
android:background="@drawable/bg_tech_gradient"
android:backgroundTint="#F7FAFC">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Perguntas Frequentes"
android:textStyle="bold"
android:textColor="#2D3748"/>
</LinearLayout>
<LinearLayout
android:id="@+id/cardTutorial"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:orientation="vertical"
android:padding="16dp"
android:layout_margin="8dp"
android:background="@drawable/bg_tech_gradient"
android:backgroundTint="#F7FAFC">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tutorial Passo a Passo"
android:textStyle="bold"
android:textColor="#2D3748"/>
</LinearLayout>
<LinearLayout
android:id="@+id/cardMensagem"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:orientation="vertical"
android:padding="16dp"
android:layout_margin="8dp"
android:background="@drawable/bg_tech_gradient"
android:backgroundTint="#F7FAFC">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Contactar Suporte"
android:textStyle="bold"
android:textColor="#2D3748"/>
</LinearLayout>
<LinearLayout
android:id="@+id/cardContactos"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:orientation="vertical"
android:padding="16dp"
android:layout_margin="8dp"
android:background="@drawable/bg_tech_gradient"
android:backgroundTint="#F7FAFC">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Contactos Diretos"
android:textStyle="bold"
android:textColor="#2D3748"/>
</LinearLayout>
</GridLayout>
<Button
android:id="@+id/btnFecharSuporte"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Fechar"
android:backgroundTint="#E2E8F0"
android:textColor="#2D3748"
android:layout_marginTop="16dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:padding="16dp">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="500dp"
app:cardCornerRadius="20dp"
app:cardBackgroundColor="#FFFFFF"
app:cardElevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="8dp">
<TextView android:id="@+id/btnVoltarTutorial" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="← Voltar" android:textColor="#2D3748" android:textStyle="bold" android:background="?attr/selectableItemBackground" android:paddingEnd="16dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tutorial da Finzora" android:textSize="18sp" android:textStyle="bold" android:textColor="#1A202C"/>
</LinearLayout>
<ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="8dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="16dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="1. Registe as suas transações" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Comece por adicionar todas as suas receitas e despesas no separador 'Adicionar'." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="2. Defina os seus orçamentos" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Estabeleça limites de gastos por categoria para manter o controlo financeiro." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="3. Acompanhe os seus gastos" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Use os gráficos para visualizar para onde vai o seu dinheiro." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F7FAFC" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="4. Siga as dicas personalizadas" android:textStyle="bold" android:textColor="#2D3748" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="A Finzora analisa os seus padrões e fornece recomendações úteis." android:textColor="#718096" android:textSize="13sp"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/bg_tech_gradient" android:backgroundTint="#F0FFF4" android:padding="16dp" android:layout_marginBottom="12dp">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="✓ Dica Extra" android:textStyle="bold" android:textColor="#2F855A" android:layout_marginBottom="8dp"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Mantenha a sua aplicação atualizada com transações regulares para obter dicas financeiras mais precisas e personalizadas!" android:textColor="#276749" android:textSize="13sp"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -0,0 +1,395 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_tech_gradient"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Resumo da Sua Saúde Financeira"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="12dp"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Taxa de Poupança"
android:textColor="@color/white"
android:textStyle="bold"/>
<TextView
android:id="@+id/tvTaxaPoupanca"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0.0%"
android:textColor="#00E676"
android:textStyle="bold"/>
</LinearLayout>
<ProgressBar
android:id="@+id/progressPoupanca"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="8dp"
android:max="100"
android:progress="0"
android:progressTint="#00E676"
android:layout_marginBottom="16dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:baselineAligned="false">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:background="#1AFFFFFF"
android:padding="12dp"
android:layout_marginEnd="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Receitas"
android:textColor="#B0BEC5"
android:textSize="12sp"/>
<TextView
android:id="@+id/tvDicasReceitas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="€ 0.00"
android:textColor="#00E676"
android:textStyle="bold"
android:textSize="16sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:background="#1AFFFFFF"
android:padding="12dp"
android:layout_marginStart="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Despesas"
android:textColor="#B0BEC5"
android:textSize="12sp"/>
<TextView
android:id="@+id/tvDicasDespesas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="€ 0.00"
android:textColor="#FF1744"
android:textStyle="bold"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dicas Personalizadas"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="12dp"/>
<androidx.cardview.widget.CardView
android:id="@+id/cardDica1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
app:cardCornerRadius="12dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvTituloDica1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A calcular..."
android:textColor="#00E676"
android:textStyle="bold"
android:textSize="16sp"/>
<TextView
android:id="@+id/tvDescDica1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="..."
android:textColor="#B0BEC5"
android:layout_marginTop="4dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cardDica2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:cardCornerRadius="12dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvTituloDica2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A analisar gastos..."
android:textColor="#FF1744"
android:textStyle="bold"
android:textSize="16sp"/>
<TextView
android:id="@+id/tvDescDica2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="..."
android:textColor="#B0BEC5"
android:layout_marginTop="4dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Distribuição de Gastos"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="12dp"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:cardCornerRadius="12dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="2dp">
<LinearLayout
android:id="@+id/layoutDistribuicao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
</LinearLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="12dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_idea"
app:tint="@color/white"
android:layout_marginEnd="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dicas Rápidas de Economia"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"/>
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
app:cardCornerRadius="12dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@android:drawable/checkbox_on_background"
app:tint="#00E676"
android:layout_marginTop="2dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Prepare refeições em casa"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pode poupar até €200/mês reduzindo refeições fora"
android:textColor="#B0BEC5"
android:textSize="12sp"
android:layout_marginTop="2dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@android:drawable/checkbox_on_background"
app:tint="#00E676"
android:layout_marginTop="2dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Compare preços antes de comprar"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Use apps de comparação para encontrar melhores ofertas"
android:textColor="#B0BEC5"
android:textSize="12sp"
android:layout_marginTop="2dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@android:drawable/checkbox_on_background"
app:tint="#00E676"
android:layout_marginTop="2dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancele subscrições não utilizadas"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reveja streamings, ginásios e apps que paga mas não usa"
android:textColor="#B0BEC5"
android:textSize="12sp"
android:layout_marginTop="2dp"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@android:drawable/checkbox_on_background"
app:tint="#00E676"
android:layout_marginTop="2dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Use transportes públicos"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Economize em combustível, estacionamento e manutenção"
android:textColor="#B0BEC5"
android:textSize="12sp"
android:layout_marginTop="2dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_tech_gradient"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Despesas por Categoria"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="4dp">
<com.github.mikephil.charting.charts.PieChart
android:id="@+id/pieChartDespesas"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="12dp"/>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Orçamento vs Gastos Reais"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="4dp">
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/barChartOrcamento"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="12dp"/>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tendência Mensal (Geral)"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="8dp"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginBottom="24dp"
app:cardCornerRadius="16dp"
app:cardBackgroundColor="#2C5364"
app:cardElevation="4dp">
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/barChartTendencia"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="12dp"/>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/bg_tech_gradient"
android:padding="16dp"
tools:ignore="HardcodedText">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Planeamento de Orçamento"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="18sp"
android:layout_marginBottom="8dp"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:cardCornerRadius="12dp"
app:cardBackgroundColor="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Categoria"
android:textColor="#333333"
android:textStyle="bold"
android:textSize="12sp"/>
<Spinner
android:id="@+id/spinnerOrcamento"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:background="#F5F5F5"
android:layout_marginBottom="12dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Limite Mensal (€)"
android:textColor="#333333"
android:textStyle="bold"
android:textSize="12sp"/>
<EditText
android:id="@+id/editLimite"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#F5F5F5"
android:padding="8dp"
android:inputType="numberDecimal"
android:hint="0.00"
android:textColor="#000000"
android:layout_marginBottom="16dp"/>
<Button
android:id="@+id/btnDefinirOrcamento"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Definir Orçamento"
android:backgroundTint="#0F2027"
android:textColor="@color/white"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Orçamentos Ativos"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="18sp"
android:layout_marginBottom="8dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerOrcamentos"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/bg_tech_gradient"
android:padding="8dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerTransacoes"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="80dp"/> </LinearLayout>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp"
android:textSize="16sp"
android:textColor="#FFFFFF"
android:background="#1A202C"
android:ellipsize="end"
android:singleLine="true"/>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
android:background="@drawable/bg_tech_gradient"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.cardview.widget.CardView
android:layout_width="120dp"
android:layout_height="120dp"
app:cardCornerRadius="24dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#20FFFFFF"
android:layout_marginBottom="40dp">
<ImageView
android:id="@+id/imgOnboarding"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center"
android:src="@drawable/wallet"/>
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/textTitulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Título do Tutorial"
android:textColor="@color/white"
android:textSize="28sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/textDescricao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Descrição detalhada sobre a funcionalidade."
android:textColor="#B0BEC5"
android:textSize="16sp"
android:gravity="center"
android:lineSpacingExtra="6dp"
android:layout_marginHorizontal="16dp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<TextView
android:id="@+id/tvCatOrcamento"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Alimentação"
android:textStyle="bold"
android:textColor="@color/white"
android:textSize="16sp"/>
<TextView
android:id="@+id/tvValoresOrcamento"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="€85.50 / €300.00"
android:textColor="#B0BEC5"
android:textSize="14sp"/>
</LinearLayout>
<ProgressBar
android:id="@+id/progressOrcamento"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="8dp"
android:progressDrawable="@drawable/progress_savings"
android:max="100"
android:progress="50"/>
<TextView
android:id="@+id/tvRestante"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Restam €214.50 (72%)"
android:textColor="#90A4AE"
android:textSize="12sp"
android:layout_marginTop="6dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="8dp"
app:cardBackgroundColor="#FFFFFF"
app:cardCornerRadius="12dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center_vertical">
<ImageView
android:id="@+id/imgIcone"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/bg_icone_transacao"
android:src="@android:drawable/ic_menu_sort_by_size"
app:tint="#4CAF50" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tvDescricao"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Descrição"
android:textColor="#1A202C"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Data"
android:textColor="#718096"
android:textSize="14sp"
android:layout_marginTop="4dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="end">
<TextView
android:id="@+id/tvValor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="€ 0.00"
android:textColor="#4CAF50"
android:textSize="16sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/btnEliminar"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginTop="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@android:drawable/ic_menu_delete"
app:tint="#F44336" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="fundo_app">#1A202C</color>
<color name="texto_principal">#FFFFFF</color>
<color name="fundo_cartao">#2D3748</color>
<color name="linha_separadora">#2D3748</color>
</resources>

View File

@@ -2,4 +2,15 @@
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="tech_accent_cyan">#00E676</color>
<color name="tech_card_bg">#2D3748</color>
<color name="text_secondary">#A0AEC0</color>
<color name="tech_accent_green">#48BB78</color>
<color name="tech_accent_yellow">#ECC94B</color> <color name="tech_accent_red">#F56565</color>
<color name="tech_bg_dark">#1A202C</color>
<color name="fundo_app">#FFFFFF</color>
<color name="texto_principal">#1A202C</color>
<color name="fundo_cartao">#F7FAFC</color>
<color name="linha_separadora">#E2E8F0</color>
</resources>

View File

@@ -1,4 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="main" type="id" />
<item name="editValor" type="id" />
<item name="editData" type="id" />
<item name="editCategoria" type="id" />
<item name="editDescricao" type="id" />
<item name="rbReceita" type="id" />
<item name="rbDespesa" type="id" />
<item name="btnSalvarTransacao" type="id" />
<item name="tabLayoutIndicator" type="id" />
<item name="iconSeta" type="id" />
<item name="btnApagar" type="id" />
<item name="spinnerCategoria" type="id" />
<item name="btnSalvar" type="id" />
<item name="tvTotalReceitas" type="id" />
<item name="tvTotalDespesas" type="id" />
<item name="tvPercentagemPoupanca" type="id" />
<item name="progressPoupanca" type="id" />
<item name="cardElogio" type="id" />
<item name="tvTextoElogio" type="id" />
<item name="cardAlerta" type="id" />
<item name="tvTituloAlerta" type="id" />
<item name="tvTextoAlerta" type="id" />
<item name="btnGuardar" type="id" />
</resources>

View File

@@ -1,8 +1,29 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Finzora" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
<item name="colorPrimary">@color/tech_accent_cyan</item>
<item name="colorOnPrimary">@color/black</item>
<item name="colorPrimaryContainer">@color/tech_card_bg</item>
<item name="colorOnPrimaryContainer">@color/white</item>
<item name="colorSecondary">@color/tech_accent_green</item>
<item name="colorOnSecondary">@color/black</item>
<item name="colorSecondaryContainer">@color/tech_card_bg</item>
<item name="colorOnSecondaryContainer">@color/white</item>
<item name="colorTertiary">@color/tech_accent_yellow</item>
<item name="colorOnTertiary">@color/black</item>
<item name="colorTertiaryContainer">@color/tech_card_bg</item>
<item name="colorOnTertiaryContainer">@color/white</item>
<item name="colorError">@color/tech_accent_red</item>
<item name="colorOnError">@color/black</item>
<item name="colorErrorContainer">@color/tech_card_bg</item>
<item name="colorOnErrorContainer">@color/white</item>
<item name="android:colorBackground">@color/tech_bg_dark</item>
<item name="colorOnBackground">@color/white</item>
<item name="colorSurface">@color/tech_card_bg</item>
<item name="colorOnSurface">@color/white</item>
<item name="colorSurfaceVariant">@color/tech_card_bg</item>
<item name="colorOnSurfaceVariant">@color/text_secondary</item>
<item name="colorOutline">@color/tech_accent_cyan</item>
</style>
<style name="Theme.Finzora" parent="Base.Theme.Finzora" />

View File

@@ -19,3 +19,13 @@ android.useAndroidX=true
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.defaults.buildfeatures.resvalues=true
android.sdk.defaultTargetSdkToCompileSdkIfUnset=false
android.enableAppCompileTimeRClass=false
android.usesSdkInManifest.disallowed=false
android.uniquePackageNames=false
android.dependency.useConstraints=true
android.r8.strictFullModeForKeepRules=false
android.r8.optimizedResourceShrinking=false
android.builtInKotlin=false
android.newDsl=false

View File

@@ -1,5 +1,5 @@
[versions]
agp = "8.13.2"
agp = "9.0.0"
junit = "4.13.2"
junitVersion = "1.3.0"
espressoCore = "3.7.0"

View File

@@ -1,7 +1,7 @@
#Tue Jan 20 14:33:41 WET 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -16,9 +16,9 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
rootProject.name = "Finzora"
include(":app")