Compare commits

..

2 Commits

Author SHA1 Message Date
09b260d9c3 Merge remote-tracking branch 'origin/main' 2026-05-08 10:44:19 +01:00
cd75d3acfc faturas 2026-05-08 10:44:12 +01:00
11 changed files with 427 additions and 206 deletions

View File

@@ -48,6 +48,7 @@ dependencies {
implementation(libs.credentials.play.services.auth) implementation(libs.credentials.play.services.auth)
implementation(libs.googleid) implementation(libs.googleid)
implementation(libs.firebase.database) implementation(libs.firebase.database)
implementation("com.google.firebase:firebase-storage:21.0.0")
implementation(libs.recyclerview) implementation(libs.recyclerview)
implementation(libs.cardview) implementation(libs.cardview)
implementation(libs.lifecycle.runtime.ktx) implementation(libs.lifecycle.runtime.ktx)
@@ -58,8 +59,8 @@ dependencies {
implementation(libs.ui.tooling.preview) implementation(libs.ui.tooling.preview)
implementation(libs.material3) implementation(libs.material3)
implementation(libs.material3.adaptive.navigation.suite) implementation(libs.material3.adaptive.navigation.suite)
implementation("com.google.ai.client.generativeai:generativeai:0.7.0") implementation("com.google.ai.client.generativeai:generativeai:0.9.0")
implementation("com.google.guava:guava:31.1-android") implementation("com.google.guava:guava:33.0.0-android")
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.espresso.core)

View File

@@ -4,19 +4,40 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge; import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets; import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.lifegrid.adapters.DocumentoAdapter;
import com.example.lifegrid.models.Documento;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import java.util.ArrayList;
import java.util.List;
public class DocumentosActivity extends AppCompatActivity { public class DocumentosActivity extends AppCompatActivity {
private ImageView btnBack; private ImageView btnBack;
private LinearLayout llDocumentsList; private RecyclerView rvDocumentos;
private DocumentoAdapter adapter;
private List<Documento> documentoList;
private TextView tvEmptyState1, tvEmptyState2;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -30,62 +51,55 @@ public class DocumentosActivity extends AppCompatActivity {
}); });
btnBack = findViewById(R.id.btnBack); btnBack = findViewById(R.id.btnBack);
llDocumentsList = findViewById(R.id.llDocumentsList);
btnBack.setOnClickListener(v -> finish()); btnBack.setOnClickListener(v -> finish());
loadDocuments(); rvDocumentos = findViewById(R.id.rvDocumentos);
tvEmptyState1 = findViewById(R.id.tvEmptyState1);
tvEmptyState2 = findViewById(R.id.tvEmptyState2);
rvDocumentos.setLayoutManager(new LinearLayoutManager(this));
documentoList = new ArrayList<>();
adapter = new DocumentoAdapter(documentoList, this);
rvDocumentos.setAdapter(adapter);
carregarDocumentos();
} }
private void loadDocuments() { private void carregarDocumentos() {
SharedPreferences prefs = getSharedPreferences("LifeGridDocs", Context.MODE_PRIVATE); FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
int count = prefs.getInt("doc_count", 0); if (user == null) return;
if (count == 0) { DatabaseReference ref = FirebaseDatabase.getInstance().getReference("users")
TextView emptyText = new TextView(this); .child(user.getUid()).child("documentos");
emptyText.setText("Nenhum documento guardado.");
emptyText.setPadding(32, 32, 32, 32);
llDocumentsList.addView(emptyText);
return;
}
for (int i = 0; i < count; i++) { ref.addValueEventListener(new ValueEventListener() {
String uriString = prefs.getString("doc_uri_" + i, ""); @Override
String desc = prefs.getString("doc_desc_" + i, "Sem descrição"); public void onDataChange(@NonNull DataSnapshot snapshot) {
String data = prefs.getString("doc_data_" + i, "Sem data"); documentoList.clear();
for (DataSnapshot ds : snapshot.getChildren()) {
Documento doc = ds.getValue(Documento.class);
if (doc != null) {
doc.setId(ds.getKey());
documentoList.add(doc);
}
}
adapter.notifyDataSetChanged();
LinearLayout itemLayout = new LinearLayout(this); if (documentoList.isEmpty()) {
itemLayout.setOrientation(LinearLayout.HORIZONTAL); tvEmptyState1.setVisibility(View.VISIBLE);
itemLayout.setPadding(16, 16, 16, 16); tvEmptyState2.setVisibility(View.VISIBLE);
rvDocumentos.setVisibility(View.GONE);
ImageView ivDoc = new ImageView(this); } else {
ivDoc.setLayoutParams(new LinearLayout.LayoutParams(200, 200)); tvEmptyState1.setVisibility(View.GONE);
ivDoc.setScaleType(ImageView.ScaleType.CENTER_CROP); tvEmptyState2.setVisibility(View.GONE);
try { rvDocumentos.setVisibility(View.VISIBLE);
ivDoc.setImageURI(Uri.parse(uriString)); }
} catch (Exception e) {
e.printStackTrace();
} }
LinearLayout textLayout = new LinearLayout(this); @Override
textLayout.setOrientation(LinearLayout.VERTICAL); public void onCancelled(@NonNull DatabaseError error) {
textLayout.setPadding(16, 0, 0, 0); Toast.makeText(DocumentosActivity.this, "Erro ao carregar documentos.", Toast.LENGTH_SHORT).show();
}
TextView tvDesc = new TextView(this); });
tvDesc.setText(desc);
tvDesc.setTextSize(16);
tvDesc.setTypeface(null, android.graphics.Typeface.BOLD);
TextView tvData = new TextView(this);
tvData.setText(data);
textLayout.addView(tvDesc);
textLayout.addView(tvData);
itemLayout.addView(ivDoc);
itemLayout.addView(textLayout);
llDocumentsList.addView(itemLayout);
}
} }
} }

