a localizaçao esta funcionando + ou - certo

This commit is contained in:
2026-03-12 17:16:29 +00:00
parent fba454ce01
commit 86221e5179
5 changed files with 68 additions and 57 deletions

View File

@@ -18,12 +18,12 @@
<targets> <targets>
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=rogmvsfmmfus95e6" /> <DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Pixel_9_Pro.avd" />
</handle> </handle>
</Target> </Target>
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Pixel_9_Pro.avd" /> <DeviceId pluginId="LocalEmulator" identifier="path=/Users/230408/.android/avd/Pixel_9_Pro_filho.avd" />
</handle> </handle>
</Target> </Target>
</targets> </targets>

View File

@@ -4,16 +4,23 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<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" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<queries>
<package android:name="com.google.android.gms" />
<intent>
<action android:name="com.google.android.geo.API_KEY" />
</intent>
</queries>
<application <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
@@ -22,12 +29,17 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.PAP_FindU"> android:theme="@style/Theme.PAP_FindU"
tools:targetApi="34">
<meta-data <meta-data
android:name="com.google.android.geo.API_KEY" android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyAxen212OKqkfpu1AbWajLGTCTSdRhJWlM" /> android:value="AIzaSyAxen212OKqkfpu1AbWajLGTCTSdRhJWlM" />
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<service <service
android:name=".LocationService" android:name=".LocationService"
android:enabled="true" android:enabled="true"
@@ -37,9 +49,11 @@
<activity <activity
android:name=".CriarConta" android:name=".CriarConta"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".Recuperar_Passe" android:name=".Recuperar_Passe"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".ChatActivity" android:name=".ChatActivity"
android:exported="false" /> android:exported="false" />
@@ -56,6 +70,7 @@
<activity <activity
android:name=".AddZoneActivity" android:name=".AddZoneActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@@ -64,6 +79,7 @@
<activity <activity
android:name=".EditProfileActivity" android:name=".EditProfileActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".SecurityActivity" android:name=".SecurityActivity"
android:exported="false" /> android:exported="false" />

View File

