a localizaçao esta funcionando + ou - certo
This commit is contained in:
6
.idea/deploymentTargetSelector.xml
generated
6
.idea/deploymentTargetSelector.xml
generated
@@ -7,7 +7,7 @@
|
|||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="login_activity">
|
<SelectionState runConfigName="login_activity">
|
||||||
<option name="selectionMode" value="DIALOG" />
|
<option name="selectionMode" value="DIALOG" />
|
||||||
<DropdownSelection timestamp="2026-02-03T16:06:11.417598Z">
|
<DropdownSelection timestamp="2026-03-12T15:46:37.841197Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Medium_Phone_2.avd" />
|
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Medium_Phone_2.avd" />
|
||||||
@@ -18,12 +18,12 @@
|
|||||||
<targets>
|
<targets>
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Medium_Phone_2.avd" />
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=rogmvsfmmfus95e6" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Medium_Phone.avd" />
|
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Pixel_9_Pro.avd" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</targets>
|
</targets>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package com.example.pap_findu;
|
package com.example.pap_findu;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@@ -20,6 +25,10 @@ import com.google.android.gms.location.LocationRequest;
|
|||||||
import com.google.android.gms.location.LocationResult;
|
import com.google.android.gms.location.LocationResult;
|
||||||
import com.google.android.gms.location.LocationServices;
|
import com.google.android.gms.location.LocationServices;
|
||||||
import com.google.android.gms.location.Priority;
|
import com.google.android.gms.location.Priority;
|
||||||
|
|
||||||
|
// IMPORTAÇÕES ADICIONADAS PARA A AUTENTICAÇÃO AUTOMÁTICA
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
import com.google.firebase.database.DatabaseReference;
|
import com.google.firebase.database.DatabaseReference;
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
|
||||||
@@ -42,38 +51,65 @@ public class LocationService extends Service {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
// Inicializa o cliente de GPS e a ligação ao Firebase
|
// Inicializa o cliente de GPS
|
||||||
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
|
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");
|
// AUTOMAÇÃO: Vai buscar o ID do Filho que está logado neste telemóvel
|
||||||
|
// =================================================================
|
||||||
|
FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
|
||||||
|
if (currentUser != null) {
|
||||||
|
String myUid = currentUser.getUid(); // Pega o ID único gerado pelo Firebase
|
||||||
|
|
||||||
|
// Cria a referência da base de dados usando este ID dinâmico
|
||||||
|
databaseReference = FirebaseDatabase.getInstance().getReference("users/" + myUid + "/live_location");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
// 1. Inicia a Notificação Persistente (Obrigatório para serviços em background)
|
// 1. Cria o Canal e a Notificação IMEDIATAMENTE
|
||||||
createNotificationChannel();
|
createNotificationChannel();
|
||||||
Notification notification = new NotificationCompat.Builder(this, "LocationChannel")
|
Notification notification = new NotificationCompat.Builder(this, "LocationChannel")
|
||||||
.setContentTitle("FindU Ativo")
|
.setContentTitle("FindU Ativo")
|
||||||
.setContentText("A monitorizar a localização em tempo real...")
|
.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)
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// 2. ENVOLVER TUDO NUM TRY-CATCH PARA EVITAR CRASHES NO ANDROID 14
|
||||||
|
try {
|
||||||
|
// O Android Q (API 29) e superior permite/exige especificar o tipo de serviço no código
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
|
||||||
|
} else {
|
||||||
startForeground(1, notification);
|
startForeground(1, notification);
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Inicia o Rastreamento de GPS
|
// 3. Verifica permissão ANTES de ligar o motor de GPS
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// Se tem permissão, arranca com a recolha de coordenadas!
|
||||||
requestLocationUpdates();
|
requestLocationUpdates();
|
||||||
|
} else {
|
||||||
|
// Se por acaso a permissão falhou, desliga-se silenciosamente
|
||||||
|
stopForeground(true);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Se o sistema operativo forçar uma paragem (muito comum em emuladores), apanhamos o erro aqui!
|
||||||
|
e.printStackTrace();
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
return START_STICKY;
|
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")
|
@SuppressWarnings("MissingPermission")
|
||||||
private void requestLocationUpdates() {
|
private void requestLocationUpdates() {
|
||||||
// Configura a frequência do GPS (ex: a cada 10 segundos)
|
// Configura a frequência do GPS (a cada 10 segundos, só se mover 5 metros)
|
||||||
LocationRequest locationRequest = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10000)
|
LocationRequest locationRequest = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10000)
|
||||||
.setMinUpdateDistanceMeters(5.0f) // Só envia se a pessoa se mover 5 metros (poupa bateria)
|
.setMinUpdateDistanceMeters(5.0f)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
locationCallback = new LocationCallback() {
|
locationCallback = new LocationCallback() {
|
||||||
@@ -81,19 +117,26 @@ public class LocationService extends Service {
|
|||||||
public void onLocationResult(@NonNull LocationResult locationResult) {
|
public void onLocationResult(@NonNull LocationResult locationResult) {
|
||||||
super.onLocationResult(locationResult);
|
super.onLocationResult(locationResult);
|
||||||
for (Location location : locationResult.getLocations()) {
|
for (Location location : locationResult.getLocations()) {
|
||||||
|
// Prepara os dados
|
||||||
// 3. Envia os dados atualizados para o Firebase Realtime Database
|
|
||||||
Map<String, Object> locationData = new HashMap<>();
|
Map<String, Object> locationData = new HashMap<>();
|
||||||
locationData.put("latitude", location.getLatitude());
|
locationData.put("latitude", location.getLatitude());
|
||||||
locationData.put("longitude", location.getLongitude());
|
locationData.put("longitude", location.getLongitude());
|
||||||
locationData.put("last_updated", System.currentTimeMillis());
|
locationData.put("last_updated", System.currentTimeMillis());
|
||||||
|
|
||||||
|
// Verifica se o utilizador não é nulo antes de enviar para o Firebase
|
||||||
|
if (databaseReference != null) {
|
||||||
databaseReference.setValue(locationData);
|
databaseReference.setValue(locationData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Outro try-catch de segurança para a comunicação com os serviços do Google (GMS)
|
||||||
|
try {
|
||||||
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
|
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -111,7 +154,7 @@ public class LocationService extends Service {
|
|||||||
NotificationChannel channel = new NotificationChannel(
|
NotificationChannel channel = new NotificationChannel(
|
||||||
"LocationChannel",
|
"LocationChannel",
|
||||||
"Monitoramento de Localização",
|
"Monitoramento de Localização",
|
||||||
NotificationManager.IMPORTANCE_LOW // Low para não fazer barulho nem vibrar a toda a hora
|
NotificationManager.IMPORTANCE_LOW // Low para não fazer barulho nem vibrar
|
||||||
);
|
);
|
||||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||||
if (manager != null) {
|
if (manager != null) {
|
||||||
|
|||||||
@@ -1,21 +1,51 @@
|
|||||||
package com.example.pap_findu;
|
package com.example.pap_findu;
|
||||||
|
|
||||||
import android.content.Intent; // <-- ADICIONADO
|
import android.Manifest;
|
||||||
import android.os.Build; // <-- ADICIONADO
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.navigation.NavController;
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.navigation.ui.AppBarConfiguration;
|
import androidx.navigation.ui.AppBarConfiguration;
|
||||||
import androidx.navigation.ui.NavigationUI;
|
import androidx.navigation.ui.NavigationUI;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
|
||||||
import com.example.pap_findu.databinding.ActivityMainBinding;
|
import com.example.pap_findu.databinding.ActivityMainBinding;
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private ActivityMainBinding binding;
|
private ActivityMainBinding binding;
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// 1. GESTOR DE PERMISSÕES (O "Contrato" com o Android)
|
||||||
|
// ========================================================
|
||||||
|
private final ActivityResultLauncher<String[]> requestPermissionLauncher =
|
||||||
|
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), permissions -> {
|
||||||
|
Boolean fineLocationGranted = permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false);
|
||||||
|
Boolean coarseLocationGranted = permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false);
|
||||||
|
|
||||||
|
if (fineLocationGranted != null && fineLocationGranted) {
|
||||||
|
// Permissão precisa concedida, podemos iniciar o serviço!
|
||||||
|
startLocationService();
|
||||||
|
} else if (coarseLocationGranted != null && coarseLocationGranted) {
|
||||||
|
// Permissão aproximada concedida, podemos iniciar o serviço!
|
||||||
|
startLocationService();
|
||||||
|
} else {
|
||||||
|
// O utilizador recusou as permissões. A app não pode iniciar o serviço.
|
||||||
|
Toast.makeText(this, "Permissão de localização negada. O rastreamento não funcionará.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -40,16 +70,39 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================
|
// ========================================================
|
||||||
// CÓDIGO ADICIONADO: INICIAR O RASTREAMENTO EM BACKGROUND
|
// 2. PEDE PERMISSÕES ANTES DE LIGAR O MOTOR
|
||||||
// ========================================================
|
// ========================================================
|
||||||
Intent serviceIntent = new Intent(this, LocationService.class);
|
checkAndRequestPermissions();
|
||||||
|
|
||||||
// O Android 8.0 (Oreo) ou superior exige o startForegroundService
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
startForegroundService(serviceIntent);
|
|
||||||
} else {
|
|
||||||
startService(serviceIntent);
|
|
||||||
}
|
}
|
||||||
// ========================================================
|
|
||||||
|
private void checkAndRequestPermissions() {
|
||||||
|
List<String> permissionsNeeded = new ArrayList<>();
|
||||||
|
|
||||||
|
// Verifica a permissão de localização
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
|
||||||
|
permissionsNeeded.add(Manifest.permission.ACCESS_COARSE_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se for Android 13 ou superior, verifica a permissão de notificações (Obrigatório para Foreground Services)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.POST_NOTIFICATIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!permissionsNeeded.isEmpty()) {
|
||||||
|
// Se faltam permissões, pede-as ao utilizador! (Isto vai mostrar a janelinha)
|
||||||
|
requestPermissionLauncher.launch(permissionsNeeded.toArray(new String[0]));
|
||||||
|
} else {
|
||||||
|
// Se já tem todas as permissões, arranca logo com o serviço de localização!
|
||||||
|
startLocationService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startLocationService() {
|
||||||
|
Intent serviceIntent = new Intent(this, LocationService.class);
|
||||||
|
// O ContextCompat resolve automaticamente os problemas de compatibilidade de versões do Android!
|
||||||
|
ContextCompat.startForegroundService(this, serviceIntent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import android.content.ClipData;
|
|||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -15,13 +16,24 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import com.example.pap_findu.R;
|
import com.example.pap_findu.R;
|
||||||
|
import com.google.android.gms.maps.CameraUpdateFactory;
|
||||||
|
import com.google.android.gms.maps.GoogleMap;
|
||||||
|
import com.google.android.gms.maps.OnMapReadyCallback;
|
||||||
import com.google.android.gms.maps.SupportMapFragment;
|
import com.google.android.gms.maps.SupportMapFragment;
|
||||||
|
import com.google.android.gms.maps.model.LatLng;
|
||||||
|
import com.google.android.gms.maps.model.Marker;
|
||||||
|
import com.google.android.gms.maps.model.MarkerOptions;
|
||||||
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.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
import com.google.firebase.firestore.FirebaseFirestore;
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.firestore.QuerySnapshot;
|
import com.google.firebase.firestore.QuerySnapshot;
|
||||||
|
|
||||||
public class MapFragment extends Fragment {
|
public class MapFragment extends Fragment implements OnMapReadyCallback {
|
||||||
|
|
||||||
private Button btnAddChild;
|
private Button btnAddChild;
|
||||||
private View layoutEmptyState;
|
private View layoutEmptyState;
|
||||||
@@ -29,6 +41,15 @@ public class MapFragment extends Fragment {
|
|||||||
private FirebaseFirestore db;
|
private FirebaseFirestore db;
|
||||||
private FirebaseAuth auth;
|
private FirebaseAuth auth;
|
||||||
|
|
||||||
|
// Variáveis para o Mapa e Tempo Real
|
||||||
|
private GoogleMap mMap;
|
||||||
|
private Marker childMarker;
|
||||||
|
private DatabaseReference locationRef;
|
||||||
|
private ValueEventListener locationListener;
|
||||||
|
|
||||||
|
// VARIÁVEL ADICIONADA PARA GUARDAR O ID DA CRIANÇA AUTOMATICAMENTE
|
||||||
|
private String currentChildId = null;
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
|
||||||
@@ -37,7 +58,6 @@ public class MapFragment extends Fragment {
|
|||||||
db = FirebaseFirestore.getInstance();
|
db = FirebaseFirestore.getInstance();
|
||||||
auth = FirebaseAuth.getInstance();
|
auth = FirebaseAuth.getInstance();
|
||||||
|
|
||||||
// Encontra os IDs no XML
|
|
||||||
layoutEmptyState = root.findViewById(R.id.layoutEmptyState);
|
layoutEmptyState = root.findViewById(R.id.layoutEmptyState);
|
||||||
mapContainer = root.findViewById(R.id.mapContainer);
|
mapContainer = root.findViewById(R.id.mapContainer);
|
||||||
btnAddChild = root.findViewById(R.id.btnAddChild);
|
btnAddChild = root.findViewById(R.id.btnAddChild);
|
||||||
@@ -80,11 +100,20 @@ public class MapFragment extends Fragment {
|
|||||||
.limit(1)
|
.limit(1)
|
||||||
.get()
|
.get()
|
||||||
.addOnCompleteListener(task -> {
|
.addOnCompleteListener(task -> {
|
||||||
if (task.isSuccessful()) {
|
if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) {
|
||||||
QuerySnapshot snapshot = task.getResult();
|
|
||||||
if (snapshot != null && !snapshot.isEmpty()) {
|
// =================================================================
|
||||||
showMapState();
|
// AUTOMAÇÃO: Encontra a criança e extrai o ID dela!
|
||||||
|
// =================================================================
|
||||||
|
var document = task.getResult().getDocuments().get(0);
|
||||||
|
|
||||||
|
// MUITO IMPORTANTE: Mude "uid" para o nome do campo na sua base de dados que tem o ID do filho!
|
||||||
|
currentChildId = document.getString("uid");
|
||||||
|
|
||||||
|
if (currentChildId != null) {
|
||||||
|
showMapState(); // Se encontrou o ID, carrega o mapa
|
||||||
} else {
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Erro: Criança encontrada, mas sem ID.", Toast.LENGTH_SHORT).show();
|
||||||
showEmptyState();
|
showEmptyState();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -97,13 +126,79 @@ public class MapFragment extends Fragment {
|
|||||||
if (layoutEmptyState != null) layoutEmptyState.setVisibility(View.GONE);
|
if (layoutEmptyState != null) layoutEmptyState.setVisibility(View.GONE);
|
||||||
if (mapContainer != null) mapContainer.setVisibility(View.VISIBLE);
|
if (mapContainer != null) mapContainer.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
Fragment existingMap = getChildFragmentManager().findFragmentById(R.id.mapContainer);
|
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.mapContainer);
|
||||||
if (existingMap == null) {
|
if (mapFragment == null) {
|
||||||
SupportMapFragment googleMapFragment = SupportMapFragment.newInstance();
|
mapFragment = SupportMapFragment.newInstance();
|
||||||
getChildFragmentManager().beginTransaction()
|
getChildFragmentManager().beginTransaction()
|
||||||
.replace(R.id.mapContainer, googleMapFragment)
|
.replace(R.id.mapContainer, mapFragment)
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapFragment.getMapAsync(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMapReady(@NonNull GoogleMap googleMap) {
|
||||||
|
mMap = googleMap;
|
||||||
|
|
||||||
|
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(0, 0), 2f));
|
||||||
|
|
||||||
|
// Inicia a escuta APENAS se tivermos encontrado um ID válido
|
||||||
|
if (currentChildId != null) {
|
||||||
|
startListeningToChildLocation();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "A aguardar ligação à criança...", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startListeningToChildLocation() {
|
||||||
|
// =================================================================
|
||||||
|
// AUTOMAÇÃO: Usa a variável currentChildId em vez de texto fixo
|
||||||
|
// =================================================================
|
||||||
|
locationRef = FirebaseDatabase.getInstance().getReference("users/" + currentChildId + "/live_location");
|
||||||
|
|
||||||
|
locationListener = new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
if (snapshot.exists()) {
|
||||||
|
Double lat = snapshot.child("latitude").getValue(Double.class);
|
||||||
|
Double lng = snapshot.child("longitude").getValue(Double.class);
|
||||||
|
|
||||||
|
if (lat != null && lng != null && mMap != null) {
|
||||||
|
LatLng childPosition = new LatLng(lat, lng);
|
||||||
|
|
||||||
|
if (childMarker == null) {
|
||||||
|
MarkerOptions markerOptions = new MarkerOptions()
|
||||||
|
.position(childPosition)
|
||||||
|
.title("Criança");
|
||||||
|
|
||||||
|
childMarker = mMap.addMarker(markerOptions);
|
||||||
|
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(childPosition, 16f));
|
||||||
|
} else {
|
||||||
|
childMarker.setPosition(childPosition);
|
||||||
|
mMap.animateCamera(CameraUpdateFactory.newLatLng(childPosition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w("MapFragment", "Pasta de localização vazia ou inexistente para o ID: " + currentChildId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
Log.e("MapFragment", "Erro ao ler localização: " + error.getMessage());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
locationRef.addValueEventListener(locationListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (locationRef != null && locationListener != null) {
|
||||||
|
locationRef.removeEventListener(locationListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showEmptyState() {
|
private void showEmptyState() {
|
||||||
@@ -113,40 +208,24 @@ public class MapFragment extends Fragment {
|
|||||||
|
|
||||||
private void openAddChildScreen() {
|
private void openAddChildScreen() {
|
||||||
AddChildBottomSheet bottomSheet = new AddChildBottomSheet();
|
AddChildBottomSheet bottomSheet = new AddChildBottomSheet();
|
||||||
|
bottomSheet.setListener(this::showCodeDialog);
|
||||||
bottomSheet.setListener(code -> {
|
|
||||||
// Quando o código é gerado, mostramos o diálogo de escolha
|
|
||||||
showCodeDialog(code);
|
|
||||||
});
|
|
||||||
|
|
||||||
bottomSheet.show(getParentFragmentManager(), "AddChildSheet");
|
bottomSheet.show(getParentFragmentManager(), "AddChildSheet");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- AQUI ESTÁ A MUDANÇA PRINCIPAL ---
|
|
||||||
private void showCodeDialog(String code) {
|
private void showCodeDialog(String code) {
|
||||||
if (getContext() == null) return;
|
if (getContext() == null) return;
|
||||||
|
|
||||||
// 1. Copia o código automaticamente para a área de transferência
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
ClipData clip = ClipData.newPlainText("Código", code);
|
ClipData clip = ClipData.newPlainText("Código", code);
|
||||||
clipboard.setPrimaryClip(clip);
|
clipboard.setPrimaryClip(clip);
|
||||||
Toast.makeText(getContext(), "Código copiado!", Toast.LENGTH_SHORT).show();
|
Toast.makeText(getContext(), "Código copiado!", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
// 2. Cria o Alerta com as Duas Opções
|
|
||||||
new AlertDialog.Builder(getContext())
|
new AlertDialog.Builder(getContext())
|
||||||
.setTitle("Filho Adicionado!")
|
.setTitle("Filho Adicionado!")
|
||||||
.setMessage("O código de acesso é: " + code + "\n\n(Já copiado automaticamente)")
|
.setMessage("O código de acesso é: " + code + "\n\n(Já copiado automaticamente)")
|
||||||
.setCancelable(false) // Impede fechar clicando fora
|
.setCancelable(false)
|
||||||
|
.setPositiveButton("Ir para o Mapa", (dialog, which) -> checkIfHasChildren())
|
||||||
// OPÇÃO 1: Ir para o Mapa (Atualiza a tela)
|
.setNegativeButton("Adicionar Outro", (dialog, which) -> dialog.dismiss())
|
||||||
.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();
|
.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user