View File

@@ -32,7 +32,7 @@ public class InvoiceScannerHelper {
} }
public static void scanInvoice(Context context, Uri imageUri, ScanCallback callback) { public static void scanInvoice(Context context, Uri imageUri, ScanCallback callback) {
if (API_KEY == null || API_KEY.isEmpty() || API_KEY.contains("AIzaSyCoUZSXfEk43LfPtkCCjsnQ_ZMWX7NG1xQ")) { if (API_KEY == null || API_KEY.isEmpty() || API_KEY.contains("CHAVE_API_KEY")) {
callback.onError("Chave API do Gemini não configurada."); callback.onError("Chave API do Gemini não configurada.");
return; return;
} }
@@ -47,7 +47,7 @@ public class InvoiceScannerHelper {
} }
GenerativeModel gm = new GenerativeModel( GenerativeModel gm = new GenerativeModel(
"gemini-1.5-flash", "gemini-2.5-flash",
API_KEY API_KEY
); );
GenerativeModelFutures model = GenerativeModelFutures.from(gm); GenerativeModelFutures model = GenerativeModelFutures.from(gm);

View File

@@ -16,13 +16,10 @@ import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.example.lifegrid.menu.AtivosFragment; import com.example.lifegrid.menu.AtivosFragment;
import com.example.lifegrid.menu.DefinicoesFragment;
import com.example.lifegrid.menu.DocumentosFragment;
import com.example.lifegrid.menu.GraficosFragment; import com.example.lifegrid.menu.GraficosFragment;
import com.example.lifegrid.menu.HomeFragment; import com.example.lifegrid.menu.HomeFragment;
import com.example.lifegrid.menu.MetasFragment; import com.example.lifegrid.menu.MetasFragment;
import com.example.lifegrid.menu.TransacoesFragment; import com.example.lifegrid.menu.TransacoesFragment;
import com.example.lifegrid.models.Transacao;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@@ -30,17 +27,12 @@ import android.widget.Spinner;
import android.widget.Toast; import android.widget.Toast;
import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import androidx.annotation.NonNull;
import java.util.Calendar; import java.util.Calendar;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
/** /**
* TelaInicialActivity funciona como a janela mestre (Container) de navegação da aplicação. * TelaInicialActivity funciona como a janela mestre (Container) de navegação da aplicação.
@@ -182,8 +174,8 @@ public class TelaInicialActivity extends AppCompatActivity {
} }
transFragment.showNovaTransacaoDialog(valor, descricao, categoria, data); transFragment.showNovaTransacaoDialog(valor, descricao, categoria, data);
// Save document to shared preferences or database // Save document to Firebase
saveDocument(imageUri.toString(), descricao, data); uploadDocumentoToFirebase(imageUri, descricao, data);
}); });
} }
@@ -194,15 +186,37 @@ public class TelaInicialActivity extends AppCompatActivity {
}); });
} }
private void saveDocument(String uriString, String descricao, String data) { private void uploadDocumentoToFirebase(android.net.Uri imageUri, String descricao, String data) {
SharedPreferences prefs = getSharedPreferences("LifeGridDocs", Context.MODE_PRIVATE); FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
int count = prefs.getInt("doc_count", 0); if (user == null) {
SharedPreferences.Editor editor = prefs.edit(); Toast.makeText(this, "Utilizador não autenticado.", Toast.LENGTH_SHORT).show();
editor.putString("doc_uri_" + count, uriString); return;
editor.putString("doc_desc_" + count, descricao); }
editor.putString("doc_data_" + count, data);
editor.putInt("doc_count", count + 1); Toast.makeText(this, "A guardar documento na Cloud...", Toast.LENGTH_SHORT).show();
editor.apply();
String fileName = "invoice_" + System.currentTimeMillis() + ".jpg";
com.google.firebase.storage.StorageReference storageRef = com.google.firebase.storage.FirebaseStorage.getInstance().getReference()
.child("users/" + user.getUid() + "/documentos/" + fileName);
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
String downloadUrl = uri.toString();
DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference("users")
.child(user.getUid()).child("documentos").push();
com.example.lifegrid.models.Documento doc = new com.example.lifegrid.models.Documento(descricao, data, downloadUrl);
dbRef.setValue(doc).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Toast.makeText(TelaInicialActivity.this, "Documento guardado com sucesso!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(TelaInicialActivity.this, "Erro ao guardar dados na Cloud.", Toast.LENGTH_SHORT).show();
}
});
});
}).addOnFailureListener(e -> {
Toast.makeText(this, "Erro ao enviar imagem para a Cloud.", Toast.LENGTH_SHORT).show();
});
} }
@Override @Override
@@ -276,11 +290,14 @@ public class TelaInicialActivity extends AppCompatActivity {
public void atualizarDadosHome() { public void atualizarDadosHome() {
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragmentContainerView); Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragmentContainerView);
int mesSelecionado = spinnerMes.getSelectedItemPosition() + 1; // 1 to 12
String anoSelecionado = spinnerAno.getSelectedItem().toString();
if (currentFragment instanceof HomeFragment) { if (currentFragment instanceof HomeFragment) {
int mesSelecionado = spinnerMes.getSelectedItemPosition() + 1; // 1 to 12
String anoSelecionado = spinnerAno.getSelectedItem().toString();
String mesNome = meses[spinnerMes.getSelectedItemPosition()]; String mesNome = meses[spinnerMes.getSelectedItemPosition()];
((HomeFragment) currentFragment).carregarDados(mesSelecionado, anoSelecionado, mesNome); ((HomeFragment) currentFragment).carregarDados(mesSelecionado, anoSelecionado, mesNome);
} else if (currentFragment instanceof TransacoesFragment) {
((TransacoesFragment) currentFragment).setFiltro(mesSelecionado, anoSelecionado);
} }
} }

View File

@@ -0,0 +1,92 @@
package com.example.lifegrid.adapters;
import android.app.DownloadManager;
import android.content.Context;
import android.net.Uri;
import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.lifegrid.R;
import com.example.lifegrid.models.Documento;
import java.util.List;
public class DocumentoAdapter extends RecyclerView.Adapter<DocumentoAdapter.DocumentoViewHolder> {
private List<Documento> documentos;
private Context context;
public DocumentoAdapter(List<Documento> documentos, Context context) {
this.documentos = documentos;
this.context = context;
}
@NonNull
@Override
public DocumentoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_documento, parent, false);
return new DocumentoViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull DocumentoViewHolder holder, int position) {
Documento documento = documentos.get(position);
holder.tvDescricao.setText(documento.getDescricao() != null && !documento.getDescricao().isEmpty() ? documento.getDescricao() : "Fatura");
holder.tvData.setText(documento.getData());
holder.btnDownload.setOnClickListener(v -> {
String url = documento.getUrlImagem();
if (url != null && !url.isEmpty()) {
downloadFile(url, holder.tvDescricao.getText().toString());
} else {
Toast.makeText(context, "URL inválido para o documento.", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return documentos != null ? documentos.size() : 0;
}
private void downloadFile(String url, String descricao) {
try {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
String fileName = "Fatura_" + System.currentTimeMillis() + ".jpg";
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
request.setTitle("Descarregar Fatura");
request.setDescription("A transferir " + descricao);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (manager != null) {
manager.enqueue(request);
Toast.makeText(context, "Transferência iniciada. Verifique as notificações.", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context, "Erro ao iniciar transferência.", Toast.LENGTH_SHORT).show();
}
}
public static class DocumentoViewHolder extends RecyclerView.ViewHolder {
TextView tvDescricao, tvData;
ImageButton btnDownload;
public DocumentoViewHolder(@NonNull View itemView) {
super(itemView);
tvDescricao = itemView.findViewById(R.id.tvDescricao);
tvData = itemView.findViewById(R.id.tvData);
btnDownload = itemView.findViewById(R.id.btnDownload);
}
}
}

View File

@@ -1,37 +0,0 @@
package com.example.lifegrid.menu;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.lifegrid.R;
/**
* DocumentosFragment é a área orientada ao scan, compilação de faturas e arquivo seguro.
* Foca-se unicamente nas componentes não contabéis mas sim em anotações em anexo faturado.
*/
public class DocumentosFragment extends Fragment {
public DocumentosFragment() {
// Construtor público vazio obrigatório
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflaciona o layout para este fragmento
View root = inflater.inflate(R.layout.fragment_documentos, container, false);
return root;
}
}

View File

@@ -50,9 +50,12 @@ public class TransacoesFragment extends Fragment {
private RecyclerView rvTransacoes; private RecyclerView rvTransacoes;
private TransacaoAdapter transacaoAdapter; private TransacaoAdapter transacaoAdapter;
private List<Transacao> transacoesList; private List<Transacao> transacoesList;
private List<Transacao> allTransacoesList;
private TextView tvEmptyState; private TextView tvEmptyState;
private DatabaseReference databaseReference; private DatabaseReference databaseReference;
private String userId; private String userId;
private int currentMes = -1;
private String currentAno = "";
public TransacoesFragment() { public TransacoesFragment() {
// Construtor público vazio obrigatório // Construtor público vazio obrigatório
@@ -65,6 +68,14 @@ public class TransacoesFragment extends Fragment {
} }
@Override
public void onResume() {
super.onResume();
if (getActivity() instanceof com.example.lifegrid.TelaInicialActivity) {
((com.example.lifegrid.TelaInicialActivity) getActivity()).atualizarDadosHome();
}
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -88,6 +99,7 @@ public class TransacoesFragment extends Fragment {
rvTransacoes.setLayoutManager(new LinearLayoutManager(requireContext())); rvTransacoes.setLayoutManager(new LinearLayoutManager(requireContext()));
transacoesList = new ArrayList<>(); transacoesList = new ArrayList<>();
allTransacoesList = new ArrayList<>();
transacaoAdapter = new TransacaoAdapter(transacoesList, transacao -> { transacaoAdapter = new TransacaoAdapter(transacoesList, transacao -> {
new AlertDialog.Builder(requireContext()) new AlertDialog.Builder(requireContext())
@@ -119,23 +131,15 @@ public class TransacoesFragment extends Fragment {
databaseReference.child("users").child(userId).child("transacoes").addValueEventListener(new ValueEventListener() { databaseReference.child("users").child(userId).child("transacoes").addValueEventListener(new ValueEventListener() {
@Override @Override
public void onDataChange(@NonNull DataSnapshot snapshot) { public void onDataChange(@NonNull DataSnapshot snapshot) {
transacoesList.clear(); allTransacoesList.clear();
for (DataSnapshot ds : snapshot.getChildren()) { for (DataSnapshot ds : snapshot.getChildren()) {
Transacao t = ds.getValue(Transacao.class); Transacao t = ds.getValue(Transacao.class);
if (t != null) { if (t != null) {
t.setId(ds.getKey()); t.setId(ds.getKey());
transacoesList.add(t); allTransacoesList.add(t);
} }
} }
transacaoAdapter.notifyDataSetChanged(); aplicarFiltro();
if (transacoesList.isEmpty()) {
tvEmptyState.setVisibility(View.VISIBLE);
rvTransacoes.setVisibility(View.GONE);
} else {
tvEmptyState.setVisibility(View.GONE);
rvTransacoes.setVisibility(View.VISIBLE);
}
} }
@Override @Override
@@ -145,6 +149,61 @@ public class TransacoesFragment extends Fragment {
}); });
} }
private void aplicarFiltro() {
transacoesList.clear();
for (Transacao t : allTransacoesList) {
if (currentMes != -1 && !currentAno.isEmpty() && t.getData() != null) {
String[] parts = t.getData().split("/");
if (parts.length == 3) {
try {
int mesTransacao = Integer.parseInt(parts[1]);
String anoTransacao = parts[2];
if (mesTransacao == currentMes && anoTransacao.equals(currentAno)) {
transacoesList.add(t);
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
} else {
transacoesList.add(t);
}
}
java.util.Collections.sort(transacoesList, new java.util.Comparator<Transacao>() {
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd/MM/yyyy", java.util.Locale.getDefault());
@Override
public int compare(Transacao t1, Transacao t2) {
try {
java.util.Date d1 = sdf.parse(t1.getData());
java.util.Date d2 = sdf.parse(t2.getData());
if (d1 != null && d2 != null) {
return d2.compareTo(d1); // Ordem decrescente (mais recentes no topo)
}
} catch (java.text.ParseException e) {
e.printStackTrace();
}
return 0;
}
});
transacaoAdapter.notifyDataSetChanged();
if (transacoesList.isEmpty()) {
tvEmptyState.setVisibility(View.VISIBLE);
rvTransacoes.setVisibility(View.GONE);
} else {
tvEmptyState.setVisibility(View.GONE);
rvTransacoes.setVisibility(View.VISIBLE);
}
}
public void setFiltro(int mesSelecionado, String anoSelecionado) {
this.currentMes = mesSelecionado;
this.currentAno = anoSelecionado;
aplicarFiltro();
}
public void showNovaTransacaoDialog() { public void showNovaTransacaoDialog() {
showNovaTransacaoDialog(0.0, "", "", ""); showNovaTransacaoDialog(0.0, "", "", "");
} }

View File

@@ -0,0 +1,53 @@
package com.example.lifegrid.models;
import com.google.firebase.database.Exclude;
public class Documento {
private String id;
private String descricao;
private String data;
private String urlImagem;
public Documento() {
}
public Documento(String descricao, String data, String urlImagem) {
this.descricao = descricao;
this.data = data;
this.urlImagem = urlImagem;
}
@Exclude
public String getId() {
return id;
}
@Exclude
public void setId(String id) {
this.id = id;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getUrlImagem() {
return urlImagem;
}
public void setUrlImagem(String urlImagem) {
this.urlImagem = urlImagem;
}
}

View File

@@ -41,18 +41,44 @@
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<ScrollView <TextView
android:id="@+id/tvEmptyState1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Nenhum documento encontrado"
android:textAlignment="center"
android:textColor="#898585"
android:textSize="18sp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/tvEmptyState2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/headerLayout"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/tvEmptyState2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Adicione documentos das suas transações"
android:textAlignment="center"
android:textColor="@color/cinzaescuro"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvEmptyState1" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvDocumentos"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:clipToPadding="false"
android:paddingTop="16dp"
android:paddingBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/headerLayout"> app:layout_constraintTop_toBottomOf="@id/headerLayout" />
<LinearLayout
android:id="@+id/llDocumentsList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,73 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".menu.DocumentosFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/transacoes2CardView"
android:layout_width="379dp"
android:layout_height="544dp"
android:layout_marginTop="16dp"
android:background="@drawable/cardview_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textView12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:text="Documentos"
android:textSize="17sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.111"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView13"
android:layout_width="302dp"
android:layout_height="40dp"
android:layout_marginTop="216dp"
android:text="Nenhum documento encontrado"
android:textAlignment="center"
android:textColor="#898585"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.493"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView12" />
<TextView
android:id="@+id/textView23"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Adicione documentos das suas transações"
android:textColor="@color/cinzaescuro"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.493"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView13" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="348dp"
android:layout_height="382dp"
android:layout_marginTop="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.516"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView12" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="2dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/branco"
android:padding="16dp">
<ImageView
android:id="@+id/ivIcon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/documento"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btnDownload"
app:layout_constraintStart_toEndOf="@+id/ivIcon"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tvDescricao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Fatura de Exemplo"
android:textColor="@color/preto"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="12/04/2026"
android:textColor="#898585"
android:textSize="14sp" />
</LinearLayout>
<ImageButton
android:id="@+id/btnDownload"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_arrow_down"
app:tint="@color/preto"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>