diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 2c341b0..18aa2a5 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -72,4 +72,6 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.google.firebase:firebase-storage:21.0.1")
+ implementation("com.google.android.gms:play-services-location:21.0.1")
+ implementation("com.google.firebase:firebase-database-ktx:20.3.0")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9db9f4a..28adb82 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,7 +4,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/example/pap_findu/LocationService.java b/app/src/main/java/com/example/pap_findu/LocationService.java
new file mode 100644
index 0000000..7e35245
--- /dev/null
+++ b/app/src/main/java/com/example/pap_findu/LocationService.java
@@ -0,0 +1,122 @@
+package com.example.pap_findu;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.location.Location;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.NotificationCompat;
+
+import com.google.android.gms.location.FusedLocationProviderClient;
+import com.google.android.gms.location.LocationCallback;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationResult;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.location.Priority;
+import com.google.firebase.database.DatabaseReference;
+import com.google.firebase.database.FirebaseDatabase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class LocationService extends Service {
+
+ private FusedLocationProviderClient fusedLocationClient;
+ private LocationCallback locationCallback;
+ private DatabaseReference databaseReference;
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ // Inicializa o cliente de GPS e a ligação ao Firebase
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
+
+ // ATENÇÃO: Substitua "ID_DA_CRIANCA" pelo ID real do utilizador logado no momento
+ databaseReference = FirebaseDatabase.getInstance().getReference("users/ID_DA_CRIANCA/live_location");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // 1. Inicia a Notificação Persistente (Obrigatório para serviços em background)
+ createNotificationChannel();
+ Notification notification = new NotificationCompat.Builder(this, "LocationChannel")
+ .setContentTitle("FindU Ativo")
+ .setContentText("A monitorizar a localização em tempo real...")
+ // Substitua ic_launcher pelo ícone da sua app (ex: R.drawable.ic_launcher_foreground)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .build();
+
+ startForeground(1, notification);
+
+ // 2. Inicia o Rastreamento de GPS
+ requestLocationUpdates();
+
+ return START_STICKY;
+ }
+
+ // Ignora o aviso de permissão porque vamos pedir a permissão na Activity ANTES de iniciar este serviço
+ @SuppressWarnings("MissingPermission")
+ private void requestLocationUpdates() {
+ // Configura a frequência do GPS (ex: a cada 10 segundos)
+ LocationRequest locationRequest = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10000)
+ .setMinUpdateDistanceMeters(5.0f) // Só envia se a pessoa se mover 5 metros (poupa bateria)
+ .build();
+
+ locationCallback = new LocationCallback() {
+ @Override
+ public void onLocationResult(@NonNull LocationResult locationResult) {
+ super.onLocationResult(locationResult);
+ for (Location location : locationResult.getLocations()) {
+
+ // 3. Envia os dados atualizados para o Firebase Realtime Database
+ Map locationData = new HashMap<>();
+ locationData.put("latitude", location.getLatitude());
+ locationData.put("longitude", location.getLongitude());
+ locationData.put("last_updated", System.currentTimeMillis());
+
+ databaseReference.setValue(locationData);
+ }
+ }
+ };
+
+ fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ // Quando o serviço for desligado, para de usar o GPS para poupar bateria
+ if (fusedLocationClient != null && locationCallback != null) {
+ fusedLocationClient.removeLocationUpdates(locationCallback);
+ }
+ }
+
+ // Cria o Canal de Notificação (obrigatório a partir do Android 8.0)
+ private void createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(
+ "LocationChannel",
+ "Monitoramento de Localização",
+ NotificationManager.IMPORTANCE_LOW // Low para não fazer barulho nem vibrar a toda a hora
+ );
+ NotificationManager manager = getSystemService(NotificationManager.class);
+ if (manager != null) {
+ manager.createNotificationChannel(channel);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap_findu/MainActivity.java b/app/src/main/java/com/example/pap_findu/MainActivity.java
index 0734bb4..50f9811 100644
--- a/app/src/main/java/com/example/pap_findu/MainActivity.java
+++ b/app/src/main/java/com/example/pap_findu/MainActivity.java
@@ -1,6 +1,9 @@
package com.example.pap_findu;
+import android.content.Intent; // <-- ADICIONADO
+import android.os.Build; // <-- ADICIONADO
import android.os.Bundle;
+
import com.google.android.material.bottomnavigation.BottomNavigationView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
@@ -33,6 +36,20 @@ public class MainActivity extends AppCompatActivity {
if (navHostFragment != null) {
NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(binding.navView, navController);
+ //teste
}
+
+ // ========================================================
+ // CÓDIGO ADICIONADO: INICIAR O RASTREAMENTO EM BACKGROUND
+ // ========================================================
+ Intent serviceIntent = new Intent(this, LocationService.class);
+
+ // O Android 8.0 (Oreo) ou superior exige o startForegroundService
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForegroundService(serviceIntent);
+ } else {
+ startService(serviceIntent);
+ }
+ // ========================================================
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/pap_findu/ui/map/MapFragment.java b/app/src/main/java/com/example/pap_findu/ui/map/MapFragment.java
index cd8fd37..4f05072 100644
--- a/app/src/main/java/com/example/pap_findu/ui/map/MapFragment.java
+++ b/app/src/main/java/com/example/pap_findu/ui/map/MapFragment.java
@@ -37,7 +37,7 @@ public class MapFragment extends Fragment {
db = FirebaseFirestore.getInstance();
auth = FirebaseAuth.getInstance();
- // Agora o Java vai encontrar estes IDs porque colocamos no XML acima
+ // Encontra os IDs no XML
layoutEmptyState = root.findViewById(R.id.layoutEmptyState);
mapContainer = root.findViewById(R.id.mapContainer);
btnAddChild = root.findViewById(R.id.btnAddChild);
@@ -46,7 +46,7 @@ public class MapFragment extends Fragment {
btnAddChild.setOnClickListener(v -> {
FirebaseUser user = auth.getCurrentUser();
if (user != null) {
- openAddChildScreen(); // Agora esta função existe lá embaixo
+ openAddChildScreen();
} else {
Toast.makeText(getContext(), "Erro: Utilizador não autenticado", Toast.LENGTH_SHORT).show();
}
@@ -111,27 +111,41 @@ public class MapFragment extends Fragment {
if (mapContainer != null) mapContainer.setVisibility(View.GONE);
}
- // Esta é a função que estava faltando ou mal fechada
private void openAddChildScreen() {
AddChildBottomSheet bottomSheet = new AddChildBottomSheet();
bottomSheet.setListener(code -> {
+ // Quando o código é gerado, mostramos o diálogo de escolha
showCodeDialog(code);
});
bottomSheet.show(getParentFragmentManager(), "AddChildSheet");
}
+ // --- AQUI ESTÁ A MUDANÇA PRINCIPAL ---
private void showCodeDialog(String code) {
if (getContext() == null) return;
+
+ // 1. Copia o código automaticamente para a área de transferência
+ ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText("Código", code);
+ clipboard.setPrimaryClip(clip);
+ Toast.makeText(getContext(), "Código copiado!", Toast.LENGTH_SHORT).show();
+
+ // 2. Cria o Alerta com as Duas Opções
new AlertDialog.Builder(getContext())
- .setTitle("Perfil Criado!")
- .setMessage("Digite este código no telemóvel do filho:\n\n" + code)
- .setPositiveButton("Copiar Código", (dialog, which) -> {
- ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("Código", code);
- clipboard.setPrimaryClip(clip);
- Toast.makeText(getContext(), "Copiado!", Toast.LENGTH_SHORT).show();
+ .setTitle("Filho Adicionado!")
+ .setMessage("O código de acesso é: " + code + "\n\n(Já copiado automaticamente)")
+ .setCancelable(false) // Impede fechar clicando fora
+
+ // OPÇÃO 1: Ir para o Mapa (Atualiza a tela)
+ .setPositiveButton("Ir para o Mapa", (dialog, which) -> {
+ checkIfHasChildren(); // <--- Esta função recarrega a tela e mostra o mapa
+ })
+
+ // OPÇÃO 2: Adicionar Outro (Fica na mesma tela)
+ .setNegativeButton("Adicionar Outro", (dialog, which) -> {
+ dialog.dismiss(); // Apenas fecha o alerta e continua na tela azul
})
.show();
}
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 6d264cb..522750f 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -2,6 +2,7 @@
@@ -73,7 +74,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
- android:text="Nenhum filho adicionado"
+ android:text="Nenhum membro adicionado"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/tvNoChildDesc"
@@ -87,7 +88,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
- android:text="Adicione o seu primeiro filho para começar."
+ android:text="Adicione o seu primeiro membro para começar."
app:layout_constraintBottom_toTopOf="@+id/btnAddChild"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -97,11 +98,12 @@
android:id="@+id/btnAddChild"
android:layout_width="match_parent"
android:layout_height="60dp"
- android:text="Adicionar Filho"
+ android:text="Adicionar Membro"
android:textStyle="bold"
app:backgroundTint="#3D6DFF"
app:cornerRadius="12dp"
- app:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toBottomOf="parent"
+ tools:layout_editor_absoluteX="24dp" />
diff --git a/app/src/main/res/layout/fragment_map.xml b/app/src/main/res/layout/fragment_map.xml
index e140842..75bfdd8 100644
--- a/app/src/main/res/layout/fragment_map.xml
+++ b/app/src/main/res/layout/fragment_map.xml
@@ -65,11 +65,11 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@android:drawable/ic_menu_add"
- app:tint="#3D6DFF"
app:layout_constraintBottom_toBottomOf="@+id/bgIcon"
app:layout_constraintEnd_toEndOf="@+id/bgIcon"
app:layout_constraintStart_toStartOf="@+id/bgIcon"
- app:layout_constraintTop_toTopOf="@+id/bgIcon" />
+ app:layout_constraintTop_toTopOf="@+id/bgIcon"
+ app:tint="#3D6DFF" />