first commit
|
|
@ -0,0 +1,15 @@
|
||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Gestão de Despesas
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="21" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "interactive"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
# 💰 Gestão de Despesas - Aplicação Android com SQLite
|
||||||
|
|
||||||
|
Uma aplicação Android moderna para gestão pessoal de despesas, desenvolvida com SQLite para armazenamento local de dados e interface Material Design.
|
||||||
|
|
||||||
|
## 🎯 Funcionalidades Implementadas
|
||||||
|
|
||||||
|
### ✅ **Funcionalidades Principais**
|
||||||
|
- **💰 Adicionar Despesas**: Registre novas despesas com descrição, valor, categoria, data e notas
|
||||||
|
- **📋 Listar Despesas**: Visualize todas as despesas em uma lista organizada e moderna
|
||||||
|
- **✏️ Editar Despesas**: Modifique informações de despesas existentes
|
||||||
|
- **🗑️ Excluir Despesas**: Remova despesas com confirmação de segurança
|
||||||
|
- **📊 Estatísticas**: Visualize o valor total gasto e número de despesas
|
||||||
|
- **🏷️ Categorização**: Organize despesas por categorias personalizadas
|
||||||
|
- **🎨 Interface Moderna**: Design Material Design com gradientes e animações
|
||||||
|
|
||||||
|
### ✅ **Funcionalidades Avançadas**
|
||||||
|
- **📱 Dashboard Principal**: Tela de boas-vindas com estatísticas em tempo real
|
||||||
|
- **🔍 Filtros**: Filtre despesas por categoria
|
||||||
|
- **📈 Estatísticas Visuais**: Cards com métricas importantes
|
||||||
|
- **🎯 Navegação Intuitiva**: Botões e ações bem organizados
|
||||||
|
- **🛡️ Tratamento de Erros**: Aplicação robusta que não fecha com erros
|
||||||
|
|
||||||
|
## 🏗️ Arquitetura do Projeto
|
||||||
|
|
||||||
|
### 📱 **Activities (Telas)**
|
||||||
|
- **`MainActivity.java`**: Tela principal com dashboard e estatísticas
|
||||||
|
- **`SimpleExpensesActivity.java`**: Lista simplificada de despesas
|
||||||
|
- **`AddExpenseActivity.java`**: Formulário para adicionar/editar despesas
|
||||||
|
|
||||||
|
### 🗄️ **Banco de Dados**
|
||||||
|
- **`DatabaseHelper.java`**: Gerenciador completo do SQLite
|
||||||
|
- **`Expense.java`**: Modelo de dados para despesas
|
||||||
|
|
||||||
|
### 🎨 **Interface**
|
||||||
|
- **`ExpenseAdapter.java`**: Adapter para RecyclerView
|
||||||
|
- **Layouts XML**: Design moderno com Material Design
|
||||||
|
|
||||||
|
## 🗄️ Banco de Dados SQLite
|
||||||
|
|
||||||
|
### 📊 **Tabela: `expenses`**
|
||||||
|
|
||||||
|
| Campo | Tipo | Descrição | Obrigatório |
|
||||||
|
|-------|------|-----------|-------------|
|
||||||
|
| `id` | INTEGER | Chave primária (auto incremento) | ✅ |
|
||||||
|
| `description` | TEXT | Descrição da despesa | ✅ |
|
||||||
|
| `amount` | REAL | Valor da despesa | ✅ |
|
||||||
|
| `category` | TEXT | Categoria da despesa | ✅ |
|
||||||
|
| `date` | TEXT | Data da despesa (DD/MM/AAAA) | ✅ |
|
||||||
|
| `notes` | TEXT | Notas adicionais | ❌ |
|
||||||
|
|
||||||
|
### 🔧 **Operações do DatabaseHelper**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Operações CRUD+wsqzxe
|
||||||
|
addExpense(Expense) // Adiciona nova despesa
|
||||||
|
getAllExpenses() // Retorna todas as despesas
|
||||||
|
getExpense(int id) // Retorna despesa específica
|
||||||
|
updateExpense(Expense) // Atualiza despesa existente
|
||||||
|
deleteExpense(int id) // Remove despesa
|
||||||
|
|
||||||
|
// Consultas personalizadas
|
||||||
|
getTotalExpenses() // Calcula total de despesas
|
||||||
|
getExpensesByCategory(String) // Filtra por categoria
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎨 Design e Interface
|
||||||
|
|
||||||
|
### 🎯 **Paleta de Cores**
|
||||||
|
- **Primária**: Azul moderno (#6366F1)
|
||||||
|
- **Secundária**: Azul claro (#A5B4FC)
|
||||||
|
- **Acento**: Laranja (#F59E0B)
|
||||||
|
- **Sucesso**: Verde (#10B981)
|
||||||
|
- **Erro**: Vermelho (#EF4444)GREA
|
||||||
|
|
||||||
|
### 🎨 **Elementos Visuais**
|
||||||
|
- **Gradientes**: Fundos com transições suaves
|
||||||
|
- **Cards**: Design Material com sombras e bordas arredondadas
|
||||||
|
- **Ícones**: SVG otimizados para cada função
|
||||||
|
- **Animações**: Transições suaves entre telas
|
||||||
|
|
||||||
|
## 📱 Como Usar a Aplicação
|
||||||
|
|
||||||
|
### 🚀 **Primeiro Uso**
|
||||||
|
1. **Abrir a aplicação** → Vê a tela de boas-vindas
|
||||||
|
2. **Ver estatísticas** → Total gasto e número de despesas
|
||||||
|
3. **Clicar "Começar Agora"** → Vai para adicionar primeira despesa
|
||||||
|
|
||||||
|
### 💰 **Gerenciar Despesas**
|
||||||
|
1. **Adicionar**: Botão "+" ou "Adicionar" → Formulário completo
|
||||||
|
2. **Preencher**: Descrição, valor, categoria, data, notas
|
||||||
|
3. **Salvar**: Confirma e retorna à tela anterior
|
||||||
|
4. **Ver Lista**: Botão "Ver Despesas" → Lista completa
|
||||||
|
|
||||||
|
### ✏️ **Editar/Excluir**
|
||||||
|
1. **Na Lista**: Cada despesa tem botões "Editar" e "Excluir"
|
||||||
|
2. **Editar**: Abre o formulário com dados preenchidos
|
||||||
|
3. **Excluir**: Confirmação de segurança antes de remover
|
||||||
|
|
||||||
|
## 🛠️ Tecnologias Utilizadas
|
||||||
|
|
||||||
|
- **📱 Android SDK**: Desenvolvimento nativo Android
|
||||||
|
- **🗄️ SQLite**: Banco de dados local persistente
|
||||||
|
- **🎨 Material Design**: Interface moderna e responsiva
|
||||||
|
- **📋 RecyclerView**: Lista otimizada para performance
|
||||||
|
- **☕ Java**: Linguagem de programação
|
||||||
|
- **🎨 Gradientes**: Design visual atrativo
|
||||||
|
|
||||||
|
## 📋 Requisitos do Sistema
|
||||||
|
|
||||||
|
- **Android**: API 24+ (Android 7.0 Nougat)
|
||||||
|
- **Android Studio**: Última versão estável
|
||||||
|
- **Gradle**: 8.13+
|
||||||
|
- **Java**: JDK 11+
|
||||||
|
|
||||||
|
## 🚀 Instalação e Execução
|
||||||
|
|
||||||
|
### 📥 **Instalação**
|
||||||
|
```bash
|
||||||
|
# 1. Clone o repositório
|
||||||
|
git clone [url-do-repositorio]
|
||||||
|
|
||||||
|
# 2. Abra no Android Studio
|
||||||
|
# 3. Sincronize o Gradle
|
||||||
|
# 4. Execute no emulador ou dispositivo
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔧 **Compilação**
|
||||||
|
```bash
|
||||||
|
# Compilar APK de debug
|
||||||
|
./gradlew assembleDebug
|
||||||
|
|
||||||
|
# Executar testes
|
||||||
|
./gradlew test
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Estrutura de Arquivos
|
||||||
|
|
||||||
|
```
|
||||||
|
app/src/main/
|
||||||
|
├── java/pt/epvc/gestodedespesas/
|
||||||
|
│ ├── MainActivity.java # Tela principal com dashboard
|
||||||
|
│ ├── SimpleExpensesActivity.java # Lista simplificada de despesas
|
||||||
|
│ ├── AddExpenseActivity.java # Formulário de adicionar/editar
|
||||||
|
│ ├── Expense.java # Modelo de dados
|
||||||
|
│ ├── DatabaseHelper.java # Gerenciador SQLite
|
||||||
|
│ └── ExpenseAdapter.java # Adapter do RecyclerView
|
||||||
|
│
|
||||||
|
├── res/layout/
|
||||||
|
│ ├── activity_main.xml # Layout da tela principal
|
||||||
|
│ ├── activity_simple_expenses.xml # Layout da lista de despesas
|
||||||
|
│ ├── activity_add_expense.xml # Layout do formulário
|
||||||
|
│ └── item_expense.xml # Layout do item da lista
|
||||||
|
│
|
||||||
|
├── res/drawable/ # Ícones e recursos visuais
|
||||||
|
├── res/values/
|
||||||
|
│ ├── colors.xml # Paleta de cores
|
||||||
|
│ └── strings.xml # Textos da aplicação
|
||||||
|
└── AndroidManifest.xml # Configuração da aplicação
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Funcionalidades Técnicas
|
||||||
|
|
||||||
|
### 🛡️ **Tratamento de Erros**
|
||||||
|
- **Try-Catch**: Em todas as operações críticas
|
||||||
|
- **Verificações de Null**: Para evitar crashes
|
||||||
|
- **Mensagens Informativas**: Toast para feedback do usuário
|
||||||
|
- **Fallback Seguro**: Aplicação continua funcionando mesmo com erros
|
||||||
|
|
||||||
|
### 📊 **Performance**
|
||||||
|
- **RecyclerView**: Lista otimizada para grandes quantidades de dados
|
||||||
|
- **SQLite**: Consultas eficientes com índices
|
||||||
|
- **Lazy Loading**: Carregamento sob demanda
|
||||||
|
- **Memory Management**: Gerenciamento eficiente de memória
|
||||||
|
|
||||||
|
### 🎨 **Design Responsivo**
|
||||||
|
- **Material Design**: Seguindo guidelines do Google
|
||||||
|
- **Adaptação**: Funciona em diferentes tamanhos de tela
|
||||||
|
- **Acessibilidade**: Elementos grandes e bem contrastados
|
||||||
|
- **Navegação**: Fluxo intuitivo entre telas
|
||||||
|
|
||||||
|
## 🚀 Próximas Funcionalidades
|
||||||
|
|
||||||
|
### 📊 **Análises e Relatórios**
|
||||||
|
- Gráficos de gastos por categoria
|
||||||
|
- Relatórios mensais/anuais
|
||||||
|
- Comparativos entre períodos
|
||||||
|
- Exportação para PDF/Excel
|
||||||
|
|
||||||
|
### 🔍 **Filtros Avançados**
|
||||||
|
- Filtro por período de datas
|
||||||
|
- Busca por texto na descrição
|
||||||
|
- Ordenação por valor, data, categoria
|
||||||
|
- Filtros salvos
|
||||||
|
|
||||||
|
### 💾 **Backup e Sincronização**
|
||||||
|
- Backup automático na nuvem
|
||||||
|
- Sincronização entre dispositivos
|
||||||
|
- Restauração de dados
|
||||||
|
- Exportação/importação
|
||||||
|
|
||||||
|
### 🎨 **Personalização**
|
||||||
|
- Modo escuro/claro
|
||||||
|
- Temas personalizados
|
||||||
|
- Categorias customizáveis
|
||||||
|
- Widget para tela inicial
|
||||||
|
|
||||||
|
## 📝 Notas de Desenvolvimento
|
||||||
|
|
||||||
|
### 🎯 **Decisões de Design**
|
||||||
|
- **Interface Simplificada**: Foco na usabilidade
|
||||||
|
- **Material Design**: Consistência visual
|
||||||
|
- **SQLite Local**: Privacidade e performance
|
||||||
|
- **Tratamento de Erros**: Robustez da aplicação
|
||||||
|
|
||||||
|
### 🔧 **Implementações Técnicas**
|
||||||
|
- **DatabaseHelper**: Padrão Singleton para acesso ao banco
|
||||||
|
- **Serializable**: Passagem de objetos entre Activities
|
||||||
|
- **RecyclerView**: Performance otimizada para listas
|
||||||
|
- **Material Components**: Interface moderna e acessível
|
||||||
|
|
||||||
|
### 📱 **Compatibilidade**
|
||||||
|
- **Android 7.0+**: Suporte amplo de dispositivos
|
||||||
|
- **Edge-to-Edge**: Design moderno
|
||||||
|
- **Responsive**: Adapta-se a diferentes telas
|
||||||
|
- **Acessibilidade**: Suporte a leitores de tela
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 👨💻 Desenvolvido com ❤️
|
||||||
|
|
||||||
|
Esta aplicação foi desenvolvida como um exemplo completo de aplicação Android com SQLite, demonstrando:
|
||||||
|
- Arquitetura limpa e organizada
|
||||||
|
- Interface moderna e responsiva
|
||||||
|
- Tratamento robusto de erros
|
||||||
|
- Documentação completa em português
|
||||||
|
- Código bem comentado e explicado
|
||||||
|
|
||||||
|
**Tecnologias**: Android SDK, SQLite, Material Design, Java
|
||||||
|
**Design**: Interface moderna com gradientes e animações
|
||||||
|
**Funcionalidades**: CRUD completo, estatísticas, filtros, navegação intuitiva
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Configuração do módulo app - aplicação principal Android
|
||||||
|
|
||||||
|
// Plugins aplicados a este módulo
|
||||||
|
plugins {
|
||||||
|
id 'com.android.application' // Plugin para criar aplicação Android
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurações específicas do Android
|
||||||
|
android {
|
||||||
|
namespace 'pt.epvc.gestodedespesas' // Namespace único da aplicação
|
||||||
|
compileSdk 35 // Versão do SDK Android para compilação
|
||||||
|
|
||||||
|
// Configurações padrão da aplicação
|
||||||
|
defaultConfig {
|
||||||
|
applicationId 'pt.epvc.gestodedespesas' // ID único da aplicação na Play Store
|
||||||
|
minSdk 24 // Versão mínima do Android suportada (Android 7.0)
|
||||||
|
targetSdk 35 // Versão do Android para a qual a aplicação foi desenvolvida
|
||||||
|
versionCode 1 // Número da versão (incrementa a cada release)
|
||||||
|
versionName '1.0' // Nome da versão exibido aos usuários
|
||||||
|
|
||||||
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' // Runner para testes instrumentados
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurações de build (Debug/Release)
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false // Desabilita minificação (otimização de código)
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // Arquivos ProGuard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurações de compilação Java
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11 // Versão do Java para código fonte
|
||||||
|
targetCompatibility JavaVersion.VERSION_11 // Versão do Java para bytecode compilado
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependências da aplicação
|
||||||
|
dependencies {
|
||||||
|
// Bibliotecas AndroidX (suporte moderno)
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.7.1' // Compatibilidade com versões antigas
|
||||||
|
implementation 'com.google.android.material:material:1.10.0' // Material Design Components
|
||||||
|
implementation 'androidx.activity:activity:1.8.2' // Activity base moderna
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' // Layout com constraints
|
||||||
|
|
||||||
|
// Bibliotecas de teste
|
||||||
|
testImplementation 'junit:junit:4.13.2' // Framework de testes unitários
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5' // Extensões JUnit para Android
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' // Framework de testes de interface
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package pt.epvc.gestodedespesas;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
assertEquals("pt.epvc.gestodedespesas", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.GestãoDeDespesas">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".AddExpenseActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".ExpensesListActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".SimpleExpensesActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
package pt.epvc.gestodedespesas; // Define o pacote onde esta classe está localizada
|
||||||
|
|
||||||
|
import android.os.Bundle; // Importa classe para passar dados entre Activities
|
||||||
|
import android.view.View; // Importa classe base para elementos visuais
|
||||||
|
import android.widget.Toast; // Importa classe para exibir mensagens temporárias
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity; // Importa classe base para Activities modernas
|
||||||
|
|
||||||
|
import com.google.android.material.button.MaterialButton; // Importa botão Material Design
|
||||||
|
import com.google.android.material.textfield.TextInputEditText; // Importa campo de texto Material Design
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat; // Importa classe para formatação de datas
|
||||||
|
import java.util.Date; // Importa classe para trabalhar com datas
|
||||||
|
import java.util.Locale; // Importa classe para configurações de localização
|
||||||
|
|
||||||
|
import pt.epvc.gestodedespesas.data.DatabaseHelper; // DatabaseHelper movido para pacote data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AddExpenseActivity - Tela de Adicionar/Editar Despesas
|
||||||
|
*
|
||||||
|
* Esta Activity permite ao usuário adicionar novas despesas ou editar despesas existentes.
|
||||||
|
* Funcionalidades implementadas:
|
||||||
|
* - Formulário completo para entrada de dados da despesa
|
||||||
|
* - Validação de todos os campos obrigatórios
|
||||||
|
* - Suporte para edição de despesas existentes
|
||||||
|
* - Data atual como padrão para novas despesas
|
||||||
|
* - Feedback visual com mensagens de sucesso/erro
|
||||||
|
* - Navegação de volta após salvar/cancelar
|
||||||
|
*/
|
||||||
|
public class AddExpenseActivity extends AppCompatActivity { // Declara classe que estende AppCompatActivity
|
||||||
|
|
||||||
|
// Views da interface - campos de entrada do formulário
|
||||||
|
private TextInputEditText etDescription, etAmount, etCategory, etDate, etNotes; // Declara campos de texto para entrada de dados
|
||||||
|
private MaterialButton btnSave, btnCancel; // Declara botões de ação
|
||||||
|
|
||||||
|
// Objetos para funcionalidade
|
||||||
|
private DatabaseHelper databaseHelper; // Declara helper para operações com SQLite
|
||||||
|
private Expense expenseToEdit = null; // Declara variável para despesa sendo editada (null para nova despesa)
|
||||||
|
|
||||||
|
@Override // Sobrescreve método da classe pai
|
||||||
|
protected void onCreate(Bundle savedInstanceState) { // Método chamado quando Activity é criada
|
||||||
|
super.onCreate(savedInstanceState); // Chama método da classe pai
|
||||||
|
// Define o layout da tela de adicionar despesa (activity_add_expense.xml)
|
||||||
|
setContentView(R.layout.activity_add_expense); // Conecta layout XML com esta Activity
|
||||||
|
|
||||||
|
// Inicializa o helper do banco de dados SQLite
|
||||||
|
databaseHelper = new DatabaseHelper(this); // Cria nova instância do DatabaseHelper
|
||||||
|
|
||||||
|
// Configura a interface e funcionalidades
|
||||||
|
initializeViews(); // Chama método para conectar views com variáveis
|
||||||
|
setupClickListeners(); // Chama método para configurar eventos de clique
|
||||||
|
|
||||||
|
// Verificar se é edição de uma despesa existente
|
||||||
|
if (getIntent().hasExtra("expense")) { // Verifica se Intent contém dados de despesa para edição
|
||||||
|
// Se há uma despesa extra, estamos editando
|
||||||
|
expenseToEdit = (Expense) getIntent().getSerializableExtra("expense"); // Obtém despesa do Intent e converte para Expense
|
||||||
|
populateFields(); // Chama método para preencher campos com dados da despesa
|
||||||
|
} else { // Se não há despesa extra, é uma nova despesa
|
||||||
|
// Para nova despesa, define a data atual como padrão
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()); // Cria formato de data brasileiro
|
||||||
|
etDate.setText(sdf.format(new Date())); // Define data atual no campo de data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicializa as views conectando os elementos do layout com as variáveis Java
|
||||||
|
* Este método é chamado no onCreate para preparar a interface
|
||||||
|
*/
|
||||||
|
private void initializeViews() { // Declara método privado para inicializar views
|
||||||
|
// Conecta os campos de entrada de texto
|
||||||
|
etDescription = findViewById(R.id.etDescription); // Busca campo de descrição por ID e atribui à variável
|
||||||
|
etAmount = findViewById(R.id.etAmount); // Busca campo de valor por ID e atribui à variável
|
||||||
|
etCategory = findViewById(R.id.etCategory); // Busca campo de categoria por ID e atribui à variável
|
||||||
|
etDate = findViewById(R.id.etDate); // Busca campo de data por ID e atribui à variável
|
||||||
|
etNotes = findViewById(R.id.etNotes); // Busca campo de notas por ID e atribui à variável
|
||||||
|
|
||||||
|
// Conecta os botões de ação
|
||||||
|
btnSave = findViewById(R.id.btnSave); // Busca botão salvar por ID e atribui à variável
|
||||||
|
btnCancel = findViewById(R.id.btnCancel); // Busca botão cancelar por ID e atribui à variável
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configura os eventos de clique para os botões da interface
|
||||||
|
* Cada botão executa uma ação específica
|
||||||
|
*/
|
||||||
|
private void setupClickListeners() { // Declara método privado para configurar listeners
|
||||||
|
// Botão Salvar - valida e salva a despesa
|
||||||
|
btnSave.setOnClickListener(new View.OnClickListener() { // Define listener de clique para o botão salvar
|
||||||
|
@Override // Sobrescreve método da interface
|
||||||
|
public void onClick(View v) { // Método chamado quando botão é clicado
|
||||||
|
saveExpense(); // Chama método para salvar a despesa
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Botão Cancelar - fecha a tela sem salvar
|
||||||
|
btnCancel.setOnClickListener(new View.OnClickListener() { // Define listener de clique para o botão cancelar
|
||||||
|
@Override // Sobrescreve método da interface
|
||||||
|
public void onClick(View v) { // Método chamado quando botão é clicado
|
||||||
|
finish(); // Fecha a Activity atual
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preenche os campos do formulário com os dados da despesa sendo editada
|
||||||
|
* Este método é chamado apenas quando estamos editando uma despesa existente
|
||||||
|
*/
|
||||||
|
private void populateFields() { // Declara método privado para preencher campos
|
||||||
|
if (expenseToEdit != null) { // Verifica se há despesa para editar
|
||||||
|
// Preenche cada campo com os dados da despesa
|
||||||
|
etDescription.setText(expenseToEdit.getDescription()); // Define texto do campo descrição com dados da despesa
|
||||||
|
etAmount.setText(String.valueOf(expenseToEdit.getAmount())); // Define texto do campo valor convertendo double para string
|
||||||
|
etCategory.setText(expenseToEdit.getCategory()); // Define texto do campo categoria com dados da despesa
|
||||||
|
etDate.setText(expenseToEdit.getDate()); // Define texto do campo data com dados da despesa
|
||||||
|
etNotes.setText(expenseToEdit.getNotes()); // Define texto do campo notas com dados da despesa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Salva a despesa no banco de dados após validar todos os campos
|
||||||
|
* Este método é responsável por:
|
||||||
|
* - Validar todos os campos obrigatórios
|
||||||
|
* - Converter e validar o valor numérico
|
||||||
|
* - Criar ou atualizar a despesa no banco
|
||||||
|
* - Exibir feedback ao usuário
|
||||||
|
* - Fechar a tela em caso de sucesso
|
||||||
|
*/
|
||||||
|
private void saveExpense() { // Declara método privado para salvar despesa
|
||||||
|
// Obtém os valores dos campos e remove espaços em branco
|
||||||
|
String description = etDescription.getText().toString().trim(); // Obtém texto do campo descrição e remove espaços
|
||||||
|
String amountStr = etAmount.getText().toString().trim(); // Obtém texto do campo valor e remove espaços
|
||||||
|
String category = etCategory.getText().toString().trim(); // Obtém texto do campo categoria e remove espaços
|
||||||
|
String date = etDate.getText().toString().trim(); // Obtém texto do campo data e remove espaços
|
||||||
|
String notes = etNotes.getText().toString().trim(); // Obtém texto do campo notas e remove espaços
|
||||||
|
|
||||||
|
// VALIDAÇÕES DOS CAMPOS OBRIGATÓRIOS
|
||||||
|
|
||||||
|
// Validação da descrição
|
||||||
|
if (description.isEmpty()) { // Verifica se descrição está vazia
|
||||||
|
etDescription.setError("Descrição é obrigatória"); // Define mensagem de erro no campo
|
||||||
|
etDescription.requestFocus(); // Foca no campo com erro
|
||||||
|
return; // Para a execução se houver erro
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validação do valor
|
||||||
|
if (amountStr.isEmpty()) { // Verifica se valor está vazio
|
||||||
|
etAmount.setError("Valor é obrigatório"); // Define mensagem de erro no campo
|
||||||
|
etAmount.requestFocus(); // Foca no campo com erro
|
||||||
|
return; // Para a execução se houver erro
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversão e validação do valor numérico
|
||||||
|
double amount; // Declara variável para armazenar valor convertido
|
||||||
|
try { // Inicia bloco try para capturar erros de conversão
|
||||||
|
amount = Double.parseDouble(amountStr); // Converte string para double
|
||||||
|
if (amount <= 0) { // Verifica se o valor é positivo
|
||||||
|
etAmount.setError("Valor deve ser maior que zero"); // Define mensagem de erro
|
||||||
|
etAmount.requestFocus(); // Foca no campo com erro
|
||||||
|
return; // Para a execução se houver erro
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) { // Captura erro de conversão
|
||||||
|
etAmount.setError("Valor inválido"); // Define mensagem de erro
|
||||||
|
etAmount.requestFocus(); // Foca no campo com erro
|
||||||
|
return; // Para a execução se houver erro
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validação da categoria
|
||||||
|
if (category.isEmpty()) { // Verifica se categoria está vazia
|
||||||
|
etCategory.setError("Categoria é obrigatória"); // Define mensagem de erro no campo
|
||||||
|
etCategory.requestFocus(); // Foca no campo com erro
|
||||||
|
return; // Para a execução se houver erro
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validação da data
|
||||||
|
if (date.isEmpty()) { // Verifica se data está vazia
|
||||||
|
etDate.setError("Data é obrigatória"); // Define mensagem de erro no campo
|
||||||
|
etDate.requestFocus(); // Foca no campo com erro
|
||||||
|
return; // Para a execução se houver erro
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRIAÇÃO/ATUALIZAÇÃO DA DESPESA
|
||||||
|
|
||||||
|
// Cria um objeto Expense com os dados validados
|
||||||
|
Expense expense = new Expense(description, amount, category, date, notes); // Cria nova instância de Expense com dados validados
|
||||||
|
|
||||||
|
if (expenseToEdit != null) { // Verifica se estamos editando uma despesa existente
|
||||||
|
// EDIÇÃO - Atualiza despesa existente
|
||||||
|
expense.setId(expenseToEdit.getId()); // Define o ID da despesa existente no objeto
|
||||||
|
int result = databaseHelper.updateExpense(expense); // Chama método para atualizar despesa no banco
|
||||||
|
|
||||||
|
if (result > 0) { // Verifica se a atualização foi bem-sucedida (retorna número de linhas afetadas)
|
||||||
|
Toast.makeText(this, "Despesa atualizada com sucesso!", Toast.LENGTH_SHORT).show(); // Exibe mensagem de sucesso
|
||||||
|
setResult(RESULT_OK); // Define resultado de sucesso para Activity pai
|
||||||
|
finish(); // Fecha a Activity atual
|
||||||
|
} else { // Se houve erro na atualização
|
||||||
|
Toast.makeText(this, "Erro ao atualizar despesa", Toast.LENGTH_SHORT).show(); // Exibe mensagem de erro
|
||||||
|
}
|
||||||
|
} else { // Se não estamos editando, é uma nova despesa
|
||||||
|
// NOVA DESPESA - Adiciona nova despesa
|
||||||
|
long id = databaseHelper.addExpense(expense); // Chama método para adicionar despesa no banco
|
||||||
|
|
||||||
|
if (id > 0) { // Verifica se a adição foi bem-sucedida (retorna ID > 0)
|
||||||
|
Toast.makeText(this, "Despesa adicionada com sucesso!", Toast.LENGTH_SHORT).show(); // Exibe mensagem de sucesso
|
||||||
|
setResult(RESULT_OK); // Define resultado de sucesso para Activity pai
|
||||||
|
finish(); // Fecha a Activity atual
|
||||||
|
} else { // Se houve erro na adição
|
||||||
|
Toast.makeText(this, "Erro ao adicionar despesa", Toast.LENGTH_SHORT).show(); // Exibe mensagem de erro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
package pt.epvc.gestodedespesas; // Define o pacote onde esta classe está localizada
|
||||||
|
|
||||||
|
import java.io.Serializable; // Importa interface para permitir serialização de objetos
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expense - Modelo de Dados para Despesas
|
||||||
|
*
|
||||||
|
* Esta classe representa uma despesa no sistema.
|
||||||
|
* Implementa Serializable para permitir passagem entre Activities.
|
||||||
|
*
|
||||||
|
* Campos da despesa:
|
||||||
|
* - id: Identificador único (chave primária)
|
||||||
|
* - description: Descrição da despesa
|
||||||
|
* - amount: Valor da despesa
|
||||||
|
* - category: Categoria da despesa
|
||||||
|
* - date: Data da despesa
|
||||||
|
* - notes: Notas adicionais (opcional)
|
||||||
|
*/
|
||||||
|
public class Expense implements Serializable { // Declara classe que implementa Serializable
|
||||||
|
private int id; // Declara campo privado para ID da despesa
|
||||||
|
private String description; // Declara campo privado para descrição da despesa
|
||||||
|
private double amount; // Declara campo privado para valor da despesa
|
||||||
|
private String category; // Declara campo privado para categoria da despesa
|
||||||
|
private String date; // Declara campo privado para data da despesa
|
||||||
|
private String notes; // Declara campo privado para notas da despesa
|
||||||
|
|
||||||
|
// Construtores
|
||||||
|
public Expense() {} // Construtor padrão sem parâmetros
|
||||||
|
|
||||||
|
public Expense(String description, double amount, String category, String date, String notes) { // Construtor com parâmetros
|
||||||
|
this.description = description; // Define descrição da despesa
|
||||||
|
this.amount = amount; // Define valor da despesa
|
||||||
|
this.category = category; // Define categoria da despesa
|
||||||
|
this.date = date; // Define data da despesa
|
||||||
|
this.notes = notes; // Define notas da despesa
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expense(int id, String description, double amount, String category, String date, String notes) { // Construtor completo com ID
|
||||||
|
this.id = id; // Define ID da despesa
|
||||||
|
this.description = description; // Define descrição da despesa
|
||||||
|
this.amount = amount; // Define valor da despesa
|
||||||
|
this.category = category; // Define categoria da despesa
|
||||||
|
this.date = date; // Define data da despesa
|
||||||
|
this.notes = notes; // Define notas da despesa
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters e Setters
|
||||||
|
public int getId() { // Declara método público getter para ID
|
||||||
|
return id; // Retorna valor do campo id
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) { // Declara método público setter para ID
|
||||||
|
this.id = id; // Define valor do campo id
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() { // Declara método público getter para descrição
|
||||||
|
return description; // Retorna valor do campo description
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) { // Declara método público setter para descrição
|
||||||
|
this.description = description; // Define valor do campo description
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAmount() { // Declara método público getter para valor
|
||||||
|
return amount; // Retorna valor do campo amount
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAmount(double amount) { // Declara método público setter para valor
|
||||||
|
this.amount = amount; // Define valor do campo amount
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() { // Declara método público getter para categoria
|
||||||
|
return category; // Retorna valor do campo category
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategory(String category) { // Declara método público setter para categoria
|
||||||
|
this.category = category; // Define valor do campo category
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDate() { // Declara método público getter para data
|
||||||
|
return date; // Retorna valor do campo date
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDate(String date) { // Declara método público setter para data
|
||||||
|
this.date = date; // Define valor do campo date
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNotes() { // Declara método público getter para notas
|
||||||
|
return notes; // Retorna valor do campo notes
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotes(String notes) { // Declara método público setter para notas
|
||||||
|
this.notes = notes; // Define valor do campo notes
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // Sobrescreve método da classe Object
|
||||||
|
public String toString() { // Declara método público para converter objeto em string
|
||||||
|
return "Expense{" + // Inicia string de representação do objeto
|
||||||
|
"id=" + id + // Adiciona ID à string
|
||||||
|
", description='" + description + '\'' + // Adiciona descrição à string
|
||||||
|
", amount=" + amount + // Adiciona valor à string
|
||||||
|
", category='" + category + '\'' + // Adiciona categoria à string
|
||||||
|
", date='" + date + '\'' + // Adiciona data à string
|
||||||
|
", notes='" + notes + '\'' + // Adiciona notas à string
|
||||||
|
'}'; // Finaliza string de representação do objeto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
package pt.epvc.gestodedespesas; // Define o pacote onde esta classe está localizada
|
||||||
|
|
||||||
|
import android.view.LayoutInflater; // Importa classe para inflar layouts XML
|
||||||
|
import android.view.View; // Importa classe base para elementos visuais
|
||||||
|
import android.view.ViewGroup; // Importa classe para grupos de views
|
||||||
|
import android.widget.TextView; // Importa classe para exibir texto
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull; // Importa anotação para parâmetros não nulos
|
||||||
|
import androidx.recyclerview.widget.RecyclerView; // Importa classe base para adaptadores de lista
|
||||||
|
|
||||||
|
import com.google.android.material.button.MaterialButton; // Importa botão Material Design
|
||||||
|
|
||||||
|
import java.text.DecimalFormat; // Importa classe para formatação de números
|
||||||
|
import java.util.List; // Importa interface para listas
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExpenseAdapter - Adaptador para RecyclerView de Despesas
|
||||||
|
*
|
||||||
|
* Esta classe é responsável por exibir a lista de despesas em um RecyclerView.
|
||||||
|
* Funcionalidades implementadas:
|
||||||
|
* - Exibe cada despesa em um item personalizado
|
||||||
|
* - Formata valores monetários em euros
|
||||||
|
* - Permite editar e excluir despesas através de botões
|
||||||
|
* - Atualiza a lista quando os dados mudam
|
||||||
|
* - Interface para comunicação com a Activity pai
|
||||||
|
*/
|
||||||
|
public class ExpenseAdapter extends RecyclerView.Adapter<ExpenseAdapter.ExpenseViewHolder> { // Declara classe que estende RecyclerView.Adapter
|
||||||
|
|
||||||
|
// Dados e configurações
|
||||||
|
private List<Expense> expenseList; // Declara lista de despesas para exibir
|
||||||
|
private OnExpenseClickListener listener; // Declara interface para eventos de clique
|
||||||
|
private DecimalFormat currencyFormat = new DecimalFormat("€#,##0.00"); // Cria formato de moeda em euros
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface para comunicação com a Activity que usa este adapter
|
||||||
|
* Permite que a Activity responda aos cliques de editar e excluir
|
||||||
|
*/
|
||||||
|
public interface OnExpenseClickListener { // Declara interface pública para eventos de clique
|
||||||
|
void onEditClick(Expense expense); // Declara método para evento de clique em editar
|
||||||
|
void onDeleteClick(Expense expense); // Declara método para evento de clique em excluir
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor do adapter
|
||||||
|
* @param expenseList Lista de despesas para exibir
|
||||||
|
* @param listener Interface para eventos de clique
|
||||||
|
*/
|
||||||
|
public ExpenseAdapter(List<Expense> expenseList, OnExpenseClickListener listener) { // Declara construtor público
|
||||||
|
this.expenseList = expenseList; // Armazena a lista de despesas
|
||||||
|
this.listener = listener; // Armazena o listener para eventos
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cria um novo ViewHolder para um item da lista
|
||||||
|
* Este método é chamado pelo RecyclerView quando precisa de um novo item
|
||||||
|
* @param parent ViewGroup pai (o RecyclerView)
|
||||||
|
* @param viewType Tipo da view (não usado neste caso)
|
||||||
|
* @return Novo ExpenseViewHolder
|
||||||
|
*/
|
||||||
|
@NonNull // Anotação indicando que o retorno não pode ser nulo
|
||||||
|
@Override // Sobrescreve método da classe pai
|
||||||
|
public ExpenseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // Declara método para criar ViewHolder
|
||||||
|
// Infla o layout do item (item_expense.xml) e cria o ViewHolder
|
||||||
|
View view = LayoutInflater.from(parent.getContext()) // Obtém LayoutInflater do contexto do pai
|
||||||
|
.inflate(R.layout.item_expense, parent, false); // Infla layout XML para criar view
|
||||||
|
return new ExpenseViewHolder(view); // Retorna novo ViewHolder criado
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preenche os dados de um item da lista com os dados da despesa correspondente
|
||||||
|
* Este método é chamado pelo RecyclerView para cada item visível
|
||||||
|
* @param holder ViewHolder que contém as views do item
|
||||||
|
* @param position Posição do item na lista
|
||||||
|
*/
|
||||||
|
@Override // Sobrescreve método da classe pai
|
||||||
|
public void onBindViewHolder(@NonNull ExpenseViewHolder holder, int position) { // Declara método para vincular dados ao ViewHolder
|
||||||
|
Expense expense = expenseList.get(position); // Obtém a despesa na posição atual da lista
|
||||||
|
|
||||||
|
// Preenche os campos de texto com os dados da despesa
|
||||||
|
holder.tvDescription.setText(expense.getDescription()); // Define texto da descrição no TextView
|
||||||
|
holder.tvAmount.setText(currencyFormat.format(expense.getAmount())); // Define texto do valor formatado em moeda no TextView
|
||||||
|
holder.tvCategory.setText(expense.getCategory()); // Define texto da categoria no TextView
|
||||||
|
holder.tvDate.setText(expense.getDate()); // Define texto da data no TextView
|
||||||
|
|
||||||
|
// Tratamento especial para notas - só exibe se não estiver vazia
|
||||||
|
if (expense.getNotes() != null && !expense.getNotes().trim().isEmpty()) { // Verifica se notas não são nulas nem vazias
|
||||||
|
holder.tvNotes.setText(expense.getNotes()); // Define texto das notas no TextView
|
||||||
|
holder.tvNotes.setVisibility(View.VISIBLE); // Torna TextView de notas visível
|
||||||
|
} else { // Se notas estão vazias ou nulas
|
||||||
|
holder.tvNotes.setVisibility(View.GONE); // Oculta TextView de notas
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configura o botão Editar
|
||||||
|
holder.btnEdit.setOnClickListener(v -> { // Define listener de clique para botão editar
|
||||||
|
if (listener != null) { // Verifica se listener não é nulo
|
||||||
|
listener.onEditClick(expense); // Chama método da interface para notificar clique em editar
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configura o botão Excluir
|
||||||
|
holder.btnDelete.setOnClickListener(v -> { // Define listener de clique para botão excluir
|
||||||
|
if (listener != null) { // Verifica se listener não é nulo
|
||||||
|
listener.onDeleteClick(expense); // Chama método da interface para notificar clique em excluir
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna o número total de itens na lista
|
||||||
|
* @return Número de despesas na lista
|
||||||
|
*/
|
||||||
|
@Override // Sobrescreve método da classe pai
|
||||||
|
public int getItemCount() { // Declara método para obter número de itens
|
||||||
|
return expenseList.size(); // Retorna o tamanho da lista de despesas
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atualiza a lista de despesas e notifica o RecyclerView sobre a mudança
|
||||||
|
* Este método é usado quando os dados mudam (adicionar, editar, excluir)
|
||||||
|
* @param newExpenseList Nova lista de despesas
|
||||||
|
*/
|
||||||
|
public void updateExpenses(List<Expense> newExpenseList) { // Declara método público para atualizar lista
|
||||||
|
this.expenseList = newExpenseList; // Atualiza a lista de despesas com nova lista
|
||||||
|
notifyDataSetChanged(); // Notifica o RecyclerView para atualizar a interface de todos os itens
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExpenseViewHolder - Classe interna que mantém referências para as views de cada item
|
||||||
|
*
|
||||||
|
* Esta classe é responsável por:
|
||||||
|
* - Manter referências para todos os elementos visuais do item
|
||||||
|
* - Facilitar o acesso às views durante o binding
|
||||||
|
* - Otimizar a performance do RecyclerView
|
||||||
|
*/
|
||||||
|
public static class ExpenseViewHolder extends RecyclerView.ViewHolder { // Declara classe interna estática que estende RecyclerView.ViewHolder
|
||||||
|
// Views de texto para exibir os dados da despesa
|
||||||
|
TextView tvDescription, tvAmount, tvCategory, tvDate, tvNotes; // Declara TextViews para exibir dados da despesa
|
||||||
|
|
||||||
|
// Botões de ação para editar e excluir
|
||||||
|
MaterialButton btnEdit, btnDelete; // Declara botões Material Design para ações
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor do ViewHolder
|
||||||
|
* @param itemView View raiz do item (inflada do layout item_expense.xml)
|
||||||
|
*/
|
||||||
|
public ExpenseViewHolder(@NonNull View itemView) { // Declara construtor público com parâmetro não nulo
|
||||||
|
super(itemView); // Chama construtor da classe pai
|
||||||
|
|
||||||
|
// Conecta as views do layout com as variáveis
|
||||||
|
tvDescription = itemView.findViewById(R.id.tvDescription); // Busca TextView de descrição por ID e atribui à variável
|
||||||
|
tvAmount = itemView.findViewById(R.id.tvAmount); // Busca TextView de valor por ID e atribui à variável
|
||||||
|
tvCategory = itemView.findViewById(R.id.tvCategory); // Busca TextView de categoria por ID e atribui à variável
|
||||||
|
tvDate = itemView.findViewById(R.id.tvDate); // Busca TextView de data por ID e atribui à variável
|
||||||
|
tvNotes = itemView.findViewById(R.id.tvNotes); // Busca TextView de notas por ID e atribui à variável
|
||||||
|
|
||||||
|
// Conecta os botões de ação
|
||||||
|
btnEdit = itemView.findViewById(R.id.btnEdit); // Busca botão editar por ID e atribui à variável
|
||||||
|
btnDelete = itemView.findViewById(R.id.btnDelete); // Busca botão excluir por ID e atribui à variável
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,283 @@
|
||||||
|
package pt.epvc.gestodedespesas; // Pacote da aplicação
|
||||||
|
|
||||||
|
import android.content.Intent; // Para navegar entre Activities
|
||||||
|
import android.os.Bundle; // Estado da Activity
|
||||||
|
import android.view.View; // Base para listeners de clique
|
||||||
|
import android.widget.TextView; // Exibe textos na UI
|
||||||
|
import android.widget.Toast; // Mostra mensagens curtas
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge; // Suporte a layout de ponta a ponta
|
||||||
|
import androidx.appcompat.app.AlertDialog; // Diálogo de opções/confirmar
|
||||||
|
import androidx.appcompat.app.AppCompatActivity; // Base para Activities
|
||||||
|
import androidx.core.graphics.Insets; // Dimensões de barras do sistema
|
||||||
|
import androidx.core.view.ViewCompat; // Utilidades de view
|
||||||
|
import androidx.core.view.WindowInsetsCompat; // Insets de janela
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager; // Layout vertical
|
||||||
|
import androidx.recyclerview.widget.RecyclerView; // Lista eficiente
|
||||||
|
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton; // Botão flutuante
|
||||||
|
import com.google.android.material.button.MaterialButton; // Botão Material
|
||||||
|
|
||||||
|
import java.text.DecimalFormat; // Formatar valores monetários
|
||||||
|
import java.util.List; // Estrutura de lista
|
||||||
|
|
||||||
|
import pt.epvc.gestodedespesas.data.DatabaseHelper; // Acesso ao SQLite movido para pacote data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExpensesListActivity - Tela avançada de listagem e análise de despesas
|
||||||
|
* - Exibe total, contagem e média das despesas
|
||||||
|
* - Permite filtrar por categoria
|
||||||
|
* - Possibilita adicionar, editar e excluir despesas
|
||||||
|
* - Usa Edge-to-Edge e ajusta padding conforme barras do sistema
|
||||||
|
*/
|
||||||
|
public class ExpensesListActivity extends AppCompatActivity implements ExpenseAdapter.OnExpenseClickListener { // Activity principal da lista
|
||||||
|
|
||||||
|
private RecyclerView recyclerViewExpenses; // Lista de despesas
|
||||||
|
private TextView tvTotal, tvExpenseCount, tvAverage; // Indicadores de total, contagem e média
|
||||||
|
private FloatingActionButton fabAddExpense; // Botão flutuante para adicionar
|
||||||
|
private MaterialButton btnFilter; // Botão para filtrar por categoria
|
||||||
|
private DatabaseHelper databaseHelper; // Acesso ao SQLite
|
||||||
|
private ExpenseAdapter expenseAdapter; // Adapter do RecyclerView
|
||||||
|
private List<Expense> expenseList; // Dados das despesas
|
||||||
|
private DecimalFormat currencyFormat = new DecimalFormat("€#,##0.00"); // Formatação em euro
|
||||||
|
private boolean isShowingEmptyState = false; // Controle de qual layout está visível (lista ou estado vazio)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onCreate: configura layout, Edge-to-Edge e listeners de insets, inicializa
|
||||||
|
* dependências (DB/Views/RecyclerView) e carrega dados iniciais.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) { // Ciclo de vida: criação da Activity
|
||||||
|
super.onCreate(savedInstanceState); // Chama implementação base
|
||||||
|
EdgeToEdge.enable(this); // Ativa layout de ponta a ponta
|
||||||
|
databaseHelper = new DatabaseHelper(this); // Instancia acesso ao DB (SQLiteHelper)
|
||||||
|
renderLayoutByData(); // Decide e aplica o layout conforme existência de despesas
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Faz o bind das views usadas nesta Activity via findViewById.
|
||||||
|
*/
|
||||||
|
private void initializeViews() {
|
||||||
|
try {
|
||||||
|
recyclerViewExpenses = findViewById(R.id.recyclerViewExpenses); // RecyclerView da lista
|
||||||
|
tvTotal = findViewById(R.id.tvTotal); // Texto do total gasto
|
||||||
|
tvExpenseCount = findViewById(R.id.tvExpenseCount); // Texto da contagem
|
||||||
|
tvAverage = findViewById(R.id.tvAverage); // Texto da média
|
||||||
|
fabAddExpense = findViewById(R.id.fabAddExpense); // FAB adicionar
|
||||||
|
btnFilter = findViewById(R.id.btnFilter); // Botão filtrar
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Erro ao inicializar views: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configura o RecyclerView com LayoutManager linear e o adapter
|
||||||
|
* preenchido com os dados do banco.
|
||||||
|
*/
|
||||||
|
private void setupRecyclerView() {
|
||||||
|
try {
|
||||||
|
if (recyclerViewExpenses != null) { // Garante que a view existe
|
||||||
|
expenseList = databaseHelper.getAllExpenses(); // Busca dados no DB
|
||||||
|
expenseAdapter = new ExpenseAdapter(expenseList, this); // Cria adapter
|
||||||
|
recyclerViewExpenses.setLayoutManager(new LinearLayoutManager(this)); // Layout vertical
|
||||||
|
recyclerViewExpenses.setAdapter(expenseAdapter); // Liga adapter
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Erro ao configurar RecyclerView: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registra os listeners de clique do FAB (adicionar) e do botão de filtro.
|
||||||
|
*/
|
||||||
|
private void setupClickListeners() {
|
||||||
|
try {
|
||||||
|
if (fabAddExpense != null) { // Verifica se a view foi encontrada
|
||||||
|
fabAddExpense.setOnClickListener(new View.OnClickListener() { // Clique do FAB
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) { // Ação ao clicar
|
||||||
|
Intent intent = new Intent(ExpensesListActivity.this, AddExpenseActivity.class); // Ir para adicionar
|
||||||
|
startActivityForResult(intent, 1); // Abrir aguardando resultado
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnFilter != null) { // Verifica existência do botão
|
||||||
|
btnFilter.setOnClickListener(new View.OnClickListener() { // Clique do filtro
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) { // Ação ao clicar
|
||||||
|
showFilterDialog(); // Abre diálogo de categorias
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Erro ao configurar click listeners: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carrega/recarrega a lista de despesas do banco, atualiza o adapter
|
||||||
|
* e recalcula os totais exibidos.
|
||||||
|
*/
|
||||||
|
private void loadExpenses() {
|
||||||
|
try {
|
||||||
|
expenseList = databaseHelper.getAllExpenses(); // Puxa despesas do DB
|
||||||
|
boolean hasData = expenseList != null && !expenseList.isEmpty();
|
||||||
|
if (!hasData) {
|
||||||
|
if (!isShowingEmptyState) {
|
||||||
|
renderEmptyLayout();
|
||||||
|
}
|
||||||
|
return; // Nada para atualizar no layout vazio
|
||||||
|
}
|
||||||
|
if (isShowingEmptyState) {
|
||||||
|
renderMainLayout();
|
||||||
|
}
|
||||||
|
if (expenseAdapter != null) { // Garante adapter existente
|
||||||
|
expenseAdapter.updateExpenses(expenseList); // Atualiza lista na UI
|
||||||
|
}
|
||||||
|
updateTotal(); // Recalcula indicadores
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Erro ao carregar despesas: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalcula e exibe total gasto, número de despesas e média por despesa.
|
||||||
|
*/
|
||||||
|
private void updateTotal() {
|
||||||
|
try {
|
||||||
|
double total = databaseHelper.getTotalExpenses(); // Soma dos valores
|
||||||
|
int count = expenseList != null ? expenseList.size() : 0; // Quantidade de itens
|
||||||
|
double average = count > 0 ? total / count : 0; // Média simples
|
||||||
|
|
||||||
|
if (tvTotal != null) { // Atualiza total
|
||||||
|
tvTotal.setText(currencyFormat.format(total)); // Exibe em €
|
||||||
|
}
|
||||||
|
if (tvExpenseCount != null) { // Atualiza contagem
|
||||||
|
tvExpenseCount.setText(String.valueOf(count)); // Converte para texto
|
||||||
|
}
|
||||||
|
if (tvAverage != null) { // Atualiza média
|
||||||
|
tvAverage.setText(currencyFormat.format(average)); // Exibe em €
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Erro ao atualizar totais: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Feedback de erro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exibe diálogo para seleção de categoria e aplica o filtro na lista.
|
||||||
|
*/
|
||||||
|
private void showFilterDialog() {
|
||||||
|
String[] categories = {"Todas", "Alimentação", "Transporte", "Entretenimento", "Saúde", "Compras", "Outros"}; // Opções
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this) // Constrói diálogo
|
||||||
|
.setTitle("🔍 Filtrar Despesas") // Título
|
||||||
|
.setItems(categories, (dialog, which) -> { // Lista de categorias
|
||||||
|
if (which == 0) { // Opção "Todas"
|
||||||
|
loadExpenses(); // Recarrega lista completa
|
||||||
|
} else { // Categoria específica
|
||||||
|
String category = categories[which]; // Nome da categoria
|
||||||
|
expenseList = databaseHelper.getExpensesByCategory(category); // Busca filtrada
|
||||||
|
if (expenseList == null || expenseList.isEmpty()) {
|
||||||
|
renderEmptyLayout();
|
||||||
|
} else {
|
||||||
|
if (isShowingEmptyState) {
|
||||||
|
renderMainLayout();
|
||||||
|
}
|
||||||
|
expenseAdapter.updateExpenses(expenseList); // Atualiza adapter
|
||||||
|
updateTotal(); // Recalcula totais com filtro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show(); // Exibe o diálogo
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recebe resultado de AddExpenseActivity. Recarrega lista quando sucesso.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { // Callback de retorno
|
||||||
|
super.onActivityResult(requestCode, resultCode, data); // Chamada base
|
||||||
|
if (requestCode == 1 && resultCode == RESULT_OK) { // Verifica sucesso
|
||||||
|
loadExpenses(); // Recarrega lista
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abre AddExpenseActivity para edição de um item selecionado, enviando-o via Intent.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onEditClick(Expense expense) { // Clique em editar no item
|
||||||
|
Intent intent = new Intent(this, AddExpenseActivity.class); // Abre tela de edição
|
||||||
|
intent.putExtra("expense", expense); // Envia objeto selecionado
|
||||||
|
startActivityForResult(intent, 1); // Aguarda resultado
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pede confirmação e, se positivo, exclui a despesa, recarregando a lista.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onDeleteClick(Expense expense) { // Clique em excluir no item
|
||||||
|
new AlertDialog.Builder(this) // Diálogo de confirmação
|
||||||
|
.setTitle("Confirmar Exclusão") // Título
|
||||||
|
.setMessage("Tem certeza que deseja excluir esta despesa?") // Mensagem
|
||||||
|
.setPositiveButton("Sim", (dialog, which) -> { // Confirma
|
||||||
|
databaseHelper.deleteExpense(expense.getId()); // Remove do DB
|
||||||
|
loadExpenses(); // Recarrega a lista
|
||||||
|
Toast.makeText(this, "Despesa excluída com sucesso!", Toast.LENGTH_SHORT).show(); // Feedback
|
||||||
|
})
|
||||||
|
.setNegativeButton("Não", null) // Cancela
|
||||||
|
.show(); // Exibe diálogo
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide qual layout usar com base nos dados atuais do DB.
|
||||||
|
*/
|
||||||
|
private void renderLayoutByData() { // Decide qual layout mostrar com base nos dados
|
||||||
|
try {
|
||||||
|
List<Expense> initial = databaseHelper.getAllExpenses(); // Consulta inicial ao DB
|
||||||
|
if (initial == null || initial.isEmpty()) { // Sem dados?
|
||||||
|
renderEmptyLayout(); // Mostra estado vazio
|
||||||
|
} else { // Há dados
|
||||||
|
expenseList = initial; // Mantém em memória
|
||||||
|
renderMainLayout(); // Mostra lista principal
|
||||||
|
}
|
||||||
|
} catch (Exception e) { // Qualquer erro
|
||||||
|
renderEmptyLayout(); // Fallback: evita crash mostrando estado vazio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mostra o layout principal de lista, inicializa views/adapter e listeners.
|
||||||
|
*/
|
||||||
|
private void renderMainLayout() { // Exibe layout principal de lista
|
||||||
|
setContentView(R.layout.activity_expenses_list); // Aplica XML de lista
|
||||||
|
isShowingEmptyState = false; // Marca que a lista está ativa
|
||||||
|
try {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { // Listener de insets
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); // Áreas das barras
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); // Padding seguro
|
||||||
|
return insets; // Continua propagação
|
||||||
|
});
|
||||||
|
} catch (Exception ignored) { } // Se não existir view com id main, ignora
|
||||||
|
initializeViews(); // Associa views do layout
|
||||||
|
setupRecyclerView(); // Configura RecyclerView e adapter
|
||||||
|
setupClickListeners(); // Registra cliques (FAB e filtro)
|
||||||
|
updateTotal(); // Atualiza totais na UI
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mostra o layout de estado vazio e configura o botão de adicionar.
|
||||||
|
*/
|
||||||
|
private void renderEmptyLayout() { // Exibe layout de estado vazio
|
||||||
|
setContentView(R.layout.empty_state); // Aplica XML vazio
|
||||||
|
isShowingEmptyState = true; // Marca que estado vazio está ativo
|
||||||
|
View add = findViewById(R.id.btnAddFirstExpense); // Botão "Adicionar Despesa"
|
||||||
|
if (add != null) { // Se existir no layout
|
||||||
|
add.setOnClickListener(v -> { // Ao clicar
|
||||||
|
Intent intent = new Intent(ExpensesListActivity.this, AddExpenseActivity.class); // Abre tela de adição
|
||||||
|
startActivityForResult(intent, 1); // Aguarda resultado para recarregar
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package pt.epvc.gestodedespesas; // Define o pacote onde esta classe está localizada
|
||||||
|
|
||||||
|
import android.content.Intent; // Importa classe para navegação entre Activities
|
||||||
|
import android.os.Bundle; // Importa classe para passar dados entre Activities
|
||||||
|
import android.view.View; // Importa classe base para elementos visuais
|
||||||
|
import android.widget.TextView; // Importa classe para exibir texto
|
||||||
|
import android.widget.Toast; // Importa classe para exibir mensagens temporárias
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge; // Importa classe para design edge-to-edge
|
||||||
|
import androidx.appcompat.app.AlertDialog; // Importa classe para diálogos de alerta
|
||||||
|
import androidx.appcompat.app.AppCompatActivity; // Importa classe base para Activities modernas
|
||||||
|
import androidx.core.graphics.Insets; // Importa classe para margens do sistema
|
||||||
|
import androidx.core.view.ViewCompat; // Importa classe para compatibilidade de views
|
||||||
|
import androidx.core.view.WindowInsetsCompat; // Importa classe para insets de janela
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager; // Importa classe para layout linear
|
||||||
|
import androidx.recyclerview.widget.RecyclerView; // Importa classe para listas otimizadas
|
||||||
|
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton; // Importa botão flutuante Material Design
|
||||||
|
import com.google.android.material.button.MaterialButton; // Importa botão Material Design
|
||||||
|
|
||||||
|
import java.text.DecimalFormat; // Importa classe para formatação de números
|
||||||
|
import java.util.List; // Importa interface para listas
|
||||||
|
|
||||||
|
import pt.epvc.gestodedespesas.data.DatabaseHelper; // DatabaseHelper movido para pacote data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MainActivity - Tela Principal da Aplicação de Gestão de Despesas
|
||||||
|
*
|
||||||
|
* Esta é a tela de boas-vindas que serve como dashboard principal da aplicação.
|
||||||
|
* Funcionalidades implementadas:
|
||||||
|
* - Exibe estatísticas rápidas (total gasto e número de despesas)
|
||||||
|
* - Botões para navegar para diferentes funcionalidades
|
||||||
|
* - Interface moderna com Material Design
|
||||||
|
* - Navegação para lista de despesas e formulário de adição
|
||||||
|
*/
|
||||||
|
public class MainActivity extends AppCompatActivity { // Declara classe que estende AppCompatActivity
|
||||||
|
|
||||||
|
// Views da interface - elementos visuais da tela principal
|
||||||
|
private TextView tvQuickTotal, tvQuickCount; // Declara TextViews para exibir estatísticas
|
||||||
|
private FloatingActionButton fabQuickAdd; // Declara botão flutuante para adicionar despesa
|
||||||
|
private MaterialButton btnGetStarted, btnViewExpenses, btnAddExpense; // Declara botões de navegação
|
||||||
|
|
||||||
|
// Objetos para funcionalidade
|
||||||
|
private DatabaseHelper databaseHelper; // Declara helper para operações com SQLite
|
||||||
|
private DecimalFormat currencyFormat = new DecimalFormat("€#,##0.00"); // Cria formato de moeda
|
||||||
|
|
||||||
|
@Override // Sobrescreve método da classe pai
|
||||||
|
protected void onCreate(Bundle savedInstanceState) { // Método chamado quando Activity é criada
|
||||||
|
super.onCreate(savedInstanceState); // Chama método da classe pai
|
||||||
|
EdgeToEdge.enable(this); // Habilita edge-to-edge para design moderno
|
||||||
|
|
||||||
|
// Define o layout da tela principal (activity_main.xml)
|
||||||
|
setContentView(R.layout.activity_main); // Conecta layout XML com esta Activity
|
||||||
|
|
||||||
|
// Configura insets para adaptar ao sistema (status bar, navigation bar)
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { // Define listener para insets
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); // Obtém insets das barras do sistema
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); // Aplica padding baseado nos insets
|
||||||
|
return insets; // Retorna os insets processados
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inicializa o helper do banco de dados SQLite
|
||||||
|
databaseHelper = new DatabaseHelper(this); // Cria nova instância do DatabaseHelper
|
||||||
|
|
||||||
|
// Configura a interface e funcionalidades
|
||||||
|
initializeViews(); // Chama método para conectar views com variáveis
|
||||||
|
setupClickListeners(); // Chama método para configurar eventos de clique
|
||||||
|
updateQuickStats(); // Chama método para atualizar estatísticas exibidas
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicializa as views conectando os elementos do layout com as variáveis Java
|
||||||
|
* Este método é chamado no onCreate para preparar a interface
|
||||||
|
*/
|
||||||
|
private void initializeViews() { // Declara método privado para inicializar views
|
||||||
|
// Conecta os TextViews para exibir estatísticas
|
||||||
|
tvQuickTotal = findViewById(R.id.tvQuickTotal); // Busca TextView por ID e atribui à variável
|
||||||
|
tvQuickCount = findViewById(R.id.tvQuickCount); // Busca TextView por ID e atribui à variável
|
||||||
|
|
||||||
|
// Conecta os botões de ação
|
||||||
|
fabQuickAdd = findViewById(R.id.fabQuickAdd); // Busca FloatingActionButton por ID e atribui à variável
|
||||||
|
btnGetStarted = findViewById(R.id.btnGetStarted); // Busca MaterialButton por ID e atribui à variável
|
||||||
|
btnViewExpenses = findViewById(R.id.btnViewExpenses); // Busca MaterialButton por ID e atribui à variável
|
||||||
|
btnAddExpense = findViewById(R.id.btnAddExpense); // Busca MaterialButton por ID e atribui à variável
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configura os eventos de clique para todos os botões da interface
|
||||||
|
* Cada botão navega para uma funcionalidade específica da aplicação
|
||||||
|
*/
|
||||||
|
private void setupClickListeners() { // Declara método privado para configurar listeners
|
||||||
|
// Botão flutuante para adicionar despesa rapidamente
|
||||||
|
fabQuickAdd.setOnClickListener(new View.OnClickListener() { // Define listener de clique para o botão flutuante
|
||||||
|
@Override // Sobrescreve método da interface
|
||||||
|
public void onClick(View v) { // Método chamado quando botão é clicado
|
||||||
|
// Navega para a tela de adicionar despesa
|
||||||
|
Intent intent = new Intent(MainActivity.this, AddExpenseActivity.class); // Cria Intent para navegar para AddExpenseActivity
|
||||||
|
startActivityForResult(intent, 1); // Inicia Activity esperando resultado com código 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Botão "Começar Agora" - leva direto para adicionar primeira despesa
|
||||||
|
btnGetStarted.setOnClickListener(new View.OnClickListener() { // Define listener de clique para o botão "Começar Agora"
|
||||||
|
@Override // Sobrescreve método da interface
|
||||||
|
public void onClick(View v) { // Método chamado quando botão é clicado
|
||||||
|
Intent intent = new Intent(MainActivity.this, AddExpenseActivity.class); // Cria Intent para navegar para AddExpenseActivity
|
||||||
|
startActivityForResult(intent, 1); // Inicia Activity esperando resultado com código 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Botão "Ver Despesas" - navega para a lista completa de despesas
|
||||||
|
btnViewExpenses.setOnClickListener(new View.OnClickListener() { // Define listener de clique para o botão "Ver Despesas"
|
||||||
|
@Override // Sobrescreve método da interface
|
||||||
|
public void onClick(View v) { // Método chamado quando botão é clicado
|
||||||
|
// Navega para a tela simplificada de lista de despesas
|
||||||
|
Intent intent = new Intent(MainActivity.this, SimpleExpensesActivity.class); // Cria Intent para navegar para SimpleExpensesActivity
|
||||||
|
startActivity(intent); // Inicia Activity sem esperar resultado
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Botão "Adicionar" - também leva para o formulário de adicionar despesa
|
||||||
|
btnAddExpense.setOnClickListener(new View.OnClickListener() { // Define listener de clique para o botão "Adicionar"
|
||||||
|
@Override // Sobrescreve método da interface
|
||||||
|
public void onClick(View v) { // Método chamado quando botão é clicado
|
||||||
|
Intent intent = new Intent(MainActivity.this, AddExpenseActivity.class); // Cria Intent para navegar para AddExpenseActivity
|
||||||
|
startActivityForResult(intent, 1); // Inicia Activity esperando resultado com código 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atualiza as estatísticas rápidas exibidas na tela principal
|
||||||
|
* Busca dados do banco SQLite e atualiza os TextViews
|
||||||
|
*/
|
||||||
|
private void updateQuickStats() { // Declara método privado para atualizar estatísticas
|
||||||
|
// Busca o total gasto de todas as despesas
|
||||||
|
double total = databaseHelper.getTotalExpenses(); // Chama método do DatabaseHelper para obter total
|
||||||
|
|
||||||
|
// Busca todas as despesas para contar
|
||||||
|
List<Expense> expenses = databaseHelper.getAllExpenses(); // Chama método do DatabaseHelper para obter todas as despesas
|
||||||
|
int count = expenses.size(); // Obtém o tamanho da lista (número de despesas)
|
||||||
|
|
||||||
|
// Atualiza a interface com os valores formatados
|
||||||
|
tvQuickTotal.setText(currencyFormat.format(total)); // Define texto do TextView com total formatado em moeda
|
||||||
|
tvQuickCount.setText(String.valueOf(count)); // Define texto do TextView com número de despesas convertido para string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Método chamado quando retorna de outra Activity
|
||||||
|
* Se uma despesa foi adicionada/editada, atualiza as estatísticas
|
||||||
|
*/
|
||||||
|
@Override // Sobrescreve método da classe pai
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { // Método chamado quando Activity retorna resultado
|
||||||
|
super.onActivityResult(requestCode, resultCode, data); // Chama método da classe pai
|
||||||
|
if (requestCode == 1 && resultCode == RESULT_OK) { // Verifica se é o resultado esperado (código 1) e se foi bem-sucedido
|
||||||
|
// Se uma despesa foi adicionada/editada com sucesso, atualiza as estatísticas
|
||||||
|
updateQuickStats(); // Chama método para atualizar estatísticas na tela
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
package pt.epvc.gestodedespesas; // Este arquivo faz parte do projeto "Gestão de Despesas"
|
||||||
|
|
||||||
|
import android.content.Intent; // Usado para abrir outra tela
|
||||||
|
import android.os.Bundle; // Guarda informações quando a tela abre
|
||||||
|
import android.view.View; // Necessário para saber quando algo foi clicado
|
||||||
|
import android.widget.TextView; // Mostra textos na tela
|
||||||
|
import android.widget.Toast; // Mostra avisos rápidos na parte de baixo da tela
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity; // Tipo de tela do Android
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager; // Organiza a lista de cima para baixo
|
||||||
|
import androidx.recyclerview.widget.RecyclerView; // Componente que mostra a lista de despesas
|
||||||
|
|
||||||
|
import com.google.android.material.button.MaterialButton; // Botões bonitos (Material Design)
|
||||||
|
|
||||||
|
import java.text.DecimalFormat; // Deixa valores com cara de dinheiro (ex: €12,34)
|
||||||
|
import java.util.List; // Lista de coisas (neste caso, despesas)
|
||||||
|
|
||||||
|
import pt.epvc.gestodedespesas.data.DatabaseHelper; // Acesso ao banco movido para o pacote data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* O que esta tela faz (em linguagem simples):
|
||||||
|
* - Mostra uma lista com as suas despesas
|
||||||
|
* - Mostra em cima quanto você já gastou no total
|
||||||
|
* - Tem um botão para adicionar uma nova despesa
|
||||||
|
* - Tem um botão para voltar para a tela anterior
|
||||||
|
* - Você pode tocar em uma despesa para editar ou apagar
|
||||||
|
*/
|
||||||
|
public class SimpleExpensesActivity extends AppCompatActivity implements ExpenseAdapter.OnExpenseClickListener { // Activity simples de lista
|
||||||
|
|
||||||
|
private static final int REQUEST_ADD_EDIT = 1; // Código de requisição para identificar retorno de Add/Edit
|
||||||
|
|
||||||
|
// Itens que aparecem na tela (componentes visuais)
|
||||||
|
private RecyclerView recyclerViewExpenses; // RecyclerView para exibir as despesas
|
||||||
|
private TextView tvTotal; // TextView do valor total
|
||||||
|
private MaterialButton btnAddExpense, btnBack; // Botões adicionar e voltar
|
||||||
|
|
||||||
|
// Parte "por trás das câmeras" (código que faz as coisas funcionarem)
|
||||||
|
private DatabaseHelper databaseHelper; // Fala com o banco de dados do celular
|
||||||
|
private ExpenseAdapter expenseAdapter; // Encaixa cada despesa direitinho na lista
|
||||||
|
private List<Expense> expenseList; // Onde as despesas ficam guardadas na memória
|
||||||
|
private DecimalFormat currencyFormat = new DecimalFormat("€#,##0.00"); // Deixa o número no formato de euro
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quando esta tela abre:
|
||||||
|
* - Mostramos o desenho da tela (layout)
|
||||||
|
* - Preparamos o acesso ao banco de dados
|
||||||
|
* - Ligamos os botões e a lista
|
||||||
|
* - Buscamos as despesas já salvas e atualizamos o total gasto
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) { // Ponto de criação da Activity
|
||||||
|
super.onCreate(savedInstanceState); // Chamada da superclasse
|
||||||
|
setContentView(R.layout.activity_simple_expenses); // Aplica o layout simples
|
||||||
|
|
||||||
|
databaseHelper = new DatabaseHelper(this); // Instancia o helper do DB
|
||||||
|
initializeViews(); // Liga views do layout
|
||||||
|
setupRecyclerView(); // Configura a lista
|
||||||
|
setupClickListeners(); // Registra cliques
|
||||||
|
loadExpenses(); // Carrega dados iniciais
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Faz o bind das views declaradas com os componentes do layout via findViewById.
|
||||||
|
*/
|
||||||
|
private void initializeViews() { // Encontramos na tela cada componente que vamos usar
|
||||||
|
recyclerViewExpenses = findViewById(R.id.recyclerViewExpenses); // A lista onde aparecem as despesas
|
||||||
|
tvTotal = findViewById(R.id.tvTotal); // O texto que mostra o valor total gasto
|
||||||
|
btnAddExpense = findViewById(R.id.btnAddExpense); // Botão para adicionar uma nova despesa
|
||||||
|
btnBack = findViewById(R.id.btnBack); // Botão para voltar para a tela anterior
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configura o RecyclerView:
|
||||||
|
* - Busca a lista de despesas no banco de dados
|
||||||
|
* - Cria o adapter e define o LayoutManager linear
|
||||||
|
* - Conecta o adapter ao RecyclerView
|
||||||
|
*/
|
||||||
|
private void setupRecyclerView() { // Preparamos a lista para aparecer bonitinha
|
||||||
|
try {
|
||||||
|
expenseList = databaseHelper.getAllExpenses(); // Pegamos todas as despesas que já existem
|
||||||
|
expenseAdapter = new ExpenseAdapter(expenseList, this); // Dizemos como cada item da lista deve ser mostrado
|
||||||
|
recyclerViewExpenses.setLayoutManager(new LinearLayoutManager(this)); // A lista vai de cima para baixo
|
||||||
|
recyclerViewExpenses.setAdapter(expenseAdapter); // Colocamos a lista para aparecer na tela
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Tivemos um problema ao mostrar a lista: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Mostra um aviso se algo der errado
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registra os listeners de clique dos botões:
|
||||||
|
* - Adicionar: abre a tela de cadastro/edição de despesa
|
||||||
|
* - Voltar: finaliza a Activity atual
|
||||||
|
*/
|
||||||
|
private void setupClickListeners() { // Dizemos o que acontece quando os botões são clicados
|
||||||
|
btnAddExpense.setOnClickListener(new View.OnClickListener() { // Quando tocar em "Adicionar"
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) { // Ao clicar neste botão
|
||||||
|
Intent intent = new Intent(SimpleExpensesActivity.this, AddExpenseActivity.class); // Vamos para a tela de adicionar despesa
|
||||||
|
startActivityForResult(intent, REQUEST_ADD_EDIT); // Depois que terminar, voltamos aqui com o resultado
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnBack.setOnClickListener(new View.OnClickListener() { // Quando tocar em "Voltar"
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) { // Ao clicar neste botão
|
||||||
|
finish(); // Fechamos esta tela e voltamos para a anterior
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carrega/recarrega a lista de despesas do banco e atualiza o adapter.
|
||||||
|
* Em seguida, recalcula o total gasto.
|
||||||
|
*/
|
||||||
|
private void loadExpenses() { // Busca de novo as despesas para manter a lista atualizada
|
||||||
|
try {
|
||||||
|
expenseList = databaseHelper.getAllExpenses(); // Buscamos as despesas salvas no celular
|
||||||
|
if (expenseAdapter != null) { // Se a lista já está pronta
|
||||||
|
expenseAdapter.updateExpenses(expenseList); // Atualizamos a lista que aparece na tela
|
||||||
|
}
|
||||||
|
updateTotal(); // Atualizamos o valor total mostrado em cima
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Tivemos um problema ao carregar as despesas: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Aviso de problema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcula o total de despesas no banco e atualiza o TextView com formatação em euros.
|
||||||
|
*/
|
||||||
|
private void updateTotal() { // Calcula quanto foi gasto no total e mostra na tela
|
||||||
|
try {
|
||||||
|
double total = databaseHelper.getTotalExpenses(); // Somamos os valores de todas as despesas
|
||||||
|
if (tvTotal != null) { // Se o texto do total existe na tela
|
||||||
|
tvTotal.setText(currencyFormat.format(total)); // Mostramos o total com símbolo de euro
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Não conseguimos atualizar o total agora: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Aviso de problema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recebe o retorno da AddExpenseActivity (adicionar/editar).
|
||||||
|
* Se o resultado for OK, recarrega a lista.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { // Quando voltamos da tela de adicionar/editar
|
||||||
|
super.onActivityResult(requestCode, resultCode, data); // Mantém o funcionamento padrão
|
||||||
|
if (requestCode == REQUEST_ADD_EDIT && resultCode == RESULT_OK) { // Deu tudo certo por lá?
|
||||||
|
loadExpenses(); // Atualizamos a lista aqui também
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chamado quando o usuário clica para editar uma despesa no item da lista.
|
||||||
|
* Abre a AddExpenseActivity enviando o objeto Expense via Intent.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onEditClick(Expense expense) { // Quando tocar para editar uma despesa
|
||||||
|
Intent intent = new Intent(this, AddExpenseActivity.class); // Abrimos a tela de edição
|
||||||
|
intent.putExtra("expense", expense); // Levamos as informações da despesa para lá
|
||||||
|
startActivityForResult(intent, REQUEST_ADD_EDIT); // Depois voltamos com o resultado
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chamado quando o usuário solicita a exclusão de uma despesa.
|
||||||
|
* Remove do banco, recarrega a lista e informa via Toast.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onDeleteClick(Expense expense) { // Quando tocar para apagar uma despesa
|
||||||
|
try {
|
||||||
|
databaseHelper.deleteExpense(expense.getId()); // Apagamos do banco de dados
|
||||||
|
loadExpenses(); // Atualizamos a lista na tela
|
||||||
|
Toast.makeText(this, "Despesa excluída com sucesso!", Toast.LENGTH_SHORT).show(); // Mostramos uma mensagem de sucesso
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Não conseguimos apagar agora: " + e.getMessage(), Toast.LENGTH_LONG).show(); // Aviso de problema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
package pt.epvc.gestodedespesas.data; // Esta pasta (pacote) guarda tudo que conversa com o banco de dados
|
||||||
|
|
||||||
|
import android.content.ContentValues; // Usado para montar os dados antes de salvar
|
||||||
|
import android.content.Context; // Precisamos do contexto para abrir/criar o banco
|
||||||
|
import android.database.Cursor; // Onde ficam os resultados de uma consulta
|
||||||
|
import android.database.sqlite.SQLiteDatabase; // Objeto que permite ler/escrever no banco
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper; // Classe que ajuda a criar e atualizar o banco
|
||||||
|
|
||||||
|
import java.util.ArrayList; // Lista de tamanho variável
|
||||||
|
import java.util.List; // Tipo "lista" em Java
|
||||||
|
|
||||||
|
import pt.epvc.gestodedespesas.Expense; // Nossa classe que representa uma despesa
|
||||||
|
|
||||||
|
/**
|
||||||
|
* O que esta classe faz (em linguagem simples):
|
||||||
|
* - Cria o arquivo do banco de dados no celular, caso não exista
|
||||||
|
* - Cria a tabela de despesas na primeira vez
|
||||||
|
* - Salva, busca, atualiza e apaga despesas no banco
|
||||||
|
* - Também consegue somar o total gasto e filtrar por categoria
|
||||||
|
*/
|
||||||
|
public class DatabaseHelper extends SQLiteOpenHelper { // Helper do SQLite
|
||||||
|
private static final String DATABASE_NAME = "expenses.db"; // Nome do arquivo do banco no aparelho
|
||||||
|
private static final int DATABASE_VERSION = 1; // Versão do banco (muda quando a estrutura mudar)
|
||||||
|
|
||||||
|
// Nome da tabela e das colunas (os "campos" da tabela)
|
||||||
|
private static final String TABLE_EXPENSES = "expenses";
|
||||||
|
private static final String COLUMN_ID = "id";
|
||||||
|
private static final String COLUMN_DESCRIPTION = "description";
|
||||||
|
private static final String COLUMN_AMOUNT = "amount";
|
||||||
|
private static final String COLUMN_CATEGORY = "category";
|
||||||
|
private static final String COLUMN_DATE = "date";
|
||||||
|
private static final String COLUMN_NOTES = "notes";
|
||||||
|
|
||||||
|
// Comando que cria a tabela de despesas (rodado somente na primeira vez)
|
||||||
|
private static final String CREATE_TABLE_EXPENSES =
|
||||||
|
"CREATE TABLE " + TABLE_EXPENSES + "(" +
|
||||||
|
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||||
|
COLUMN_DESCRIPTION + " TEXT NOT NULL," +
|
||||||
|
COLUMN_AMOUNT + " REAL NOT NULL," +
|
||||||
|
COLUMN_CATEGORY + " TEXT NOT NULL," +
|
||||||
|
COLUMN_DATE + " TEXT NOT NULL," +
|
||||||
|
COLUMN_NOTES + " TEXT" +
|
||||||
|
")";
|
||||||
|
|
||||||
|
public DatabaseHelper(Context context) { // Chamado quando precisamos usar o banco
|
||||||
|
super(context, DATABASE_NAME, null, DATABASE_VERSION); // Diz o nome e a versão do banco
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) { // Roda só na primeira instalação/uso
|
||||||
|
db.execSQL(CREATE_TABLE_EXPENSES); // Cria a tabela de despesas
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // Se a versão do banco aumentar
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_EXPENSES); // Apaga a tabela antiga
|
||||||
|
onCreate(db); // Cria de novo com a estrutura atualizada
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRUD = Criar, Ler, Atualizar e Deletar
|
||||||
|
public long addExpense(Expense expense) { // Salva uma nova despesa
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase(); // Abre o banco para escrita
|
||||||
|
ContentValues values = new ContentValues(); // Monta o "pacotinho" de dados
|
||||||
|
values.put(COLUMN_DESCRIPTION, expense.getDescription()); // Descrição
|
||||||
|
values.put(COLUMN_AMOUNT, expense.getAmount()); // Valor
|
||||||
|
values.put(COLUMN_CATEGORY, expense.getCategory()); // Categoria
|
||||||
|
values.put(COLUMN_DATE, expense.getDate()); // Data
|
||||||
|
values.put(COLUMN_NOTES, expense.getNotes()); // Notas
|
||||||
|
long id = db.insert(TABLE_EXPENSES, null, values); // Insere na tabela e devolve o id criado
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
return id; // Retorna o id da nova despesa
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expense getExpense(int id) { // Busca uma despesa específica pelo id
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase(); // Abre o banco para leitura
|
||||||
|
Cursor cursor = db.query(
|
||||||
|
TABLE_EXPENSES,
|
||||||
|
new String[]{COLUMN_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_CATEGORY, COLUMN_DATE, COLUMN_NOTES},
|
||||||
|
COLUMN_ID + "=?",
|
||||||
|
new String[]{String.valueOf(id)}, null, null, null, null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) { // Se achou o registro
|
||||||
|
Expense expense = new Expense( // Monta um objeto Expense com os dados
|
||||||
|
cursor.getInt(0),
|
||||||
|
cursor.getString(1),
|
||||||
|
cursor.getDouble(2),
|
||||||
|
cursor.getString(3),
|
||||||
|
cursor.getString(4),
|
||||||
|
cursor.getString(5)
|
||||||
|
);
|
||||||
|
cursor.close(); // Fecha o resultado
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
return expense;
|
||||||
|
}
|
||||||
|
if (cursor != null) cursor.close(); // Se não achou, só fecha o cursor
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
return null; // Diz que não encontrou
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Expense> getAllExpenses() { // Busca todas as despesas, mais recentes primeiro
|
||||||
|
List<Expense> expenseList = new ArrayList<>(); // Cria uma lista vazia
|
||||||
|
String selectQuery = "SELECT * FROM " + TABLE_EXPENSES + " ORDER BY " + COLUMN_DATE + " DESC"; // Consulta
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase(); // Abre o banco
|
||||||
|
Cursor cursor = db.rawQuery(selectQuery, null); // Executa a consulta
|
||||||
|
|
||||||
|
if (cursor.moveToFirst()) { // Se tem pelo menos uma linha
|
||||||
|
do { // Repete para cada linha
|
||||||
|
Expense expense = new Expense(); // Cria o objeto da despesa
|
||||||
|
expense.setId(cursor.getInt(0)); // Pega o id
|
||||||
|
expense.setDescription(cursor.getString(1)); // Pega a descrição
|
||||||
|
expense.setAmount(cursor.getDouble(2)); // Pega o valor
|
||||||
|
expense.setCategory(cursor.getString(3)); // Pega a categoria
|
||||||
|
expense.setDate(cursor.getString(4)); // Pega a data
|
||||||
|
expense.setNotes(cursor.getString(5)); // Pega as notas
|
||||||
|
expenseList.add(expense); // Coloca na lista
|
||||||
|
} while (cursor.moveToNext()); // Vai para a próxima
|
||||||
|
}
|
||||||
|
cursor.close(); // Fecha o resultado
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
return expenseList; // Devolve a lista pronta
|
||||||
|
}
|
||||||
|
|
||||||
|
public int updateExpense(Expense expense) { // Atualiza uma despesa que já existe
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase(); // Abre para escrita
|
||||||
|
ContentValues values = new ContentValues(); // Novos valores
|
||||||
|
values.put(COLUMN_DESCRIPTION, expense.getDescription());
|
||||||
|
values.put(COLUMN_AMOUNT, expense.getAmount());
|
||||||
|
values.put(COLUMN_CATEGORY, expense.getCategory());
|
||||||
|
values.put(COLUMN_DATE, expense.getDate());
|
||||||
|
values.put(COLUMN_NOTES, expense.getNotes());
|
||||||
|
int result = db.update(TABLE_EXPENSES, values, COLUMN_ID + " = ?", new String[]{String.valueOf(expense.getId())}); // Atualiza onde id = ...
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
return result; // Diz quantas linhas foram alteradas
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteExpense(int id) { // Apaga uma despesa pelo id
|
||||||
|
SQLiteDatabase db = this.getWritableDatabase(); // Abre para escrita
|
||||||
|
db.delete(TABLE_EXPENSES, COLUMN_ID + " = ?", new String[]{String.valueOf(id)}); // Remove a linha do id
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTotalExpenses() { // Soma o valor de todas as despesas
|
||||||
|
String selectQuery = "SELECT SUM(" + COLUMN_AMOUNT + ") FROM " + TABLE_EXPENSES; // Consulta de soma
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase(); // Abre para leitura
|
||||||
|
Cursor cursor = db.rawQuery(selectQuery, null); // Executa
|
||||||
|
double total = 0; // Começa em zero
|
||||||
|
if (cursor.moveToFirst()) { // Se teve resultado
|
||||||
|
total = cursor.getDouble(0); // Pega o valor somado
|
||||||
|
}
|
||||||
|
cursor.close(); // Fecha o resultado
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
return total; // Devolve o total
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Expense> getExpensesByCategory(String category) { // Busca despesas só de uma categoria
|
||||||
|
List<Expense> expenseList = new ArrayList<>(); // Lista vazia
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase(); // Abre para leitura
|
||||||
|
Cursor cursor = db.query( // Faz a consulta com filtro de categoria
|
||||||
|
TABLE_EXPENSES,
|
||||||
|
new String[]{COLUMN_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_CATEGORY, COLUMN_DATE, COLUMN_NOTES},
|
||||||
|
COLUMN_CATEGORY + "=?",
|
||||||
|
new String[]{category}, null, null, COLUMN_DATE + " DESC", null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cursor.moveToFirst()) { // Se achou resultados
|
||||||
|
do { // Percorre cada linha
|
||||||
|
Expense expense = new Expense(); // Cria a despesa
|
||||||
|
expense.setId(cursor.getInt(0)); // id
|
||||||
|
expense.setDescription(cursor.getString(1)); // descrição
|
||||||
|
expense.setAmount(cursor.getDouble(2)); // valor
|
||||||
|
expense.setCategory(cursor.getString(3)); // categoria
|
||||||
|
expense.setDate(cursor.getString(4)); // data
|
||||||
|
expense.setNotes(cursor.getString(5)); // notas
|
||||||
|
expenseList.add(expense); // coloca na lista
|
||||||
|
} while (cursor.moveToNext()); // próxima linha
|
||||||
|
}
|
||||||
|
cursor.close(); // Fecha o resultado
|
||||||
|
db.close(); // Fecha o banco
|
||||||
|
return expenseList; // Devolve lista filtrada
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/accent_color"
|
||||||
|
android:endColor="@color/warning_color"
|
||||||
|
android:angle="90" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/warning_color" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/background_gradient_start"
|
||||||
|
android:endColor="@color/background_gradient_end"
|
||||||
|
android:angle="135" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<scale
|
||||||
|
android:duration="200"
|
||||||
|
android:fromXScale="0.95"
|
||||||
|
android:fromYScale="0.95"
|
||||||
|
android:toXScale="1.0"
|
||||||
|
android:toYScale="1.0"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%" />
|
||||||
|
<alpha
|
||||||
|
android:duration="200"
|
||||||
|
android:fromAlpha="0.0"
|
||||||
|
android:toAlpha="1.0" />
|
||||||
|
</set>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/primary_light" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/primary_color" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/background_light"
|
||||||
|
android:endColor="@color/primary_light"
|
||||||
|
android:angle="45" />
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="@color/primary_light" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/primary_color"
|
||||||
|
android:endColor="@color/primary_dark"
|
||||||
|
android:angle="45" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="@color/white" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="@color/primary_light" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/primary_color" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/primary_light"
|
||||||
|
android:endColor="@color/primary_color"
|
||||||
|
android:angle="45" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="@color/white" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/white">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/white"
|
||||||
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M19,3H18V1H16V3H8V1H6V3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M19,19H5V8H19V19Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M12,2L13.09,8.26L22,9L13.09,9.74L12,16L10.91,9.74L2,9L10.91,8.26L12,2Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/white">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/white"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/white">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/white"
|
||||||
|
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M21,18V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5A2,2 0 0,1 5,3H19A2,2 0 0,1 21,5V6H12C10.89,6 10,6.89 10,8V16A2,2 0 0,0 12,18H21M12,16V8H22V16H12M16,13.5A1.5,1.5 0 0,1 14.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,12A1.5,1.5 0 0,1 16,13.5Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M7.07,11L6,10L4.5,11.5L3,10L1.93,11L3.5,12.5L1.93,14L3,15L4.5,13.5L6,15L7.07,14L5.5,12.5L7.07,11M15,5H7A1,1 0 0,0 6,6V18A1,1 0 0,0 7,19H15A1,1 0 0,0 16,18V6A1,1 0 0,0 15,5M15,18H7V6H15V18Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M10,18H14V16H10V18M3,6V8H21V6H3M6,13H18V11H6V13Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M3,13H5V11H3V13M3,17H5V15H3V17M3,9H5V7H3V9M7,13H21V11H7V13M7,17H21V15H7V17M7,7V9H21V7H7Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/white">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/white"
|
||||||
|
android:pathData="M2.81,14.12L5.64,11.29L8.17,10.79C11.39,6.41 17.55,4.22 19.78,4.22C19.78,6.45 17.59,12.61 13.21,15.83L12.71,18.36L9.88,21.19C9.88,21.19 9.88,21.19 9.88,21.19L2.81,14.12M7.76,6.94L6.34,8.36L7.76,9.78L9.18,8.36L7.76,6.94M11.29,8.17L9.88,9.58L11.29,11L12.71,9.58L11.29,8.17M14.83,11.71L13.41,13.12L14.83,14.54L16.24,13.12L14.83,11.71Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/white">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/white"
|
||||||
|
android:pathData="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/primary_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/primary_color"
|
||||||
|
android:pathData="M21,18V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5A2,2 0 0,1 5,3H19A2,2 0 0,1 21,5V6H12C10.89,6 10,6.89 10,8V16A2,2 0 0,0 12,18H21M12,16V8H22V16H12M16,13.5A1.5,1.5 0 0,1 14.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,12A1.5,1.5 0 0,1 16,13.5Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="@color/primary_light" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="@color/primary_color" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/background_light" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/primary_light" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/background_light"
|
||||||
|
android:endColor="@color/primary_light"
|
||||||
|
android:angle="135" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/primary_light" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/background_light"
|
||||||
|
android:endColor="@color/primary_light"
|
||||||
|
android:angle="135" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/primary_light" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/primary_light"
|
||||||
|
android:endColor="@color/primary_color"
|
||||||
|
android:angle="90" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/primary_dark" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/primary_light"
|
||||||
|
android:endColor="@color/primary_color"
|
||||||
|
android:angle="45" />
|
||||||
|
<stroke
|
||||||
|
android:width="4dp"
|
||||||
|
android:color="@color/white" />
|
||||||
|
</shape>
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
<?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/background_gradient"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="8dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="💰 Adicionar Despesa"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:src="@drawable/ic_money"
|
||||||
|
android:background="@drawable/icon_background"
|
||||||
|
android:padding="6dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Formulário -->
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Descrição da despesa"
|
||||||
|
app:startIconDrawable="@drawable/ic_description"
|
||||||
|
app:boxStrokeColor="@color/primary_color"
|
||||||
|
app:hintTextColor="@color/primary_color">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etDescription"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Valor (€)"
|
||||||
|
app:startIconDrawable="@drawable/ic_euro"
|
||||||
|
app:boxStrokeColor="@color/primary_color"
|
||||||
|
app:hintTextColor="@color/primary_color">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etAmount"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Categoria"
|
||||||
|
app:startIconDrawable="@drawable/ic_category"
|
||||||
|
app:boxStrokeColor="@color/primary_color"
|
||||||
|
app:hintTextColor="@color/primary_color">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etCategory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Data (DD/MM/AAAA)"
|
||||||
|
app:startIconDrawable="@drawable/ic_calendar"
|
||||||
|
app:boxStrokeColor="@color/primary_color"
|
||||||
|
app:hintTextColor="@color/primary_color">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etDate"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="date"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:hint="Notas (opcional)"
|
||||||
|
app:startIconDrawable="@drawable/ic_notes"
|
||||||
|
app:boxStrokeColor="@color/primary_color"
|
||||||
|
app:hintTextColor="@color/primary_color">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etNotes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- Botões -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSave"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="💾 Salvar"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:cornerRadius="28dp"
|
||||||
|
app:icon="@drawable/ic_save"
|
||||||
|
app:iconTint="@color/white"
|
||||||
|
app:elevation="4dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCancel"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:text="❌ Cancelar"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/primary_color"
|
||||||
|
app:backgroundTint="@android:color/transparent"
|
||||||
|
app:strokeColor="@color/primary_color"
|
||||||
|
app:strokeWidth="2dp"
|
||||||
|
app:cornerRadius="28dp"
|
||||||
|
app:icon="@drawable/ic_cancel"
|
||||||
|
app:iconTint="@color/primary_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
@ -0,0 +1,247 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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="@drawable/background_gradient">
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<!-- Header Principal -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="12dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card"
|
||||||
|
app:strokeColor="@color/primary_light"
|
||||||
|
app:strokeWidth="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<!-- Título e ícone -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginBottom="20dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💰 Minhas Despesas"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Gerencie seus gastos de forma inteligente"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:src="@drawable/ic_wallet"
|
||||||
|
android:background="@drawable/header_icon_background"
|
||||||
|
android:padding="12dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Estatísticas principais -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<!-- Total gasto -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/stat_card_background"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:layout_marginEnd="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💰 Total Gasto"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTotal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="€0.00"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/error_color"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Número de despesas -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/stat_card_background"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:layout_marginStart="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="📊 Despesas"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvExpenseCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/primary_color"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Média por despesa -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/average_background"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="📈 Média por despesa: "
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAverage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="€0.00"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/accent_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Filtros e ações -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="6dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="📋 Suas Despesas"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnFilter"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🔍 Filtrar"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
app:backgroundTint="@color/primary_light"
|
||||||
|
app:cornerRadius="16dp"
|
||||||
|
app:icon="@drawable/ic_filter"
|
||||||
|
app:iconSize="16dp"
|
||||||
|
app:iconTint="@color/primary_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Lista de despesas -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewExpenses"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="80dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<!-- Floating Action Button -->
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabAddExpense"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|bottom"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:src="@drawable/ic_add_white"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
app:elevation="12dp"
|
||||||
|
app:fabSize="normal"
|
||||||
|
app:tint="@color/white"
|
||||||
|
app:rippleColor="@color/primary_dark" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
@ -0,0 +1,436 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/background_gradient"
|
||||||
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<!-- Header de Boas-vindas -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cardCornerRadius="24dp"
|
||||||
|
app:cardElevation="12dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card"
|
||||||
|
app:strokeColor="@color/primary_light"
|
||||||
|
app:strokeWidth="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="32dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<!-- Ícone principal -->
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:src="@drawable/ic_wallet"
|
||||||
|
android:background="@drawable/welcome_icon_background"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:layout_marginBottom="24dp" />
|
||||||
|
|
||||||
|
<!-- Título principal -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💰 Gestão de Despesas"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
|
||||||
|
<!-- Subtítulo -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Controle seus gastos de forma inteligente e organizada"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:layout_marginBottom="24dp" />
|
||||||
|
|
||||||
|
<!-- Botão principal -->
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnGetStarted"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:text="🚀 Começar Agora"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:cornerRadius="28dp"
|
||||||
|
app:icon="@drawable/ic_rocket"
|
||||||
|
app:iconTint="@color/white"
|
||||||
|
app:iconSize="24dp"
|
||||||
|
app:elevation="6dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Recursos da aplicação -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="✨ Recursos Principais"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<!-- Lista de recursos -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- Recurso 1 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:src="@drawable/ic_money"
|
||||||
|
android:background="@drawable/feature_icon_background"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_marginEnd="16dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💰 Controle Total"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Registre e acompanhe todas as suas despesas"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_secondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Recurso 2 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:src="@drawable/ic_category"
|
||||||
|
android:background="@drawable/feature_icon_background"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_marginEnd="16dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="📊 Categorização"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Organize por categorias e visualize padrões"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_secondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Recurso 3 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:src="@drawable/ic_calendar"
|
||||||
|
android:background="@drawable/feature_icon_background"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_marginEnd="16dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="📅 Histórico Completo"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Acompanhe seu histórico de gastos ao longo do tempo"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_secondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Estatísticas rápidas -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="8dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card">
|
||||||
|
|
||||||
|
<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="📈 Estatísticas em Tempo Real"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/stat_background"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:layout_marginEnd="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💰"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:layout_marginBottom="4dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Total Gasto"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQuickTotal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="€0.00"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/error_color"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/stat_background"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:layout_marginStart="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="📊"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:layout_marginBottom="4dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Despesas"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQuickCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="0"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/primary_color"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Botões de ação -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnViewExpenses"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="📋 Ver Despesas"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/primary_color"
|
||||||
|
app:backgroundTint="@android:color/transparent"
|
||||||
|
app:strokeColor="@color/primary_color"
|
||||||
|
app:strokeWidth="2dp"
|
||||||
|
app:cornerRadius="28dp"
|
||||||
|
app:icon="@drawable/ic_list"
|
||||||
|
app:iconTint="@color/primary_color" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnAddExpense"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:text="➕ Adicionar"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:cornerRadius="28dp"
|
||||||
|
app:icon="@drawable/ic_add_white"
|
||||||
|
app:iconTint="@color/white" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<!-- Floating Action Button -->
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fabQuickAdd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|bottom"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:src="@drawable/ic_add_white"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
app:elevation="12dp"
|
||||||
|
app:fabSize="normal"
|
||||||
|
app:tint="@color/white"
|
||||||
|
app:rippleColor="@color/primary_dark" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?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/background_gradient"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<!-- Header simples -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💰 Minhas Despesas"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<!-- Estatísticas simples -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Total: "
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTotal"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="€0.00"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/error_color"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Lista de despesas -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewExpenses"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<!-- Botões de ação -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnAddExpense"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="➕ Adicionar"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:cornerRadius="20dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnBack"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:text="🔙 Voltar"
|
||||||
|
android:textColor="@color/primary_color"
|
||||||
|
app:backgroundTint="@android:color/transparent"
|
||||||
|
app:strokeColor="@color/primary_color"
|
||||||
|
app:strokeWidth="2dp"
|
||||||
|
app:cornerRadius="20dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?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:gravity="center"
|
||||||
|
android:padding="32dp"
|
||||||
|
android:background="@drawable/empty_state_background">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:src="@drawable/ic_empty_wallet"
|
||||||
|
android:background="@drawable/header_icon_background"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💰 Nenhuma despesa ainda"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Toque no botão + para adicionar sua primeira despesa"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnAddFirstExpense"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="➕ Adicionar Despesa"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:cornerRadius="20dp"
|
||||||
|
app:icon="@drawable/ic_add_white"
|
||||||
|
app:iconTint="@color/white" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView 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_margin="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="6dp"
|
||||||
|
app:cardBackgroundColor="@color/background_card"
|
||||||
|
app:strokeColor="@color/primary_light"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<!-- Header com descrição e valor -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDescription"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Descrição"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCategory"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Categoria"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/primary_color"
|
||||||
|
android:background="@drawable/category_chip"
|
||||||
|
android:padding="6dp"
|
||||||
|
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/tvAmount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="€0.00"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/error_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Data"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Notas (se existirem) -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvNotes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Notas"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:background="@drawable/notes_background"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- Botões de ação -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="end">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnEdit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="✏️ Editar"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
app:backgroundTint="@color/primary_color"
|
||||||
|
app:cornerRadius="20dp"
|
||||||
|
app:icon="@drawable/ic_edit"
|
||||||
|
app:iconSize="16dp"
|
||||||
|
app:iconTint="@color/white" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnDelete"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🗑️ Excluir"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
app:backgroundTint="@color/error_color"
|
||||||
|
app:cornerRadius="20dp"
|
||||||
|
app:icon="@drawable/ic_delete"
|
||||||
|
app:iconSize="16dp"
|
||||||
|
app:iconTint="@color/white" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
|
@ -0,0 +1,7 @@
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Base.Theme.GestãoDeDespesas" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your dark theme here. -->
|
||||||
|
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
|
||||||
|
<!-- Cores principais da aplicação -->
|
||||||
|
<color name="primary_color">#6366F1</color>
|
||||||
|
<color name="primary_dark">#4F46E5</color>
|
||||||
|
<color name="primary_light">#A5B4FC</color>
|
||||||
|
<color name="accent_color">#F59E0B</color>
|
||||||
|
|
||||||
|
<!-- Cores de fundo -->
|
||||||
|
<color name="background_light">#F8FAFC</color>
|
||||||
|
<color name="background_card">#FFFFFF</color>
|
||||||
|
<color name="background_gradient_start">#667EEA</color>
|
||||||
|
<color name="background_gradient_end">#764BA2</color>
|
||||||
|
|
||||||
|
<!-- Cores de texto -->
|
||||||
|
<color name="text_primary">#1F2937</color>
|
||||||
|
<color name="text_secondary">#6B7280</color>
|
||||||
|
<color name="text_light">#9CA3AF</color>
|
||||||
|
|
||||||
|
<!-- Cores de status -->
|
||||||
|
<color name="success_color">#10B981</color>
|
||||||
|
<color name="error_color">#EF4444</color>
|
||||||
|
<color name="warning_color">#F59E0B</color>
|
||||||
|
|
||||||
|
<!-- Cores de categoria -->
|
||||||
|
<color name="category_food">#F59E0B</color>
|
||||||
|
<color name="category_transport">#3B82F6</color>
|
||||||
|
<color name="category_entertainment">#8B5CF6</color>
|
||||||
|
<color name="category_health">#EF4444</color>
|
||||||
|
<color name="category_shopping">#10B981</color>
|
||||||
|
<color name="category_other">#6B7280</color>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Gestão de Despesas</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Base.Theme.GestãoDeDespesas" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your light theme here. -->
|
||||||
|
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Theme.GestãoDeDespesas" parent="Base.Theme.GestãoDeDespesas" />
|
||||||
|
</resources>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample backup rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/guide/topics/data/autobackup
|
||||||
|
for details.
|
||||||
|
Note: This file is ignored for devices older than API 31
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore
|
||||||
|
-->
|
||||||
|
<full-backup-content>
|
||||||
|
<!--
|
||||||
|
<include domain="sharedpref" path="."/>
|
||||||
|
<exclude domain="sharedpref" path="device.xml"/>
|
||||||
|
-->
|
||||||
|
</full-backup-content>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample data extraction rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||||
|
for details.
|
||||||
|
-->
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
-->
|
||||||
|
</cloud-backup>
|
||||||
|
<!--
|
||||||
|
<device-transfer>
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
</device-transfer>
|
||||||
|
-->
|
||||||
|
</data-extraction-rules>
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package pt.epvc.gestodedespesas;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
// Configuração de plugins para todo o projeto
|
||||||
|
plugins {
|
||||||
|
// Plugin Android Application - versão 8.13.0 (aplicado apenas aos módulos que o solicitarem)
|
||||||
|
id 'com.android.application' version '8.13.0' apply false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Configurações globais do Gradle para todo o projeto
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Configurações da JVM do daemon Gradle
|
||||||
|
# Especifica os argumentos JVM usados para o processo daemon.
|
||||||
|
# Esta configuração é particularmente útil para ajustar configurações de memória.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
|
# Configurações de build paralelo
|
||||||
|
# Quando configurado, o Gradle executará em modo paralelo incubating.
|
||||||
|
# Esta opção deve ser usada apenas com projetos desacoplados. Para mais detalhes, visite
|
||||||
|
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
|
||||||
|
# Configurações AndroidX
|
||||||
|
# Estrutura de pacotes AndroidX para tornar mais claro quais pacotes são agrupados com o
|
||||||
|
# sistema operacional Android, e quais são empacotados com o APK da sua aplicação
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
|
||||||
|
# Configurações de recursos
|
||||||
|
# Habilita namespacing da classe R de cada biblioteca para que sua classe R inclua apenas os
|
||||||
|
# recursos declarados na própria biblioteca e nenhum das dependências da biblioteca,
|
||||||
|
# reduzindo assim o tamanho da classe R para essa biblioteca
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
|
|
||||||
|
# Configurações de compilação
|
||||||
|
android.suppressUnsupportedCompileSdk=35
|
||||||
|
android.javaCompile.suppressSourceTargetDeprecationWarning=true
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Arquivo de versões centralizadas para dependências e plugins
|
||||||
|
# Este arquivo permite gerenciar todas as versões em um local central
|
||||||
|
|
||||||
|
# Seção de versões - define as versões de todas as dependências e plugins
|
||||||
|
[versions]
|
||||||
|
agp = "8.5.2" # Android Gradle Plugin - versão estável
|
||||||
|
junit = "4.13.2" # JUnit - framework de testes unitários
|
||||||
|
junitVersion = "1.1.5" # AndroidX JUnit - extensões JUnit para Android
|
||||||
|
espressoCore = "3.5.1" # Espresso - framework de testes de interface
|
||||||
|
appcompat = "1.7.1" # AppCompat - compatibilidade com versões antigas
|
||||||
|
material = "1.10.0" # Material Design Components
|
||||||
|
activity = "1.8.2" # Activity - biblioteca base para Activities
|
||||||
|
constraintlayout = "2.1.4" # ConstraintLayout - sistema de layout com constraints
|
||||||
|
|
||||||
|
# Seção de bibliotecas - define as dependências usando as versões acima
|
||||||
|
[libraries]
|
||||||
|
junit = { group = "junit", name = "junit", version.ref = "junit" } # JUnit para testes unitários
|
||||||
|
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } # Extensões JUnit Android
|
||||||
|
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } # Espresso para testes UI
|
||||||
|
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } # AppCompat
|
||||||
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" } # Material Design
|
||||||
|
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } # Activity
|
||||||
|
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } # ConstraintLayout
|
||||||
|
|
||||||
|
# Seção de plugins - define os plugins usando as versões acima
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp" } # Plugin Android Application
|
||||||
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Configurações do Gradle Wrapper
|
||||||
|
# Este arquivo define qual versão do Gradle será usada para construir o projeto
|
||||||
|
|
||||||
|
# Data de criação do arquivo
|
||||||
|
#Thu Oct 23 15:58:53 WEST 2025
|
||||||
|
|
||||||
|
# Configurações de distribuição do Gradle
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
|
||||||
|
# URL da distribuição Gradle - versão 8.14 (suporte Java 24)
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||||
|
|
||||||
|
# Configurações de armazenamento
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Configurações do projeto Gradle - define repositórios e módulos
|
||||||
|
|
||||||
|
// Configuração de gerenciamento de plugins
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
// Repositório Google - plugins Android e Google
|
||||||
|
google {
|
||||||
|
content {
|
||||||
|
includeGroupByRegex 'com\\.android.*' // Inclui grupos Android
|
||||||
|
includeGroupByRegex 'com\\.google.*' // Inclui grupos Google
|
||||||
|
includeGroupByRegex 'androidx.*' // Inclui grupos AndroidX
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mavenCentral() // Repositório Maven Central
|
||||||
|
gradlePluginPortal() // Portal de plugins Gradle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuração de resolução de dependências
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) // Falha se repositórios forem definidos em módulos
|
||||||
|
repositories {
|
||||||
|
google() // Repositório Google para dependências Android
|
||||||
|
mavenCentral() // Repositório Maven Central para dependências Java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuração do projeto raiz
|
||||||
|
rootProject.name = 'Gestão de Despesas' // Nome do projeto
|
||||||
|
include ':app' // Inclui o módulo app no build
|
||||||