falta so o chat
This commit is contained in:
108
docs/00_VISAO_GERAL.md
Normal file
108
docs/00_VISAO_GERAL.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# 📋 Med Cuida+ — Visão Geral do Projeto
|
||||
|
||||
## O que é?
|
||||
|
||||
O **Med Cuida+** é uma aplicação Android destinada a **médicos**, permitindo-lhes gerir os seus pacientes, consultas e perfil profissional. A aplicação comunica com o **Firebase** (Authentication + Firestore) para guardar e sincronizar dados em tempo real.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Estrutura do Projeto
|
||||
|
||||
```
|
||||
com.example.medcuida.pro/
|
||||
│
|
||||
├── MainActivity.java ← Ecrã principal (Agenda + Pacientes + Perfil)
|
||||
├── AgendaActivity.java ← Ecrã de agenda alternativo (com calendário)
|
||||
├── ChatActivity.java ← Chat em tempo real entre médico e paciente
|
||||
├── DetalhePacienteActivity.java ← Detalhes de um paciente específico
|
||||
├── EditarPerfilActivity.java ← Edição do perfil do médico
|
||||
├── PerfilMedicoActivity.java ← Visualização do perfil do médico
|
||||
│
|
||||
├── models/ ← Classes de dados (POJOs)
|
||||
│ ├── Consulta.java ← Modelo de uma consulta médica
|
||||
│ ├── Medico.java ← Modelo de um médico
|
||||
│ ├── Mensagem.java ← Modelo de uma mensagem de chat
|
||||
│ └── Paciente.java ← Modelo de um paciente
|
||||
│
|
||||
├── ui/
|
||||
│ ├── adapters/ ← Adaptadores para listas (RecyclerView)
|
||||
│ │ ├── ConsultaAdapter.java ← Lista de consultas na agenda
|
||||
│ │ ├── MensagemAdapter.java ← Lista de mensagens no chat
|
||||
│ │ └── PacienteAdapter.java ← Lista de pacientes
|
||||
│ │
|
||||
│ └── auth/ ← Autenticação
|
||||
│ ├── LoginActivity.java ← Ecrã de login
|
||||
│ └── RegisterActivity.java ← Ecrã de registo de médico
|
||||
│
|
||||
└── services/ ← (Vazio — reservado para serviços futuros)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Tecnologias Utilizadas
|
||||
|
||||
| Tecnologia | Versão | Finalidade |
|
||||
|---|---|---|
|
||||
| **Android SDK** | compileSdk 35 / minSdk 24 | Plataforma base |
|
||||
| **Java** | 1.8 | Linguagem de programação |
|
||||
| **Firebase Auth** | via BoM 32.7.2 | Autenticação de utilizadores |
|
||||
| **Firebase Firestore** | via BoM 32.7.2 | Base de dados em tempo real |
|
||||
| **Material Design** | 1.11.0 | Componentes visuais modernos |
|
||||
| **Navigation Component** | 2.7.7 | Navegação entre fragments |
|
||||
| **Retrofit + OkHttp** | 2.9.0 / 4.10.0 | Chamadas HTTP e JSON |
|
||||
| **Google Generative AI** | Última versão | Integração com Gemini AI |
|
||||
| **Guava** | 31.1 | Operações assíncronas |
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ Coleções Firestore
|
||||
|
||||
| Coleção | Descrição |
|
||||
|---|---|
|
||||
| `utilizadores` | Todos os utilizadores (pacientes e médicos) |
|
||||
| `medicos` | Dados específicos dos médicos (duplicado para compatibilidade) |
|
||||
| `consultas` | Consultas médicas agendadas |
|
||||
| `consultas/{id}/mensagens` | Sub-coleção com mensagens de chat de uma consulta |
|
||||
| `medicamentos` | Medicamentos registados por pacientes |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Fluxo da Aplicação
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[LoginActivity] -->|Credenciais válidas| B{Verificar Perfil}
|
||||
B -->|Existe em 'medicos'| C[MainActivity]
|
||||
B -->|Existe em 'utilizadores'| C
|
||||
B -->|Não existe| D[Erro: Sem perfil]
|
||||
|
||||
E[RegisterActivity] -->|Registo com sucesso| C
|
||||
|
||||
C -->|Tab Agenda| F[Ver Consultas do Dia]
|
||||
C -->|Tab Pacientes| G[Lista de Pacientes]
|
||||
C -->|Tab Perfil| H[Ver/Editar Perfil]
|
||||
|
||||
F -->|Clicar consulta| I[DetalhePacienteActivity]
|
||||
G -->|Clicar paciente| I
|
||||
I -->|Botão Chat| J[ChatActivity]
|
||||
H -->|Editar| K[EditarPerfilActivity]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Índice da Documentação
|
||||
|
||||
| Ficheiro | Conteúdo |
|
||||
|---|---|
|
||||
| [01_MainActivity.md](./01_MainActivity.md) | Ecrã principal com 3 tabs |
|
||||
| [02_AgendaActivity.md](./02_AgendaActivity.md) | Agenda alternativa com calendário |
|
||||
| [03_ChatActivity.md](./03_ChatActivity.md) | Chat em tempo real |
|
||||
| [04_DetalhePacienteActivity.md](./04_DetalhePacienteActivity.md) | Detalhes do paciente |
|
||||
| [05_EditarPerfilActivity.md](./05_EditarPerfilActivity.md) | Edição do perfil médico |
|
||||
| [06_PerfilMedicoActivity.md](./06_PerfilMedicoActivity.md) | Visualização do perfil |
|
||||
| [07_Models.md](./07_Models.md) | Classes de dados (Consulta, Medico, etc.) |
|
||||
| [08_Adapters.md](./08_Adapters.md) | Adaptadores RecyclerView |
|
||||
| [09_Autenticacao.md](./09_Autenticacao.md) | Login e Registo |
|
||||
| [10_Configuracao_Gradle.md](./10_Configuracao_Gradle.md) | Configuração do projeto |
|
||||
| [11_Layouts_XML.md](./11_Layouts_XML.md) | Layouts das Activities e itens RecyclerView |
|
||||
| [12_Layouts_Extras_e_Drawables.md](./12_Layouts_Extras_e_Drawables.md) | Fragments, Dialogs e recursos visuais |
|
||||
165
docs/01_MainActivity.md
Normal file
165
docs/01_MainActivity.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# 🏠 MainActivity.java — Ecrã Principal
|
||||
|
||||
> **Ficheiro:** `app/src/main/java/com/example/medcuida/pro/MainActivity.java`
|
||||
> **Layout:** `activity_main.xml`
|
||||
> **Linhas de código:** 404
|
||||
|
||||
---
|
||||
|
||||
## Descrição Geral
|
||||
|
||||
A `MainActivity` é o **ecrã central da aplicação**. Funciona como um painel de controlo do médico, com **3 secções** controladas por uma `BottomNavigationView`:
|
||||
|
||||
| Tab | Conteúdo |
|
||||
|---|---|
|
||||
| 📅 **Agenda** | Calendário + lista de consultas do dia selecionado |
|
||||
| 👥 **Pacientes** | Lista de todos os pacientes registados |
|
||||
| 👤 **Perfil** | Dados do médico (nome, email, especialidade, cédula) |
|
||||
|
||||
---
|
||||
|
||||
## Inicialização (`onCreate`) — Linhas 59–188
|
||||
|
||||
### 1. Configuração do Layout (Linhas 63–69)
|
||||
```java
|
||||
setContentView(R.layout.activity_main);
|
||||
```
|
||||
- Carrega o layout XML
|
||||
- Envolto em `try-catch` para capturar erros de inflação da view
|
||||
|
||||
### 2. Verificação de Autenticação (Linhas 71–79)
|
||||
```java
|
||||
if (mAuth.getCurrentUser() == null) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
```
|
||||
- Se o utilizador **não está autenticado**, redireciona para o `LoginActivity`
|
||||
- Obtém o `uid` do médico atual para filtrar dados
|
||||
|
||||
### 3. Carregamento do Nome em Cache (Linhas 91–95)
|
||||
```java
|
||||
String cachedName = getSharedPreferences("SessaoMedico", MODE_PRIVATE).getString("nome_medico", "");
|
||||
```
|
||||
- Lê o nome do médico das `SharedPreferences` para **exibição instantânea** (sem esperar pelo Firestore)
|
||||
- Mostra "Olá, [Primeiro Nome]" no topo
|
||||
|
||||
### 4. Configuração dos Pacientes (Linhas 97–107)
|
||||
- Cria um `RecyclerView` com `PacienteAdapter`
|
||||
- Ao clicar num paciente → abre `DetalhePacienteActivity`
|
||||
|
||||
### 5. Configuração da Agenda (Linhas 109–120)
|
||||
- Cria um `RecyclerView` com `ConsultaAdapter`
|
||||
- Ao clicar numa consulta → abre `DetalhePacienteActivity` com ID da consulta
|
||||
|
||||
### 6. Configuração do Perfil (Linhas 122–139)
|
||||
- Associa os `TextView` do perfil (nome, email, especialidade, cédula)
|
||||
- **Botão "Editar Dados"** → abre `EditarPerfilActivity`
|
||||
- **Botão "Logout"** → faz sign out e redireciona para login
|
||||
|
||||
### 7. Bottom Navigation (Linhas 141–162)
|
||||
```java
|
||||
bottomNav.setOnItemSelectedListener(item -> {
|
||||
// Esconde todas as views
|
||||
// Mostra apenas a view selecionada
|
||||
});
|
||||
```
|
||||
- Controla a visibilidade das 3 secções (Agenda, Pacientes, Perfil)
|
||||
- A tab **Agenda** é a selecionada por defeito
|
||||
|
||||
### 8. Carregamento de Dados (Linhas 164–183)
|
||||
- Chama `loadUtilizadores()` para carregar pacientes
|
||||
- Chama `loadPerfilMedico()` para carregar o perfil
|
||||
- Configura o `CalendarView` para filtrar consultas por data
|
||||
|
||||
---
|
||||
|
||||
## Lógica dos Pacientes — Linhas 190–230
|
||||
|
||||
### `loadUtilizadores()`
|
||||
```java
|
||||
db.collection("utilizadores").addSnapshotListener(...)
|
||||
```
|
||||
|
||||
**O que faz:**
|
||||
1. Escuta **em tempo real** a coleção `utilizadores` no Firestore
|
||||
2. Para cada documento:
|
||||
- Converte para objeto `Paciente`
|
||||
- Tenta o campo `nome_completo`, depois `nome` como fallback
|
||||
- Filtra: apenas inclui se `tipo` for `"paciente"` ou não estiver definido
|
||||
3. Atualiza o `RecyclerView` ou mostra mensagem "vazio"
|
||||
|
||||
---
|
||||
|
||||
## Ações nas Consultas — Linhas 232–260
|
||||
|
||||
### `onAceitarClick(Consulta)` — Linha 233
|
||||
- Atualiza o campo `status` da consulta para `"aceite"` no Firestore
|
||||
|
||||
### `onEliminarClick(Consulta)` — Linha 238
|
||||
- **Elimina** o documento da consulta do Firestore
|
||||
|
||||
### `onConsultaClick(Consulta)` — Linha 246
|
||||
- Abre o `DetalhePacienteActivity` com os IDs do paciente e da consulta
|
||||
|
||||
### `updateConsultaStatus()` — Linha 253
|
||||
- Método auxiliar que atualiza o campo `status` de uma consulta
|
||||
|
||||
---
|
||||
|
||||
## Lógica da Agenda — Linhas 262–342
|
||||
|
||||
### `loadConsultasAgendaForDate(String dateStr)`
|
||||
|
||||
**O que faz:**
|
||||
1. Remove qualquer listener anterior (para evitar duplicados)
|
||||
2. Escuta **todas** as consultas em tempo real
|
||||
3. Para cada consulta:
|
||||
- Verifica se **pertence ao médico atual** (campo `medicos` pode ser `String` ou `List`)
|
||||
- Verifica se a **data corresponde** à data selecionada (suporta 3 formatos: `dd/MM/yyyy`, `d/M/yyyy`, `yyyy-MM-dd`)
|
||||
- Exclui consultas com status `"cancelada"`
|
||||
4. Atualiza a lista na UI
|
||||
|
||||
### `convertToISODate()` — Linha 324
|
||||
- Converte `"22/04/2026"` → `"2026-04-22"`
|
||||
|
||||
### `getAlternativeDate()` — Linha 335
|
||||
- Converte `"01/04/2026"` → `"1/4/2026"` (sem zeros à esquerda)
|
||||
|
||||
---
|
||||
|
||||
## Lógica do Perfil — Linhas 350–402
|
||||
|
||||
### `loadPerfilMedico()`
|
||||
1. Escuta em tempo real na coleção `utilizadores`
|
||||
2. Se o documento **não existe**, faz fallback para a coleção `medicos`
|
||||
3. Chama `updateProfileUI()` com os dados encontrados
|
||||
|
||||
### `updateProfileUI(DocumentSnapshot doc)`
|
||||
1. Lê `nome_completo` (ou `nome` como fallback)
|
||||
2. Guarda o nome nas `SharedPreferences` para cache
|
||||
3. Atualiza todos os campos da UI:
|
||||
- Nome, Email, Especialidade, Cédula profissional
|
||||
- Saudação "Olá, [Nome]" no topo
|
||||
|
||||
---
|
||||
|
||||
## Diagrama de Fluxo
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[onCreate] --> B{Utilizador autenticado?}
|
||||
B -->|Não| C[Ir para Login]
|
||||
B -->|Sim| D[Carregar UI]
|
||||
D --> E[Carregar nome em cache]
|
||||
D --> F[Configurar RecyclerViews]
|
||||
D --> G[Configurar BottomNav]
|
||||
D --> H[loadUtilizadores]
|
||||
D --> I[loadPerfilMedico]
|
||||
D --> J[loadConsultasAgendaForDate - hoje]
|
||||
|
||||
G -->|Tab Agenda| K[Mostrar Agenda]
|
||||
G -->|Tab Pacientes| L[Mostrar Pacientes]
|
||||
G -->|Tab Perfil| M[Mostrar Perfil]
|
||||
```
|
||||
136
docs/02_AgendaActivity.md
Normal file
136
docs/02_AgendaActivity.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 📅 AgendaActivity.java — Ecrã de Agenda
|
||||
|
||||
> **Ficheiro:** `app/src/main/java/com/example/medcuida/pro/AgendaActivity.java`
|
||||
> **Layout:** `activity_agenda.xml`
|
||||
> **Linhas de código:** 192
|
||||
|
||||
---
|
||||
|
||||
## Descrição Geral
|
||||
|
||||
A `AgendaActivity` é um **ecrã dedicado à gestão da agenda do médico**. Mostra um calendário interativo e, abaixo dele, a lista de consultas para o dia selecionado.
|
||||
|
||||
> ⚠️ Esta Activity funciona de forma **semelhante à tab Agenda do MainActivity**, mas é uma Activity separada, acessível a partir de outras partes da app.
|
||||
|
||||
---
|
||||
|
||||
## Inicialização (`onCreate`) — Linhas 36–93
|
||||
|
||||
### 1. Configuração dos Componentes (Linhas 43–48)
|
||||
```java
|
||||
CalendarView calendarView = findViewById(R.id.calendarView);
|
||||
recyclerAgenda = findViewById(R.id.recycler_agenda);
|
||||
textAgendaVazia = findViewById(R.id.text_agenda_vazia);
|
||||
```
|
||||
- `CalendarView` → Calendário nativo do Android
|
||||
- `RecyclerView` → Lista das consultas
|
||||
- `TextView` → Mensagem quando não há consultas
|
||||
|
||||
### 2. Bottom Navigation (Linhas 49–62)
|
||||
```java
|
||||
bottomNav.setSelectedItemId(R.id.nav_agenda);
|
||||
```
|
||||
- Marca a tab "Agenda" como ativa
|
||||
- Navegação para `MainActivity` (Pacientes) e `PerfilMedicoActivity` (Perfil)
|
||||
|
||||
### 3. Configuração do Adapter (Linhas 64–80)
|
||||
```java
|
||||
adapter = new ConsultaAdapter(
|
||||
new ConsultaAdapter.OnConsultaActionClickListener() {
|
||||
void onAceitarClick(Consulta consulta) { aceitarConsulta(consulta); }
|
||||
void onEliminarClick(Consulta consulta) { eliminarConsulta(consulta); }
|
||||
},
|
||||
consulta -> { /* abrir detalhes */ }
|
||||
);
|
||||
```
|
||||
- Implementa dois callbacks:
|
||||
- **Aceitar** → muda o status para `"aceite"`
|
||||
- **Eliminar** → remove a consulta do Firestore
|
||||
- Clique no item → abre `DetalhePacienteActivity`
|
||||
|
||||
### 4. Carregamento Inicial (Linhas 82–93)
|
||||
```java
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());
|
||||
loadConsultasForDate(sdf.format(new Date()));
|
||||
```
|
||||
- Obtém o UID do médico autenticado
|
||||
- Carrega as consultas de **hoje**
|
||||
- Configura o listener do calendário para mudar de dia
|
||||
|
||||
---
|
||||
|
||||
## Carregamento de Consultas — Linhas 96–153
|
||||
|
||||
### `loadConsultasForDate(String dateStr)`
|
||||
|
||||
**Passo a passo:**
|
||||
|
||||
1. **Remove o listener anterior** para evitar fugas de memória
|
||||
2. **Escuta em tempo real** toda a coleção `consultas`
|
||||
3. Para cada documento:
|
||||
- Converte para `Consulta`
|
||||
- **Verifica se pertence ao médico:**
|
||||
```java
|
||||
Object med = c.getMedicos();
|
||||
if (med instanceof String) { ... }
|
||||
else if (med instanceof List) { ... }
|
||||
```
|
||||
- **Filtra por data** (suporta 3 formatos diferentes)
|
||||
- **Exclui consultas canceladas**
|
||||
4. Atualiza o adapter e mostra/esconde a mensagem "vazia"
|
||||
|
||||
---
|
||||
|
||||
## Ações nas Consultas — Linhas 155–171
|
||||
|
||||
### `aceitarConsulta(Consulta)`
|
||||
```java
|
||||
db.collection("consultas").document(consulta.getId())
|
||||
.update("status", "aceite")
|
||||
```
|
||||
- Atualiza o status para `"aceite"` no Firestore
|
||||
- Mostra toast de sucesso ou erro
|
||||
|
||||
### `eliminarConsulta(Consulta)`
|
||||
```java
|
||||
db.collection("consultas").document(consulta.getId())
|
||||
.delete()
|
||||
```
|
||||
- **Apaga permanentemente** a consulta do Firestore
|
||||
- Mostra toast de sucesso ou erro
|
||||
|
||||
---
|
||||
|
||||
## Métodos Auxiliares de Data — Linhas 173–190
|
||||
|
||||
### `convertToISODate(String ddMMyyyy)`
|
||||
| Entrada | Saída |
|
||||
|---|---|
|
||||
| `"22/04/2026"` | `"2026-04-22"` |
|
||||
|
||||
### `getAlternativeDate(String paddedDate)`
|
||||
| Entrada | Saída |
|
||||
|---|---|
|
||||
| `"01/04/2026"` | `"1/4/2026"` |
|
||||
|
||||
> Estes métodos existem porque os dados no Firestore podem ter formatos de data inconsistentes.
|
||||
|
||||
---
|
||||
|
||||
## Diagrama de Fluxo
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[onCreate] --> B[Configurar CalendarView]
|
||||
A --> C[Configurar RecyclerView]
|
||||
A --> D[Carregar consultas de hoje]
|
||||
|
||||
B -->|Mudar data| E[loadConsultasForDate]
|
||||
E --> F{Consultas encontradas?}
|
||||
F -->|Sim| G[Mostrar lista]
|
||||
F -->|Não| H[Mostrar 'Sem consultas']
|
||||
|
||||
G -->|Aceitar| I[Atualizar status Firestore]
|
||||
G -->|Eliminar| J[Apagar do Firestore]
|
||||
G -->|Clicar| K[Abrir DetalhePaciente]
|
||||
```
|
||||
44
docs/03_ChatActivity.md
Normal file
44
docs/03_ChatActivity.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# 💬 ChatActivity.java — Chat em Tempo Real
|
||||
|
||||
> **Ficheiro:** `app/src/main/java/com/example/medcuida/pro/ChatActivity.java`
|
||||
> **Layout:** `activity_chat.xml` | **Linhas:** 120
|
||||
|
||||
---
|
||||
|
||||
## Descrição Geral
|
||||
|
||||
Sistema de **chat em tempo real** entre médico e paciente. As mensagens ficam numa **sub-coleção** do Firestore: `consultas/{consultaId}/mensagens/`.
|
||||
|
||||
---
|
||||
|
||||
## Inicialização (`onCreate`) — Linhas 39–78
|
||||
|
||||
1. **Verifica autenticação** — se não há utilizador, fecha a Activity
|
||||
2. **Obtém dados via Intent:**
|
||||
- `currentUserId` → UID do médico autenticado
|
||||
- `consultaId` → ID da consulta (recebido da Activity anterior)
|
||||
3. **Configura RecyclerView** com `setStackFromEnd(true)` (mensagens começam de baixo, como WhatsApp)
|
||||
4. **Botão enviar** → chama `enviarMensagem()`
|
||||
|
||||
---
|
||||
|
||||
## `carregarMensagens()` — Linhas 80–103
|
||||
|
||||
```java
|
||||
db.collection("consultas").document(consultaId).collection("mensagens")
|
||||
.orderBy("timestamp", Query.Direction.ASCENDING)
|
||||
.addSnapshotListener(...)
|
||||
```
|
||||
|
||||
- Escuta em **tempo real** a sub-coleção `mensagens`
|
||||
- Ordena por `timestamp` (mais antigas primeiro)
|
||||
- Faz **scroll automático** para a última mensagem
|
||||
|
||||
---
|
||||
|
||||
## `enviarMensagem()` — Linhas 106–118
|
||||
|
||||
1. Lê o texto e valida (não envia se vazio)
|
||||
2. Cria `Mensagem(texto, currentUserId, timestamp)`
|
||||
3. Adiciona ao Firestore via `.add()`
|
||||
4. Sucesso → limpa campo | Erro → mostra toast
|
||||
71
docs/04_DetalhePacienteActivity.md
Normal file
71
docs/04_DetalhePacienteActivity.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 🏥 DetalhePacienteActivity.java — Detalhes do Paciente
|
||||
|
||||
> **Ficheiro:** `app/src/main/java/com/example/medcuida/pro/DetalhePacienteActivity.java`
|
||||
> **Layout:** `activity_detalhe_paciente.xml` | **Linhas:** 150
|
||||
|
||||
---
|
||||
|
||||
## Descrição Geral
|
||||
|
||||
Mostra os **dados detalhados de um paciente** selecionado, incluindo a sua medicação ativa. Permite abrir o chat com o paciente.
|
||||
|
||||
---
|
||||
|
||||
## Dados Apresentados
|
||||
|
||||
| Campo | Origem Firestore |
|
||||
|---|---|
|
||||
| Nome | `utilizadores/{id}.nome_completo` |
|
||||
| Idade | `utilizadores/{id}.idade` |
|
||||
| Nº Utente | `utilizadores/{id}.numero_utente` |
|
||||
| Sexo | `utilizadores/{id}.sexo` |
|
||||
| Email | `utilizadores/{id}.email` |
|
||||
| Medicação | Coleção `medicamentos` (filtrada por `userId`) |
|
||||
|
||||
---
|
||||
|
||||
## Inicialização (`onCreate`) — Linhas 27–64
|
||||
|
||||
1. **Associa as views** do layout (TextViews + botões)
|
||||
2. **Recebe via Intent:**
|
||||
- `PACIENTE_ID` → ID do paciente (obrigatório)
|
||||
- `CONSULTA_ID` → ID da consulta (opcional, para o chat)
|
||||
3. **Botão "Sair"** → `finish()` (volta à Activity anterior)
|
||||
4. **Botão "Chat":**
|
||||
- Se tem `consultaId` → abre chat diretamente
|
||||
- Se não tem → procura uma consulta existente via `buscarConsultaEParaChat()`
|
||||
|
||||
---
|
||||
|
||||
## `buscarConsultaEParaChat()` — Linhas 66–79
|
||||
|
||||
```java
|
||||
db.collection("consultas")
|
||||
.whereEqualTo("pacienteId", pacienteId)
|
||||
.limit(1)
|
||||
.get()
|
||||
```
|
||||
|
||||
- Procura uma consulta associada ao paciente
|
||||
- Se encontrar → abre o `ChatActivity` com o `consultaId`
|
||||
- Se não encontrar → mostra aviso "Não existe consulta ativa"
|
||||
|
||||
---
|
||||
|
||||
## `carregarDadosPaciente()` — Linhas 88–148
|
||||
|
||||
### Parte 1: Dados pessoais (Linhas 89–111)
|
||||
- Lê o documento do paciente em `utilizadores/{pacienteId}`
|
||||
- Preenche nome, idade, nº utente, sexo e email
|
||||
- Campos nulos aparecem como `"N/D"`
|
||||
|
||||
### Parte 2: Medicação (Linhas 113–138)
|
||||
```java
|
||||
db.collection("medicamentos")
|
||||
.whereEqualTo("userId", pacienteId)
|
||||
.get()
|
||||
```
|
||||
- Pesquisa na coleção `medicamentos` todos os registos do paciente
|
||||
- Suporta nomes de campos em **PT** (`nome`, `hora`) e **EN** (`name`, `time`)
|
||||
- Formata como lista: `• Paracetamol (08:00)`
|
||||
- Se não houver medicação → mostra "Nenhuma medicação ativa"
|
||||
66
docs/05_EditarPerfilActivity.md
Normal file
66
docs/05_EditarPerfilActivity.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# ✏️ EditarPerfilActivity.java — Edição do Perfil
|
||||
|
||||
> **Ficheiro:** `app/src/main/java/com/example/medcuida/pro/EditarPerfilActivity.java`
|
||||
> **Layout:** `activity_editar_perfil.xml` | **Linhas:** 125
|
||||
|
||||
---
|
||||
|
||||
## Descrição Geral
|
||||
|
||||
Permite ao médico **editar os seus dados pessoais**: nome, especialidade e género. As alterações são guardadas **simultaneamente** nas coleções `utilizadores` e `medicos` do Firestore.
|
||||
|
||||
---
|
||||
|
||||
## Campos Editáveis
|
||||
|
||||
| Campo | Tipo de Input | Obrigatório |
|
||||
|---|---|---|
|
||||
| Nome completo | `TextInputEditText` | ✅ Sim |
|
||||
| Especialidade | `TextInputEditText` | ✅ Sim |
|
||||
| Género | `AutoCompleteTextView` (dropdown) | Não |
|
||||
| Email | `TextInputEditText` (só leitura) | — |
|
||||
|
||||
---
|
||||
|
||||
## Inicialização (`onCreate`) — Linhas 32–59
|
||||
|
||||
1. Verifica autenticação — se não há utilizador, fecha a Activity
|
||||
2. Associa as views do layout
|
||||
3. Configura o **dropdown de género** com 3 opções: `Masculino`, `Feminino`, `Outro`
|
||||
4. **Botão Voltar** → `finish()`
|
||||
5. **Botão Guardar** → `guardarAlteracoes()`
|
||||
6. Chama `carregarDados()` para preencher os campos atuais
|
||||
|
||||
---
|
||||
|
||||
## `carregarDados()` — Linhas 71–83
|
||||
|
||||
```java
|
||||
db.collection("utilizadores").document(currentUserId).get()
|
||||
```
|
||||
|
||||
- Lê o documento do médico na coleção `utilizadores`
|
||||
- Preenche os campos: `nome_completo`, `especialidade`, `email`, `sexo`
|
||||
- Em caso de erro → mostra toast
|
||||
|
||||
---
|
||||
|
||||
## `guardarAlteracoes()` — Linhas 86–123
|
||||
|
||||
### Validações (Linhas 87–99)
|
||||
- Nome não pode estar vazio
|
||||
- Especialidade não pode estar vazia
|
||||
|
||||
### Gravação (Linhas 101–122)
|
||||
```java
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
updates.put("nome_completo", name);
|
||||
updates.put("especialidade", specialty);
|
||||
updates.put("sexo", gender);
|
||||
```
|
||||
|
||||
1. Desativa o botão para evitar duplo clique
|
||||
2. Atualiza na coleção `utilizadores`
|
||||
3. **Também atualiza em `medicos`** para manter sincronizado
|
||||
4. Sucesso → mostra toast + `finish()`
|
||||
5. Erro → reativa o botão
|
||||
52
docs/06_PerfilMedicoActivity.md
Normal file
52
docs/06_PerfilMedicoActivity.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# 👤 PerfilMedicoActivity.java — Perfil do Médico
|
||||
|
||||
> **Ficheiro:** `app/src/main/java/com/example/medcuida/pro/PerfilMedicoActivity.java`
|
||||
> **Layout:** `activity_perfil_medico.xml` | **Linhas:** 110
|
||||
|
||||
---
|
||||
|
||||
## Descrição Geral
|
||||
|
||||
Ecrã de **visualização do perfil do médico**. Mostra nome, email, especialidade e cédula profissional. Inclui botões para editar dados e fazer logout.
|
||||
|
||||
---
|
||||
|
||||
## Dados Apresentados
|
||||
|
||||
| Campo | Fonte Firestore | Fallback |
|
||||
|---|---|---|
|
||||
| Nome | `nome_completo` | `nome` |
|
||||
| Email | `email` | — |
|
||||
| Especialidade | `especialidade` | `"--"` |
|
||||
| Cédula | `cedula_profissional` | `cedula` → `"Não configurada"` |
|
||||
|
||||
---
|
||||
|
||||
## Inicialização (`onCreate`) — Linhas 24–66
|
||||
|
||||
1. Associa views e inicializa Firebase
|
||||
2. **Botão "Editar Dados"** → abre `EditarPerfilActivity`
|
||||
3. **Bottom Navigation** com 3 tabs (Agenda, Pacientes, Perfil)
|
||||
4. **Botão Logout:**
|
||||
```java
|
||||
mAuth.signOut();
|
||||
intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
|
||||
```
|
||||
- Faz sign out do Firebase
|
||||
- Limpa toda a pilha de Activities (o utilizador não pode voltar atrás)
|
||||
|
||||
---
|
||||
|
||||
## `carregarPerfil()` — Linhas 74–91
|
||||
|
||||
Chamado no `onResume()` (atualiza sempre que o ecrã fica visível).
|
||||
|
||||
**Estratégia de busca com fallback:**
|
||||
1. Tenta na coleção `utilizadores`
|
||||
2. Se não existir → tenta na coleção `medicos`
|
||||
|
||||
---
|
||||
|
||||
## `mostrarDados(DocumentSnapshot)` — Linhas 94–108
|
||||
|
||||
Preenche a UI com os dados do documento Firestore, suportando múltiplos nomes de campos para compatibilidade.
|
||||
68
docs/07_Models.md
Normal file
68
docs/07_Models.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 📦 Models — Classes de Dados
|
||||
|
||||
> **Pasta:** `app/src/main/java/com/example/medcuida/pro/models/`
|
||||
|
||||
Estas classes representam os **objetos de dados** (POJOs) usados em toda a aplicação. São compatíveis com o Firestore — cada uma tem um construtor vazio obrigatório e getters/setters.
|
||||
|
||||
---
|
||||
|
||||
## Consulta.java (29 linhas)
|
||||
|
||||
Representa uma **consulta médica agendada**.
|
||||
|
||||
| Campo | Tipo | Descrição |
|
||||
|---|---|---|
|
||||
| `id` | `String` | ID do documento Firestore |
|
||||
| `data` | `String` | Data da consulta (ex: `"22/04/2026"`) |
|
||||
| `hora` | `String` | Hora da consulta (ex: `"14:30"`) |
|
||||
| `medicos` | `Object` | ID(s) do(s) médico(s) — pode ser `String` ou `List<String>` |
|
||||
| `pacienteId` | `String` | ID do paciente associado |
|
||||
| `pacienteNome` | `String` | Nome do paciente (para exibição rápida) |
|
||||
| `status` | `String` | Estado: `"Pendente"`, `"aceite"`, `"cancelada"` |
|
||||
|
||||
> ⚠️ O campo `medicos` é do tipo `Object` porque no Firestore pode ser guardado como String simples ou como Array, dependendo de como foi criado.
|
||||
|
||||
---
|
||||
|
||||
## Medico.java (61 linhas)
|
||||
|
||||
Representa um **médico** registado na aplicação.
|
||||
|
||||
| Campo | Tipo | Descrição |
|
||||
|---|---|---|
|
||||
| `id` | `String` | UID do Firebase Auth |
|
||||
| `nome` | `String` | Nome completo |
|
||||
| `email` | `String` | Email de login |
|
||||
| `especialidade` | `String` | Especialidade médica |
|
||||
| `genero` | `String` | Género (`Masculino`, `Feminino`, `Outro`) |
|
||||
|
||||
Tem **dois construtores**: vazio (para Firestore) e completo (para criação manual).
|
||||
|
||||
---
|
||||
|
||||
## Mensagem.java (42 linhas)
|
||||
|
||||
Representa uma **mensagem de chat** entre médico e paciente.
|
||||
|
||||
| Campo | Tipo | Descrição |
|
||||
|---|---|---|
|
||||
| `texto` | `String` | Conteúdo da mensagem |
|
||||
| `senderId` | `String` | UID de quem enviou |
|
||||
| `timestamp` | `long` | Hora de envio em milissegundos (epoch) |
|
||||
|
||||
Guardada na sub-coleção `consultas/{id}/mensagens/`.
|
||||
|
||||
---
|
||||
|
||||
## Paciente.java (20 linhas)
|
||||
|
||||
Representa um **paciente** da aplicação.
|
||||
|
||||
| Campo | Tipo | Descrição |
|
||||
|---|---|---|
|
||||
| `id` | `String` | ID do documento Firestore |
|
||||
| `nome` | `String` | Nome do paciente |
|
||||
| `email` | `String` | Email do paciente |
|
||||
| `fotoUrl` | `String` | URL da foto de perfil (se existir) |
|
||||
|
||||
> É a classe mais simples — os dados adicionais (idade, sexo, nº utente) são lidos diretamente do Firestore no `DetalhePacienteActivity`.
|
||||
102
docs/08_Adapters.md
Normal file
102
docs/08_Adapters.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# 🔄 Adapters — Adaptadores RecyclerView
|
||||
|
||||
> **Pasta:** `app/src/main/java/com/example/medcuida/pro/ui/adapters/`
|
||||
|
||||
Os Adapters fazem a ligação entre os **dados** (listas de objetos) e as **views** (itens visuais no RecyclerView). Cada adapter tem um `ViewHolder` interno que mapeia os elementos do layout XML.
|
||||
|
||||
---
|
||||
|
||||
## ConsultaAdapter.java (113 linhas)
|
||||
|
||||
**Layout do item:** `item_consulta_medico.xml`
|
||||
|
||||
### Para que serve
|
||||
Mostra cada consulta na lista da agenda com: nome do paciente, data/hora, status e botões de ação.
|
||||
|
||||
### Interfaces (callbacks)
|
||||
|
||||
```java
|
||||
interface OnConsultaActionClickListener {
|
||||
void onAceitarClick(Consulta consulta); // Botão "Confirmar"
|
||||
void onEliminarClick(Consulta consulta); // Botão "Cancelar"
|
||||
}
|
||||
|
||||
interface OnConsultaClickListener {
|
||||
void onConsultaClick(Consulta consulta); // Clique no item inteiro
|
||||
}
|
||||
```
|
||||
|
||||
### Lógica do `bind()` (Linhas 75–109)
|
||||
|
||||
1. **Nome do paciente** → mostra `pacienteNome` ou `"Desconhecido"`
|
||||
2. **Data e hora** → combina `data` + `hora` (ex: `"22/04/2026 às 14:30"`)
|
||||
3. **Status** → mostra o texto do status atual
|
||||
4. **Botão Confirmar:**
|
||||
- Visível **apenas** se o status for `"Pendente"`
|
||||
- Chama `onAceitarClick()` ao clicar
|
||||
5. **Botão Cancelar:**
|
||||
- **Sempre visível**
|
||||
- Chama `onEliminarClick()` ao clicar
|
||||
6. **Clique no item** → chama `onConsultaClick()`
|
||||
|
||||
---
|
||||
|
||||
## MensagemAdapter.java (74 linhas)
|
||||
|
||||
**Layout do item:** `item_mensagem.xml`
|
||||
|
||||
### Para que serve
|
||||
Mostra cada mensagem no chat. Distingue visualmente mensagens **enviadas** vs **recebidas**.
|
||||
|
||||
### Como funciona (Linhas 40–53)
|
||||
|
||||
```java
|
||||
if (mensagem.getSenderId().equals(currentUserId)) {
|
||||
// Mostrar layout de mensagem ENVIADA (direita)
|
||||
} else {
|
||||
// Mostrar layout de mensagem RECEBIDA (esquerda)
|
||||
}
|
||||
```
|
||||
|
||||
- Cada item tem **dois layouts** (`layout_msg_enviada` e `layout_msg_recebida`)
|
||||
- Apenas um fica visível de cada vez, com base no `senderId`
|
||||
- O `currentUserId` é passado no construtor
|
||||
|
||||
---
|
||||
|
||||
## PacienteAdapter.java (75 linhas)
|
||||
|
||||
**Layout do item:** `item_paciente.xml`
|
||||
|
||||
### Para que serve
|
||||
Mostra cada paciente numa lista simples com nome e email.
|
||||
|
||||
### Interface
|
||||
|
||||
```java
|
||||
interface OnPacienteClickListener {
|
||||
void onPacienteClick(Paciente paciente);
|
||||
}
|
||||
```
|
||||
|
||||
### Lógica do `bind()` (Linhas 63–72)
|
||||
|
||||
1. **Nome** → mostra `nome` ou `"Desconhecido"`
|
||||
2. **Email** → mostra `email` ou `"N/A"`
|
||||
3. **Clique no item** → chama `onPacienteClick()`
|
||||
|
||||
---
|
||||
|
||||
## Padrão Comum dos 3 Adapters
|
||||
|
||||
Todos seguem a mesma estrutura:
|
||||
|
||||
```
|
||||
1. Extends RecyclerView.Adapter<ViewHolder>
|
||||
2. Lista interna de dados
|
||||
3. Método setXxx(List) para atualizar + notifyDataSetChanged()
|
||||
4. onCreateViewHolder → infla o layout XML
|
||||
5. onBindViewHolder → preenche os dados
|
||||
6. getItemCount → retorna tamanho da lista
|
||||
7. ViewHolder interno (static class)
|
||||
```
|
||||
107
docs/09_Autenticacao.md
Normal file
107
docs/09_Autenticacao.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# 🔐 Autenticação — Login e Registo
|
||||
|
||||
> **Pasta:** `app/src/main/java/com/example/medcuida/pro/ui/auth/`
|
||||
|
||||
---
|
||||
|
||||
## LoginActivity.java (154 linhas)
|
||||
|
||||
> **Layout:** `activity_login.xml`
|
||||
|
||||
### Descrição
|
||||
Ecrã de **entrada na aplicação**. Permite login com email/password via Firebase Auth.
|
||||
|
||||
### Inicialização (`onCreate`) — Linhas 34–78
|
||||
|
||||
1. **Auto-login:** Se já existe sessão ativa → redireciona direto para `MainActivity`
|
||||
2. **Campos:** email e password (`TextInputEditText`)
|
||||
3. **Botão "Entrar"** → `attemptLogin()`
|
||||
4. **Link "Registar"** → abre `RegisterActivity`
|
||||
5. **Link "Esqueci password":**
|
||||
- Valida se o email foi preenchido
|
||||
- Envia email de recuperação via `mAuth.sendPasswordResetEmail()`
|
||||
|
||||
### `attemptLogin()` — Linhas 81–98
|
||||
|
||||
Validações antes de enviar ao Firebase:
|
||||
- Email não pode estar vazio
|
||||
- Password não pode estar vazia
|
||||
|
||||
### `loginUser()` — Linhas 100–123
|
||||
|
||||
```java
|
||||
mAuth.signInWithEmailAndPassword(email, password)
|
||||
```
|
||||
|
||||
1. Desativa o botão (evita duplo clique) e mostra "A entrar..."
|
||||
2. Se login OK → chama `checkUserAccess()`
|
||||
3. Se falhar → mostra mensagem de erro e reativa o botão
|
||||
|
||||
### `checkUserAccess()` — Linhas 125–152
|
||||
|
||||
**Verifica se o utilizador tem perfil configurado** (estratégia com fallback):
|
||||
|
||||
```
|
||||
1º → Procura em 'medicos' → Se existe → Entrar na app
|
||||
2º → Procura em 'utilizadores' → Se existe → Entrar na app
|
||||
3º → Não encontrado → Erro + signOut
|
||||
```
|
||||
|
||||
Se não encontrar em nenhuma coleção → faz `signOut()` e pede para registar.
|
||||
|
||||
---
|
||||
|
||||
## RegisterActivity.java (187 linhas)
|
||||
|
||||
> **Layout:** `activity_register_medico.xml`
|
||||
|
||||
### Descrição
|
||||
Ecrã de **registo de novos médicos**. Cria conta no Firebase Auth e guarda dados no Firestore.
|
||||
|
||||
### Campos do Formulário
|
||||
|
||||
| Campo | Tipo | Obrigatório | Validação |
|
||||
|---|---|---|---|
|
||||
| Nome completo | `TextInputEditText` | ✅ | Não vazio |
|
||||
| Especialidade | `TextInputEditText` | ✅ | Não vazio |
|
||||
| Género | `AutoCompleteTextView` | ✅ | Não vazio |
|
||||
| Email | `TextInputEditText` | ✅ | Não vazio |
|
||||
| Password | `TextInputEditText` | ✅ | Mínimo 6 caracteres |
|
||||
|
||||
### `attemptRegister()` — Linhas 75–118
|
||||
|
||||
Valida todos os campos sequencialmente. Se algum falhar, faz `requestFocus()` nesse campo.
|
||||
|
||||
### `registerUser()` — Linhas 121–155
|
||||
|
||||
```java
|
||||
mAuth.createUserWithEmailAndPassword(email, password)
|
||||
```
|
||||
|
||||
1. Desativa o botão de registo
|
||||
2. Se criar conta OK → chama `saveMedicoData()`
|
||||
3. **Caso especial — email já existe:**
|
||||
- Tenta fazer login com as credenciais fornecidas
|
||||
- Se login OK → grava os dados (pode ter falhado antes)
|
||||
- Se login falhar → mostra erro "email já registado"
|
||||
|
||||
### `saveMedicoData()` — Linhas 157–185
|
||||
|
||||
Grava os dados do médico em **duas coleções** para compatibilidade:
|
||||
|
||||
```java
|
||||
Map<String, Object> medicoMap = new HashMap<>();
|
||||
medicoMap.put("id", uid);
|
||||
medicoMap.put("nome_completo", name);
|
||||
medicoMap.put("email", email);
|
||||
medicoMap.put("especialidade", specialty);
|
||||
medicoMap.put("sexo", gender);
|
||||
medicoMap.put("tipo", "medico");
|
||||
```
|
||||
|
||||
| Coleção | Documento |
|
||||
|---|---|
|
||||
| `utilizadores/{uid}` | Dados principais |
|
||||
| `medicos/{uid}` | Cópia para compatibilidade |
|
||||
|
||||
Após gravar com sucesso → redireciona para `MainActivity`.
|
||||
98
docs/10_Configuracao_Gradle.md
Normal file
98
docs/10_Configuracao_Gradle.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# ⚙️ Configuração Gradle — Build do Projeto
|
||||
|
||||
> **Ficheiros:** `build.gradle` (raiz), `app/build.gradle`, `settings.gradle`
|
||||
|
||||
---
|
||||
|
||||
## settings.gradle
|
||||
|
||||
Define o **nome do projeto** e os **repositórios** de onde são descarregadas as dependências.
|
||||
|
||||
```gradle
|
||||
rootProject.name = "Cuida"
|
||||
include ':app'
|
||||
```
|
||||
|
||||
| Repositório | Finalidade |
|
||||
|---|---|
|
||||
| `google()` | Bibliotecas Android e Firebase |
|
||||
| `mavenCentral()` | Bibliotecas Java/Kotlin gerais |
|
||||
| `jitpack.io` | Bibliotecas de projetos GitHub |
|
||||
|
||||
---
|
||||
|
||||
## build.gradle (raiz)
|
||||
|
||||
Define os **plugins globais** e as suas versões.
|
||||
|
||||
| Plugin | Versão | Finalidade |
|
||||
|---|---|---|
|
||||
| `com.android.application` | 9.2.0 | Compilação de apps Android |
|
||||
| `com.android.library` | 9.2.0 | Compilação de bibliotecas Android |
|
||||
| `com.google.gms.google-services` | 4.4.4 | Integração com Firebase |
|
||||
|
||||
---
|
||||
|
||||
## app/build.gradle
|
||||
|
||||
Configuração principal da aplicação.
|
||||
|
||||
### Identificação da App
|
||||
|
||||
| Propriedade | Valor |
|
||||
|---|---|
|
||||
| `namespace` | `com.example.medcuida.pro` |
|
||||
| `applicationId` | `com.example.medcuida.pro` |
|
||||
| `compileSdk` | 35 |
|
||||
| `minSdk` | 24 (Android 7.0) |
|
||||
| `targetSdk` | 35 |
|
||||
| `versionCode` | 1 |
|
||||
| `versionName` | `"1.0"` |
|
||||
| Java | 1.8 |
|
||||
| View Binding | ✅ Ativado |
|
||||
|
||||
### Dependências Organizadas por Categoria
|
||||
|
||||
#### 🎨 Interface (UI)
|
||||
| Dependência | Versão | Uso |
|
||||
|---|---|---|
|
||||
| `appcompat` | 1.6.1 | Compatibilidade com versões antigas do Android |
|
||||
| `material` | 1.11.0 | Componentes Material Design (botões, cards, nav) |
|
||||
| `constraintlayout` | 2.1.4 | Layouts flexíveis e responsivos |
|
||||
| `navigation-fragment` | 2.7.7 | Navegação entre fragments |
|
||||
| `navigation-ui` | 2.7.7 | UI de navegação |
|
||||
|
||||
#### 🔥 Firebase
|
||||
| Dependência | Versão | Uso |
|
||||
|---|---|---|
|
||||
| `firebase-bom` | 32.7.2 | Gestão de versões Firebase (Bill of Materials) |
|
||||
| `firebase-auth` | via BoM | Autenticação (email/password) |
|
||||
| `firebase-firestore` | via BoM | Base de dados NoSQL em tempo real |
|
||||
|
||||
#### 🤖 IA e Rede
|
||||
| Dependência | Versão | Uso |
|
||||
|---|---|---|
|
||||
| `generativeai` | Última | Google Gemini AI SDK |
|
||||
| `guava` | 31.1 | Operações assíncronas (Futures) |
|
||||
| `retrofit` | 2.9.0 | Chamadas HTTP tipadas |
|
||||
| `converter-gson` | 2.9.0 | Conversão JSON ↔ objetos Java |
|
||||
| `okhttp` | 4.10.0 | Cliente HTTP base |
|
||||
|
||||
#### 🔑 Autenticação Avançada
|
||||
| Dependência | Versão | Uso |
|
||||
|---|---|---|
|
||||
| `credentials` | 1.5.0 | API de credenciais Android |
|
||||
| `credentials-play-services-auth` | 1.5.0 | Google Sign-In via Credentials |
|
||||
| `googleid` | 1.1.1 | Google Identity |
|
||||
|
||||
#### 📍 Localização
|
||||
| Dependência | Versão | Uso |
|
||||
|---|---|---|
|
||||
| `play-services-location` | 21.0.1 | Serviços de localização GPS |
|
||||
|
||||
#### 🧪 Testes
|
||||
| Dependência | Versão | Uso |
|
||||
|---|---|---|
|
||||
| `junit` | 4.13.2 | Testes unitários |
|
||||
| `ext:junit` | 1.1.5 | Testes instrumentados |
|
||||
| `espresso-core` | 3.5.1 | Testes de UI |
|
||||
156
docs/11_Layouts_XML.md
Normal file
156
docs/11_Layouts_XML.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# 🎨 Layouts XML — Interfaces Visuais
|
||||
|
||||
> **Pasta:** `app/src/main/res/layout/`
|
||||
|
||||
---
|
||||
|
||||
## Layouts de Activity (ecrãs completos)
|
||||
|
||||
### `activity_main.xml` (243 linhas)
|
||||
**Usado por:** `MainActivity.java`
|
||||
|
||||
Estrutura principal com 3 secções controladas pela BottomNav:
|
||||
|
||||
| Secção | ID | Descrição |
|
||||
|---|---|---|
|
||||
| Header | `lay_top` | Saudação "Olá, [Nome]" (`tv_greeting_main`) |
|
||||
| Agenda | `view_agenda` | CalendarView num MaterialCard + RecyclerView de consultas + texto "agenda vazia" |
|
||||
| Pacientes | `view_pacientes` | RecyclerView de pacientes (`recycler_consultas`) + texto "vazio" |
|
||||
| Perfil | `view_perfil` | Ícone de utilizador + MaterialCard com dados + botões "Editar" e "Logout" |
|
||||
| Nav | `bottom_navigation` | BottomNavigationView com menu `menu_bottom` |
|
||||
|
||||
**Layout raiz:** `RelativeLayout` com fundo `#F5F5F5`
|
||||
|
||||
---
|
||||
|
||||
### `activity_agenda.xml` (53 linhas)
|
||||
**Usado por:** `AgendaActivity.java`
|
||||
|
||||
Layout vertical simples:
|
||||
1. `CalendarView` — calendário nativo
|
||||
2. `TextView` — título "Consultas agendadas:"
|
||||
3. `RecyclerView` (`recycler_agenda`) — lista de consultas
|
||||
4. `TextView` (`text_agenda_vazia`) — mensagem quando vazio (visibility: gone)
|
||||
5. `BottomNavigationView` — navegação inferior
|
||||
|
||||
---
|
||||
|
||||
### `activity_chat.xml` (57 linhas)
|
||||
**Usado por:** `ChatActivity.java`
|
||||
|
||||
**Layout:** `ConstraintLayout`
|
||||
|
||||
| Elemento | ID | Descrição |
|
||||
|---|---|---|
|
||||
| RecyclerView | `recycler_mensagens` | Ocupa o espaço todo acima do input |
|
||||
| Barra de input | `layout_input` | LinearLayout horizontal no fundo |
|
||||
| Campo de texto | `et_mensagem_input` | EditText com hint "Escrever mensagem..." |
|
||||
| Botão enviar | `btn_enviar` | ImageButton com ícone `ic_send` |
|
||||
|
||||
---
|
||||
|
||||
### `activity_login.xml` (127 linhas)
|
||||
**Usado por:** `LoginActivity.java`
|
||||
|
||||
Layout num `ScrollView` centrado:
|
||||
1. **Logo** — ImageView 250×250dp (`logo_app`)
|
||||
2. **Card de login** — MaterialCardView contendo:
|
||||
- Título "Iniciar Sessão"
|
||||
- Campo email (`email_edit_text`)
|
||||
- Campo password (`password_edit_text`)
|
||||
- Checkbox "Lembrar-me"
|
||||
- Botão "Entrar" (`login_button`)
|
||||
- Link "Esqueceu a palavra-passe" (`forgot_password_link`)
|
||||
3. **Link de registo** — "Não tem conta? Registar" (`register_link`)
|
||||
|
||||
---
|
||||
|
||||
### `activity_register_medico.xml` (153 linhas)
|
||||
**Usado por:** `RegisterActivity.java`
|
||||
|
||||
Layout num `ScrollView`:
|
||||
1. **Logo** — ImageView 200×200dp (`ic_logo`)
|
||||
2. **Card de registo** — MaterialCardView contendo:
|
||||
- Título "Registar"
|
||||
- Campo nome (`name_edit_text`)
|
||||
- Campo especialidade (`specialty_edit_text`)
|
||||
- Dropdown género (`gender_auto_complete`)
|
||||
- Campo email (`email_edit_text`)
|
||||
- Campo password (`password_edit_text`)
|
||||
- Botão "Registar" (`register_button`)
|
||||
- Link "Já tem conta? Entrar" (`login_link`)
|
||||
|
||||
---
|
||||
|
||||
### `activity_detalhe_paciente.xml` (158 linhas)
|
||||
**Usado por:** `DetalhePacienteActivity.java`
|
||||
|
||||
**Layout:** `ConstraintLayout`
|
||||
|
||||
| Secção | Conteúdo |
|
||||
|---|---|
|
||||
| Topo | Botão voltar (`btn_sair_detalhe`) com ícone seta |
|
||||
| Card "Informação Pessoal" | Nome, idade, nº utente, sexo, email |
|
||||
| Card "Medicação Atual" | Lista de medicação (`tv_medicacao_paciente`) |
|
||||
| Fundo | Botão "Conversar com Paciente" (`btn_chat_paciente`) com ícone chat |
|
||||
|
||||
---
|
||||
|
||||
### `activity_editar_perfil.xml` (103 linhas)
|
||||
**Usado por:** `EditarPerfilActivity.java`
|
||||
|
||||
Layout num `ScrollView`:
|
||||
1. Botão voltar (`btn_voltar`)
|
||||
2. Título "Editar Dados Pessoais"
|
||||
3. Campo nome (`edit_name`)
|
||||
4. Campo especialidade (`edit_specialty`)
|
||||
5. Dropdown sexo (`edit_gender`) — `ExposedDropdownMenu`
|
||||
6. Campo email (`edit_email`) — **desativado** (não editável)
|
||||
7. Botão "GUARDAR ALTERAÇÕES" (`btn_guardar_alteracoes`)
|
||||
|
||||
---
|
||||
|
||||
### `activity_perfil_medico.xml` (125 linhas)
|
||||
**Usado por:** `PerfilMedicoActivity.java`
|
||||
|
||||
Layout vertical com:
|
||||
1. Ícone de utilizador (100×100dp)
|
||||
2. MaterialCard com dados do médico (nome, email, especialidade, cédula)
|
||||
3. Botão "EDITAR DADOS" (preto)
|
||||
4. Botão "TERMINAR SESSÃO" (contorno vermelho)
|
||||
5. BottomNavigationView
|
||||
|
||||
---
|
||||
|
||||
## Layouts de Item (para RecyclerView)
|
||||
|
||||
### `item_consulta_medico.xml` (101 linhas)
|
||||
**Usado por:** `ConsultaAdapter.java`
|
||||
|
||||
MaterialCard com cantos arredondados (16dp):
|
||||
- **Linha superior:** Ícone do utente (circle_bg) + nome + data/hora + status
|
||||
- **Linha inferior:** Botão "Eliminar" (vermelho, outlined) + Botão "Aceitar" (preenchido)
|
||||
|
||||
---
|
||||
|
||||
### `item_mensagem.xml` (51 linhas)
|
||||
**Usado por:** `MensagemAdapter.java`
|
||||
|
||||
Cada item tem **dois layouts** (apenas um visível de cada vez):
|
||||
|
||||
| Layout | ID | Posição | Estilo |
|
||||
|---|---|---|---|
|
||||
| Recebida | `layout_msg_recebida` | Esquerda | `bg_bubble_receive` (texto preto) |
|
||||
| Enviada | `layout_msg_enviada` | Direita | `bg_bubble_send` (texto branco) |
|
||||
|
||||
Largura máxima: 280dp (para não ocupar o ecrã todo)
|
||||
|
||||
---
|
||||
|
||||
### `item_paciente.xml` (59 linhas)
|
||||
**Usado por:** `PacienteAdapter.java`
|
||||
|
||||
MaterialCard simples com:
|
||||
- Ícone do paciente (circle_bg, 56×56dp)
|
||||
- Nome em negrito (18sp)
|
||||
- Email em cinza (14sp)
|
||||
143
docs/12_Layouts_Extras_e_Drawables.md
Normal file
143
docs/12_Layouts_Extras_e_Drawables.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 🎨 Layouts Adicionais e Recursos — Fragments, Dialogs e Drawables
|
||||
|
||||
> **Pasta:** `app/src/main/res/layout/` e `app/src/main/res/drawable/`
|
||||
|
||||
---
|
||||
|
||||
## Layouts de Activity (extras)
|
||||
|
||||
### `activity_forgot_password.xml` (64 linhas)
|
||||
Ecrã de **recuperação de palavra-passe**:
|
||||
- Logo da app (187×177dp)
|
||||
- Título "Esqueceu a palavra-passe"
|
||||
- Texto explicativo
|
||||
- Campo email (`email_edit_text`)
|
||||
- Botão "Recuperar Palavra-passe" (`reset_button`)
|
||||
- Link "Voltar ao Login" (`back_to_login`)
|
||||
|
||||
### `activity_register.xml` (8607 bytes)
|
||||
Layout alternativo de registo (versão paciente). Similar ao `activity_register_medico.xml` mas sem campos de especialidade médica.
|
||||
|
||||
---
|
||||
|
||||
## Layouts de Dialog (pop-ups)
|
||||
|
||||
### `dialog_add_medication.xml` (86 linhas)
|
||||
**Diálogo para adicionar medicamento:**
|
||||
|
||||
| Campo | ID | Tipo |
|
||||
|---|---|---|
|
||||
| Nome do medicamento | `edit_med_name` | TextInputEditText |
|
||||
| Horário | `text_med_time` | TextView (mostra hora, ex: "08:00") |
|
||||
| Via de administração | `radio_group_route` | RadioGroup com 3 opções |
|
||||
| Notas | `edit_med_notes` | TextInputEditText (multilinha) |
|
||||
|
||||
**Opções de via de administração:**
|
||||
- Via Oral (Pela boca)
|
||||
- Via Tópica (Na pele)
|
||||
- Via Inalatória (Pelo nariz/boca)
|
||||
|
||||
### `dialog_change_password.xml` (52 linhas)
|
||||
**Diálogo para alterar palavra-passe:**
|
||||
- Título "Alterar Palavra-passe"
|
||||
- Campo "Nova Palavra-passe" (`new_password`)
|
||||
- Botões: "Cancelar" (`button_cancel_password`) + "Salvar" (`button_save_password`)
|
||||
|
||||
### `dialog_edit_profile.xml` (129 linhas)
|
||||
**Diálogo para editar perfil do paciente:**
|
||||
- Foto de perfil circular (`edit_profile_image`) com botão "Mudar Foto"
|
||||
- Campos: Nome, Idade (máx 3 chars), Nº Utente (máx 9 chars), Email
|
||||
- Botão "Alterar Palavra-passe" (`button_change_password`)
|
||||
- Botões: "Cancelar" + "Salvar"
|
||||
|
||||
---
|
||||
|
||||
## Layouts de Fragment
|
||||
|
||||
### `fragment_home.xml` (76 linhas)
|
||||
**Ecrã inicial do paciente:**
|
||||
- Saudação "Olá, utilizador!" (`text_greeting`)
|
||||
- Card "Próxima Medicação" (`card_next_medication`) com nome e hora
|
||||
- Botão "Marcar Consulta" (`button_book_appointment`)
|
||||
|
||||
### `fragment_medication.xml` (55 linhas)
|
||||
**Gestão de medicamentos do paciente:**
|
||||
- Título "Medicação"
|
||||
- RecyclerView (`recycler_medication`) — lista de medicamentos
|
||||
- Texto "Ainda não tem medicamentos" (`text_empty_medications`)
|
||||
- FAB "+" (`fab_add_medication`) — botão flutuante para adicionar
|
||||
|
||||
### `fragment_profile.xml` (128 linhas)
|
||||
**Perfil do paciente:**
|
||||
- Foto de perfil circular (`profile_image`)
|
||||
- Card com: Nome, Email, Idade, Nº Utente
|
||||
- Botão "Editar Dados" (`button_edit_profile`)
|
||||
- Botão "Terminar Sessão" (`button_logout`) — contorno vermelho
|
||||
|
||||
### `fragment_sns24.xml` (93 linhas)
|
||||
**Módulo SNS 24 com IA:**
|
||||
- Título "SNS 24"
|
||||
- Botão "Ligar SNS 24 (808 24 24 24)" (`button_call_sns`) — verde
|
||||
- Secção "Triagem com Inteligência Artificial":
|
||||
- Campo de sintomas (`input_symptoms`) — multilinha
|
||||
- Botão "Analisar Sintomas" (`button_ai_triage`)
|
||||
- Resultado da IA (`text_ai_result`) — inicialmente escondido
|
||||
- Botão "Hospital mais próximo" (`button_find_hospital`) — vermelho, escondido
|
||||
|
||||
### `fragment_appointments.xml` (1740 bytes)
|
||||
Layout para lista de consultas do paciente com RecyclerView.
|
||||
|
||||
### `fragment_schedule_appointment.xml` (3421 bytes)
|
||||
Layout para agendar novas consultas.
|
||||
|
||||
---
|
||||
|
||||
## Layouts de Item (RecyclerView extras)
|
||||
|
||||
### `item_appointment.xml` (2163 bytes)
|
||||
Card para cada consulta na lista do paciente.
|
||||
|
||||
### `item_medication.xml` (2841 bytes)
|
||||
Card para cada medicamento na lista de medicação.
|
||||
|
||||
### `item_time_slot.xml` (421 bytes)
|
||||
Slot de horário para agendamento de consultas.
|
||||
|
||||
---
|
||||
|
||||
## Drawables (Recursos Visuais)
|
||||
|
||||
### Fundos e Shapes
|
||||
|
||||
| Ficheiro | Descrição |
|
||||
|---|---|
|
||||
| `bg_bubble_send.xml` | Fundo da bolha de mensagem **enviada** (cor primária, cantos arredondados) |
|
||||
| `bg_bubble_receive.xml` | Fundo da bolha de mensagem **recebida** (cinza claro, cantos arredondados) |
|
||||
| `bg_chat_input.xml` | Fundo do campo de texto do chat (bordas arredondadas) |
|
||||
| `circle_bg.xml` | Fundo circular para ícones de utilizador |
|
||||
| `btn_outline_error.xml` | Estilo de botão com contorno vermelho (erro) |
|
||||
| `btn_outline_primary.xml` | Estilo de botão com contorno primário |
|
||||
|
||||
### Ícones
|
||||
|
||||
| Ficheiro | Descrição |
|
||||
|---|---|
|
||||
| `ic_arrow_back.xml` | Seta para voltar (←) |
|
||||
| `ic_chat.xml` | Ícone de chat/conversa |
|
||||
| `ic_send.xml` | Ícone de enviar mensagem |
|
||||
| `ic_user.xml` | Ícone de utilizador/pessoa |
|
||||
| `ic_placeholder.xml` | Placeholder para fotos de perfil |
|
||||
| `ic_nav_agenda.xml` | Ícone da tab Agenda |
|
||||
| `ic_nav_pacientes.xml` | Ícone da tab Pacientes |
|
||||
| `ic_nav_contactos.xml` | Ícone da tab Contactos |
|
||||
| `ic_nav_inicio.xml` | Ícone da tab Início |
|
||||
|
||||
### Logos e Launcher
|
||||
|
||||
| Ficheiro | Descrição |
|
||||
|---|---|
|
||||
| `ic_logo.png` | Logo da aplicação (83KB) |
|
||||
| `logo_app.png` | Logo para o ecrã de login (23KB) |
|
||||
| `ic_launcher.xml` | Ícone do launcher (adaptive icon) |
|
||||
| `ic_launcher_round.xml` | Ícone redondo do launcher |
|
||||
| `ic_logo_scaled.xml` | Logo escalada para diferentes tamanhos |
|
||||
40
docs/codigo_detalhado/01_MainActivity_Codigo.md
Normal file
40
docs/codigo_detalhado/01_MainActivity_Codigo.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 📄 Análise de Código: `MainActivity.java`
|
||||
|
||||
O `MainActivity.java` é a classe central da aplicação. Como usa o `BottomNavigationView`, ele controla três "ecrãs" virtuais que se escondem ou aparecem dependendo do botão carregado: Agenda, Pacientes e Perfil.
|
||||
|
||||
## 1. Importações e Declarações (Linhas 1 - 34)
|
||||
A classe estende `AppCompatActivity` e implementa interfaces do `ConsultaAdapter` (`OnConsultaActionClickListener`, `OnConsultaClickListener`) para conseguir "ouvir" cliques feitos nos itens da lista de consultas.
|
||||
|
||||
## 2. Variáveis Globais (Linhas 36 - 57)
|
||||
* **Views de Navegação:** `viewAgenda`, `viewPacientes`, `viewPerfil`, `layTop` — Estas variáveis controlam a visibilidade de cada secção da app.
|
||||
* **RecyclerViews e Adapters:** `recyclerConsultas` e `adapterPacientes` (para a lista de pacientes), `recyclerAgenda` e `adapterAgenda` (para a lista de consultas).
|
||||
* **Firebase:** `mAuth` (autenticação) e `db` (Firestore).
|
||||
* **ID:** `currentMedicoId` guarda o UID (User ID) do médico logado.
|
||||
|
||||
## 3. Método `onCreate()` (Linhas 59 - 188)
|
||||
* **Verificação de Login:** A primeira coisa que o `onCreate` faz é verificar se há algum utilizador logado (`mAuth.getCurrentUser() == null`). Se não houver, ele atira o utilizador para a `LoginActivity`.
|
||||
* **Carregamento Instantâneo (SharedPreferences):** Usa o cache local `SessaoMedico` para exibir o nome ("Olá, [Nome]") antes mesmo da base de dados responder.
|
||||
* **Configuração dos Recyclers:** Define os layouts (LinearLayoutManager) e os Adapters que vão desenhar as listas.
|
||||
* **Bottom Navigation:** Há um `setOnItemSelectedListener` que, dependendo do botão tocado (Agenda, Pacientes, Perfil), faz um `setVisibility(View.GONE)` aos ecrãs indesejados e um `VISIBLE` ao ecrã selecionado.
|
||||
* **Início dos Dados:** Chama `loadUtilizadores()`, `loadPerfilMedico()`, e usa a data atual (`SimpleDateFormat`) para chamar `loadConsultasAgendaForDate(today)`.
|
||||
|
||||
## 4. Lógica de Pacientes (`loadUtilizadores()`) (Linhas 190 - 230)
|
||||
* Coloca um `SnapshotListener` (espião em tempo real) na coleção `"utilizadores"`.
|
||||
* Se o `tipo` for "paciente" (ou nulo), adiciona à lista e manda o adapter desenhá-los no ecrã.
|
||||
* Tenta ir buscar o nome a `"nome_completo"` e depois a `"nome"`.
|
||||
|
||||
## 5. Eventos de Clique nas Consultas (Linhas 232 - 260)
|
||||
* **`onAceitarClick()`:** Altera o status da consulta para "aceite".
|
||||
* **`onEliminarClick()`:** Elimina a consulta inteira da base de dados e informa o utilizador com um Toast.
|
||||
* **`onConsultaClick()`:** Se o médico clicar na consulta (e não nos botões de aceitar/rejeitar), a app vai para a `DetalhePacienteActivity`, passando o `PACIENTE_ID` via Intent.
|
||||
|
||||
## 6. Lógica da Agenda (`loadConsultasAgendaForDate()`) (Linhas 262 - 342)
|
||||
* Recebe uma data (ex: `12/04/2026`).
|
||||
* Liga-se à coleção `"consultas"`. Filtra no cliente (através de um `if`) se o ID do médico (como string ou como parte de uma lista) está na consulta.
|
||||
* Aceita três formatos de data via `getAlternativeDate` (ex: 04/05/2026 e 4/5/2026) ou formato ISO.
|
||||
* Só mostra consultas se não estiverem marcadas como "cancelada".
|
||||
|
||||
## 7. Lógica do Perfil (`loadPerfilMedico()`) (Linhas 350 - 403)
|
||||
* Acede à coleção `"utilizadores"`. Se o documento não existir, faz um *fallback* e tenta ler na coleção `"medicos"`.
|
||||
* Depois de encontrar, usa o `updateProfileUI()` para meter o nome, email, especialidade e cédula nos `TextViews` respetivos.
|
||||
* Atualiza a `SharedPreferences` para garantir que o "Olá, X" nunca fica vazio quando o ecrã recarrega.
|
||||
30
docs/codigo_detalhado/02_LoginActivity_Codigo.md
Normal file
30
docs/codigo_detalhado/02_LoginActivity_Codigo.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# 📄 Análise de Código: `LoginActivity.java`
|
||||
|
||||
Esta classe (`app/src/main/java/com/example/medcuida/pro/ui/auth/LoginActivity.java`) gere toda a entrada do médico no sistema.
|
||||
|
||||
## 1. Importações e Declarações (Linhas 1 - 33)
|
||||
Importa elementos do Firebase (`FirebaseAuth`, `FirebaseUser`, `FirebaseFirestore`) e componentes da interface (`TextInputEditText`, `MaterialButton`).
|
||||
|
||||
## 2. Inicialização `onCreate()` (Linhas 34 - 79)
|
||||
* **Instâncias Firebase:** `mAuth` e `db` são inicializados.
|
||||
* **Proteção de Sessão:** Se `mAuth.getCurrentUser() != null`, significa que o utilizador já está logado. Ele nem sequer abre o ecrã; avança logo para a `MainActivity`.
|
||||
* **Ligações da Interface:** Mapeia botões (Login) e links (Registo, Recuperação de Pass) aos IDs do ficheiro XML.
|
||||
* **Botão Recuperar Password:** Se carregado, verifica se o campo do email não está vazio. Se estiver cheio, chama `mAuth.sendPasswordResetEmail(email)`.
|
||||
|
||||
## 3. Tentativa de Login `attemptLogin()` (Linhas 81 - 98)
|
||||
* Puxa o texto dos inputs `emailEditText` e `passwordEditText`.
|
||||
* Valida os campos: se um deles estiver vazio, dá erro (`setError`) e foca o campo.
|
||||
* Passa as credenciais para o `loginUser()`.
|
||||
|
||||
## 4. O Método `loginUser()` (Linhas 100 - 123)
|
||||
* **Experiência de Utilizador (UX):** Bloqueia o botão e muda o texto para "A entrar..." (assim evita que o médico clique duas vezes).
|
||||
* Faz a chamada ao Firebase: `mAuth.signInWithEmailAndPassword()`.
|
||||
* **Se Sucesso:** Pega no UID gerado e atira-o para o `checkUserAccess()`.
|
||||
* **Se Falha:** Mostra a mensagem de erro (Ex: "Password errada") e reativa o botão de login.
|
||||
|
||||
## 5. Validação de Papel (Role) `checkUserAccess()` (Linhas 125 - 153)
|
||||
Como a base de dados tem tanto pacientes como médicos, este método é crucial para garantir que um "paciente" não entra na app dos médicos por engano.
|
||||
1. Tenta ler o documento do utilizador na coleção `"medicos"`.
|
||||
2. Se existir, é um médico; vai para a `MainActivity`.
|
||||
3. Se não existir, tenta ler na coleção `"utilizadores"`. Isto acontece porque em versões antigas/outras ramificações da app, os médicos também podem estar aí registados.
|
||||
4. Se também não estiver aí, avisa "Esta conta não tem perfil configurado" e **faz sign out** imediatamente, forçando o ecrã a ficar no Login.
|
||||
26
docs/codigo_detalhado/03_AgendaActivity_Codigo.md
Normal file
26
docs/codigo_detalhado/03_AgendaActivity_Codigo.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 📄 Análise de Código: `AgendaActivity.java`
|
||||
|
||||
O ficheiro `app/src/main/java/com/example/medcuida/pro/AgendaActivity.java` é usado como o ecrã de Calendário/Agenda em "ecrã inteiro", embora a `MainActivity` também incorpore parte desta vista. O objetivo primário é ver as consultas de um dia específico.
|
||||
|
||||
## 1. Variáveis e `onCreate()` (Linhas 28 - 94)
|
||||
* Tem um `CalendarView` e uma `RecyclerView` (que vai conter as listas das consultas).
|
||||
* A lógica do **BottomNavigationView** aqui está alterada: Se clicares em "Pacientes", ele não muda uma View interna (como faz na `MainActivity`); aqui, ele lança de facto um `Intent` para voltar para a `MainActivity`. Se clicares em "Perfil", ele lança a `PerfilMedicoActivity`.
|
||||
* Configura o `ConsultaAdapter`. Tem "listeners" para Aceitar, Eliminar ou Clicar (abre `DetalhePacienteActivity`).
|
||||
|
||||
## 2. Gestão do Calendário (Linhas 87 - 93)
|
||||
* A app usa um `SimpleDateFormat` no formato `dd/MM/yyyy` para saber o dia atual.
|
||||
* O `calendarView.setOnDateChangeListener` é acionado sempre que o médico toca num dia no ecrã. Ele refaz a string da data e chama `loadConsultasForDate(selectedDate)`.
|
||||
|
||||
## 3. Pesquisa de Consultas `loadConsultasForDate()` (Linhas 96 - 153)
|
||||
* Retira a subscrição antiga (`agendaListener.remove()`) para não ficar com múltiplas consultas de dados abertas na memória.
|
||||
* Lê na coleção `"consultas"`.
|
||||
* Em vez de fazer uma "Query" no Firestore por data, ele lê quase toda a coleção e **filtra localmente** no telemóvel (dentro do `for(QueryDocumentSnapshot doc : value)`).
|
||||
* Verifica se o id do médico logado (`doctorId`) está dentro do atributo `medicos` da consulta (que pode ser uma String solta ou uma Lista de Strings).
|
||||
* Depois, tenta ver se a data coincide. Se a consulta for cancelada, é ignorada.
|
||||
|
||||
## 4. Lógica de Alteração da Consulta (Linhas 155 - 171)
|
||||
* **`aceitarConsulta()`**: Pega no ID do documento e faz `.update("status", "aceite")`. O Firestore notifica o telemóvel de imediato graças ao `SnapshotListener`, e a consulta desaparece/muda de estado na lista sem ser necessário recarregar o ecrã.
|
||||
* **`eliminarConsulta()`**: Faz um `.delete()` duro na consulta.
|
||||
|
||||
## 5. Tratamento de Datas Sujas (Linhas 173 - 191)
|
||||
Estes dois métodos, `convertToISODate()` e `getAlternativeDate()`, existem porque a base de dados pode ter lixo (datas como `4/5/2026` em vez de `04/05/2026`, ou `2026-05-04`). Este bloco garante que, independentemente de quem guardou a consulta, o ecrã mostra o dado na mesma.
|
||||
Reference in New Issue
Block a user