@@ -14,6 +14,7 @@ import android.location.Location;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.util.Log; // Importação adicionada
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -26,9 +27,7 @@ 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 // Importações do Firebase
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;
@@ -55,15 +54,14 @@ public class LocationService extends Service {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
// ================================================================= // =================================================================
// AUTOMAÇÃO: Vai buscar o ID do Filho que está logado neste telemóvel // AUTOMAÇÃO: Vai buscar o código de 6 dígitos salvo no Login
// ================================================================= // =================================================================
FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); String childCode = getSharedPreferences("FindU_Prefs", MODE_PRIVATE)
.getString("child_access_code", null);
if (currentUser != null) { if (childCode != null) {
String myUid = currentUser.getUid(); // Pega o ID único gerado pelo Firebase // Cria a pasta usando o código de 6 dígitos (ex: users/484981/live_location)
databaseReference = FirebaseDatabase.getInstance().getReference("users/" + childCode + "/live_location");
// Cria a referência da base de dados usando este ID dinâmico
databaseReference = FirebaseDatabase.getInstance().getReference("users/" + myUid + "/live_location");
} }
} }
@@ -97,7 +95,7 @@ public class LocationService extends Service {
} }
} catch (Exception e) { } catch (Exception e) {
// Se o sistema operativo forçar uma paragem (muito comum em emuladores), apanhamos o erro aqui! Log.e("LocationService", "Erro ao iniciar Foreground Service: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
stopSelf(); stopSelf();
} }
@@ -123,7 +121,7 @@ public class LocationService extends Service {
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 // Verifica se a referência não é nula antes de enviar para o Firebase
if (databaseReference != null) { if (databaseReference != null) {
databaseReference.setValue(locationData); databaseReference.setValue(locationData);
} }
@@ -131,11 +129,22 @@ public class LocationService extends Service {
} }
}; };
// Outro try-catch de segurança para a comunicação com os serviços do Google (GMS) // MELHORIA AQUI: Try-catch mais abrangente para lidar com o erro de "Broker"
try { try {
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()); fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
} catch (SecurityException e) { } catch (Exception e) {
e.printStackTrace(); Log.e("LocationService", "Erro crítico nos Google Play Services (Broker): " + e.getMessage());
// Plano B: Tenta obter pelo menos a última localização conhecida
fusedLocationClient.getLastLocation().addOnSuccessListener(location -> {
if (location != null && databaseReference != null) {
Map<String, Object> locationData = new HashMap<>();
locationData.put("latitude", location.getLatitude());
locationData.put("longitude", location.getLongitude());
locationData.put("last_updated", System.currentTimeMillis());
databaseReference.setValue(locationData);
}
});
} }
} }
@@ -154,7 +163,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 NotificationManager.IMPORTANCE_LOW
); );
NotificationManager manager = getSystemService(NotificationManager.class); NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) { if (manager != null) {

View File

@@ -1,6 +1,7 @@
package com.example.pap_findu; package com.example.pap_findu;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; // <-- Importação adicionada automaticamente
import android.os.Bundle; import android.os.Bundle;
import android.text.InputType; import android.text.InputType;
import android.text.TextUtils; import android.text.TextUtils;
@@ -196,6 +197,14 @@ public class login_activity extends AppCompatActivity {
// 1. Invalida o código para ninguém usar de novo // 1. Invalida o código para ninguém usar de novo
db.collection("login_codes").document(code).update("used", true); db.collection("login_codes").document(code).update("used", true);
// =======================================================
// 2. NOVA LINHA: GUARDA O CÓDIGO NA MEMÓRIA DO TELEMÓVEL!
// =======================================================
getSharedPreferences("FindU_Prefs", MODE_PRIVATE)
.edit()
.putString("child_access_code", code)
.apply();
Toast.makeText(this, "Conectado como Filho!", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Conectado como Filho!", Toast.LENGTH_SHORT).show();
goToMainActivity(); goToMainActivity();
} else { } else {

View File

@@ -31,7 +31,7 @@ import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener; import 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.QueryDocumentSnapshot;
public class MapFragment extends Fragment implements OnMapReadyCallback { public class MapFragment extends Fragment implements OnMapReadyCallback {
@@ -41,13 +41,11 @@ public class MapFragment extends Fragment implements OnMapReadyCallback {
private FirebaseFirestore db; private FirebaseFirestore db;
private FirebaseAuth auth; private FirebaseAuth auth;
// Variáveis para o Mapa e Tempo Real
private GoogleMap mMap; private GoogleMap mMap;
private Marker childMarker; private Marker childMarker;
private DatabaseReference locationRef; private DatabaseReference locationRef;
private ValueEventListener locationListener; private ValueEventListener locationListener;
// VARIÁVEL ADICIONADA PARA GUARDAR O ID DA CRIANÇA AUTOMATICAMENTE
private String currentChildId = null; private String currentChildId = null;
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
@@ -83,7 +81,7 @@ public class MapFragment extends Fragment implements OnMapReadyCallback {
if (user == null) return; if (user == null) return;
if (user.isAnonymous()) { if (user.isAnonymous()) {
if (btnAddChild != null) btnAddChild.setVisibility(View.GONE); // Se o utilizador é anónimo (Filho), ele vê o próprio mapa
showMapState(); showMapState();
return; return;
} }
@@ -102,18 +100,15 @@ public class MapFragment extends Fragment implements OnMapReadyCallback {
.addOnCompleteListener(task -> { .addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) { if (task.isSuccessful() && task.getResult() != null && !task.getResult().isEmpty()) {
// ================================================================= // Pegamos no código de acesso do primeiro filho encontrado
// AUTOMAÇÃO: Encontra a criança e extrai o ID dela!
// =================================================================
var document = task.getResult().getDocuments().get(0); 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! // USAMOS O accessCode COMO CHAVE DE LIGAÇÃO
currentChildId = document.getString("uid"); currentChildId = document.getString("accessCode");
if (currentChildId != null) { if (currentChildId != null) {
showMapState(); // Se encontrou o ID, carrega o mapa showMapState();
} else { } else {
Toast.makeText(getContext(), "Erro: Criança encontrada, mas sem ID.", Toast.LENGTH_SHORT).show();
showEmptyState(); showEmptyState();
} }
} else { } else {
@@ -140,21 +135,15 @@ public class MapFragment extends Fragment implements OnMapReadyCallback {
@Override @Override
public void onMapReady(@NonNull GoogleMap googleMap) { public void onMapReady(@NonNull GoogleMap googleMap) {
mMap = googleMap; mMap = googleMap;
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(0, 0), 2f)); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(0, 0), 2f));
// Inicia a escuta APENAS se tivermos encontrado um ID válido
if (currentChildId != null) { if (currentChildId != null) {
startListeningToChildLocation(); startListeningToChildLocation();
} else {
Toast.makeText(getContext(), "A aguardar ligação à criança...", Toast.LENGTH_SHORT).show();
} }
} }
private void startListeningToChildLocation() { private void startListeningToChildLocation() {
// ================================================================= // Escuta na pasta baseada no Código de Acesso
// AUTOMAÇÃO: Usa a variável currentChildId em vez de texto fixo
// =================================================================
locationRef = FirebaseDatabase.getInstance().getReference("users/" + currentChildId + "/live_location"); locationRef = FirebaseDatabase.getInstance().getReference("users/" + currentChildId + "/live_location");
locationListener = new ValueEventListener() { locationListener = new ValueEventListener() {
@@ -168,25 +157,18 @@ public class MapFragment extends Fragment implements OnMapReadyCallback {
LatLng childPosition = new LatLng(lat, lng); LatLng childPosition = new LatLng(lat, lng);
if (childMarker == null) { if (childMarker == null) {
MarkerOptions markerOptions = new MarkerOptions() childMarker = mMap.addMarker(new MarkerOptions().position(childPosition).title("Filho"));
.position(childPosition) mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(childPosition, 15f));
.title("Criança");
childMarker = mMap.addMarker(markerOptions);
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(childPosition, 16f));
} else { } else {
childMarker.setPosition(childPosition); childMarker.setPosition(childPosition);
mMap.animateCamera(CameraUpdateFactory.newLatLng(childPosition));
} }
} }
} else {
Log.w("MapFragment", "Pasta de localização vazia ou inexistente para o ID: " + currentChildId);
} }
} }
@Override @Override
public void onCancelled(@NonNull DatabaseError error) { public void onCancelled(@NonNull DatabaseError error) {
Log.e("MapFragment", "Erro ao ler localização: " + error.getMessage()); Log.e("MapFragment", "Erro: " + error.getMessage());
} }
}; };
@@ -214,18 +196,13 @@ public class MapFragment extends Fragment implements OnMapReadyCallback {
private void showCodeDialog(String code) { private void showCodeDialog(String code) {
if (getContext() == null) return; if (getContext() == null) return;
ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Código", code); clipboard.setPrimaryClip(ClipData.newPlainText("Código", code));
clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(), "Código copiado!", Toast.LENGTH_SHORT).show();
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)
.setCancelable(false)
.setPositiveButton("Ir para o Mapa", (dialog, which) -> checkIfHasChildren()) .setPositiveButton("Ir para o Mapa", (dialog, which) -> checkIfHasChildren())
.setNegativeButton("Adicionar Outro", (dialog, which) -> dialog.dismiss())
.show(); .show();
} }
} }