app
This commit is contained in:
@@ -0,0 +1,122 @@
|
|||||||
|
package com.example.pap_findu;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.pap_findu.models.AlertMessage;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
|
||||||
|
import com.google.android.gms.location.Geofence;
|
||||||
|
import com.google.android.gms.location.GeofencingEvent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class GeofenceBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private static final String TAG = "GeofenceReceiver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
|
||||||
|
if (geofencingEvent.hasError()) {
|
||||||
|
Log.e(TAG, "Geofencing error: " + geofencingEvent.getErrorCode());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the transition type
|
||||||
|
int geofenceTransition = geofencingEvent.getGeofenceTransition();
|
||||||
|
|
||||||
|
// Test that the reported transition was of interest
|
||||||
|
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT || geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
|
||||||
|
|
||||||
|
// Get the geofences that were triggered
|
||||||
|
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
|
||||||
|
|
||||||
|
if (triggeringGeofences != null && !triggeringGeofences.isEmpty()) {
|
||||||
|
String requestId = triggeringGeofences.get(0).getRequestId();
|
||||||
|
|
||||||
|
FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
FirebaseAuth auth = FirebaseAuth.getInstance();
|
||||||
|
FirebaseUser user = auth.getCurrentUser();
|
||||||
|
|
||||||
|
db.collection("SafeZones").document(requestId).get()
|
||||||
|
.addOnSuccessListener(documentSnapshot -> {
|
||||||
|
String zoneName = "Zona Desconhecida";
|
||||||
|
if (documentSnapshot.exists() && documentSnapshot.getString("name") != null) {
|
||||||
|
zoneName = documentSnapshot.getString("name");
|
||||||
|
}
|
||||||
|
salvarAlerta(context, db, user, geofenceTransition, zoneName);
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
salvarAlerta(context, db, user, geofenceTransition, "Zona Desconhecida");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "GEOFENCE_TRANSITION INVALID TYPE: " + geofenceTransition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void salvarAlerta(Context context, FirebaseFirestore db, FirebaseUser user, int geofenceTransition, String zoneName) {
|
||||||
|
// TAREFA 1: Recuperar childCode das SharedPreferences com a mesma chave de isolamento
|
||||||
|
String childCode = context.getSharedPreferences("FindU_Prefs", Context.MODE_PRIVATE)
|
||||||
|
.getString("child_access_code", null);
|
||||||
|
|
||||||
|
Log.d(TAG, "salvarAlerta -> childCode obtido: [" + childCode + "]");
|
||||||
|
Log.d("FindU_Flow", "Gravando alerta para o código: " + childCode);
|
||||||
|
|
||||||
|
if (childCode == null || childCode.isEmpty() || childCode.equals("Unknown")) {
|
||||||
|
Log.e(TAG, "ERRO: O filho tentou gerar um alerta mas o childCode é nulo/vazio! Alerta NÃO gravado.");
|
||||||
|
return; // Não grava o alerta órfão
|
||||||
|
}
|
||||||
|
|
||||||
|
String docId = db.collection("Alerts").document().getId();
|
||||||
|
|
||||||
|
String type;
|
||||||
|
String status;
|
||||||
|
String title;
|
||||||
|
String message;
|
||||||
|
|
||||||
|
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
|
||||||
|
type = "URGENT";
|
||||||
|
status = "PENDING";
|
||||||
|
title = "Saída da Zona Segura";
|
||||||
|
message = "O filho saiu da zona: " + zoneName;
|
||||||
|
} else {
|
||||||
|
type = "INFO";
|
||||||
|
status = "RESOLVED";
|
||||||
|
title = "Entrada na Zona Segura";
|
||||||
|
message = "O filho entrou na zona: " + zoneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OBRIGATÓRIO: Usar HashMap para garantir que childCode está presente no documento
|
||||||
|
Map<String, Object> alertData = new HashMap<>();
|
||||||
|
alertData.put("id", docId);
|
||||||
|
alertData.put("title", title);
|
||||||
|
alertData.put("message", message);
|
||||||
|
alertData.put("timestamp", new Date());
|
||||||
|
alertData.put("type", type);
|
||||||
|
alertData.put("status", status);
|
||||||
|
alertData.put("childCode", childCode); // OBRIGATÓRIO: campo de isolamento
|
||||||
|
|
||||||
|
db.collection("Alerts").document(docId).set(alertData)
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
Log.d(TAG, "✅ Alerta gravado com SUCESSO. docId=" + docId + ", childCode=" + childCode);
|
||||||
|
Log.d("FindU_Flow", "Alerta gravado: docId=" + docId + " | childCode=" + childCode + " | type=" + type);
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
Log.e(TAG, "❌ ERRO ao gravar alerta: " + e.getMessage(), e);
|
||||||
|
Log.e("FindU_Flow", "Falha ao gravar alerta para childCode=" + childCode + ": " + e.getMessage());
|
||||||
|
});
|
||||||
|
|
||||||
|
Log.w(TAG, message);
|
||||||
|
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
123
app/src/main/java/com/example/pap_findu/GeofenceManager.java
Normal file
123
app/src/main/java/com/example/pap_findu/GeofenceManager.java
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package com.example.pap_findu;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.android.gms.location.Geofence;
|
||||||
|
import com.google.android.gms.location.GeofencingClient;
|
||||||
|
import com.google.android.gms.location.GeofencingRequest;
|
||||||
|
import com.google.android.gms.location.LocationServices;
|
||||||
|
import com.google.firebase.firestore.DocumentSnapshot;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GeofenceManager {
|
||||||
|
|
||||||
|
private static final String TAG = "GeofenceManager";
|
||||||
|
private final Context context;
|
||||||
|
private final GeofencingClient geofencingClient;
|
||||||
|
private final FirebaseFirestore db;
|
||||||
|
private PendingIntent geofencePendingIntent;
|
||||||
|
|
||||||
|
public GeofenceManager(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
this.geofencingClient = LocationServices.getGeofencingClient(context);
|
||||||
|
this.db = FirebaseFirestore.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupGeofences() {
|
||||||
|
// Isolamento de dados: só escutar zonas deste filho
|
||||||
|
String childCode = context.getSharedPreferences("FindU_Prefs", Context.MODE_PRIVATE)
|
||||||
|
.getString("child_access_code", null);
|
||||||
|
if (childCode == null) {
|
||||||
|
Log.w(TAG, "childCode nulo, geofences não serão configuradas.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escuta a coleção "SafeZones" filtrada por childCode em tempo real
|
||||||
|
db.collection("SafeZones")
|
||||||
|
.whereEqualTo("childCode", childCode)
|
||||||
|
.addSnapshotListener((value, error) -> {
|
||||||
|
if (error != null) {
|
||||||
|
Log.e(TAG, "Listen failed: " + error.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
List<Geofence> geofenceList = new ArrayList<>();
|
||||||
|
for (DocumentSnapshot doc : value.getDocuments()) {
|
||||||
|
String id = doc.getId();
|
||||||
|
Double lat = doc.getDouble("latitude");
|
||||||
|
Double lng = doc.getDouble("longitude");
|
||||||
|
Double radius = doc.getDouble("radius");
|
||||||
|
|
||||||
|
if (radius == null) radius = 200.0;
|
||||||
|
|
||||||
|
if (lat != null && lng != null) {
|
||||||
|
Geofence geofence = new Geofence.Builder()
|
||||||
|
.setRequestId(id)
|
||||||
|
.setCircularRegion(lat, lng, radius.floatValue())
|
||||||
|
.setExpirationDuration(Geofence.NEVER_EXPIRE)
|
||||||
|
// Detetar entrada E saída da zona segura.
|
||||||
|
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
geofenceList.add(geofence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!geofenceList.isEmpty()) {
|
||||||
|
registerGeofences(geofenceList);
|
||||||
|
} else {
|
||||||
|
removeGeofences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerGeofences(List<Geofence> geofenceList) {
|
||||||
|
GeofencingRequest geofencingRequest = new GeofencingRequest.Builder()
|
||||||
|
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_EXIT)
|
||||||
|
.addGeofences(geofenceList)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
geofencingClient.addGeofences(geofencingRequest, getGeofencePendingIntent())
|
||||||
|
.addOnSuccessListener(aVoid -> Log.d(TAG, "Geofences adicionadas com sucesso!"))
|
||||||
|
.addOnFailureListener(e -> Log.e(TAG, "Erro ao adicionar geofences: " + e.getMessage()));
|
||||||
|
} catch (SecurityException securityException) {
|
||||||
|
Log.e(TAG, "Falta permissão para Geofences: " + securityException.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeGeofences() {
|
||||||
|
if (geofencePendingIntent != null) {
|
||||||
|
geofencingClient.removeGeofences(geofencePendingIntent)
|
||||||
|
.addOnSuccessListener(aVoid -> Log.d(TAG, "Geofences removidas com sucesso"))
|
||||||
|
.addOnFailureListener(e -> Log.e(TAG, "Erro ao remover geofences: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingIntent getGeofencePendingIntent() {
|
||||||
|
if (geofencePendingIntent != null) {
|
||||||
|
return geofencePendingIntent;
|
||||||
|
}
|
||||||
|
Intent intent = new Intent(context, GeofenceBroadcastReceiver.class);
|
||||||
|
|
||||||
|
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
flags |= PendingIntent.FLAG_MUTABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
geofencePendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags);
|
||||||
|
return geofencePendingIntent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package com.example.pap_findu.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_findu.R;
|
||||||
|
import com.example.pap_findu.models.AlertMessage;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth; // ADICIONADO
|
||||||
|
import com.google.firebase.auth.FirebaseUser; // ADICIONADO
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class AlertsAdapter extends RecyclerView.Adapter<AlertsAdapter.AlertViewHolder> {
|
||||||
|
|
||||||
|
private List<AlertMessage> alertsList = new ArrayList<>();
|
||||||
|
private Context context;
|
||||||
|
private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault());
|
||||||
|
|
||||||
|
public AlertsAdapter(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlertsList(List<AlertMessage> alertsList) {
|
||||||
|
this.alertsList = alertsList;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AlertViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(context).inflate(R.layout.item_alert, parent, false);
|
||||||
|
return new AlertViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull AlertViewHolder holder, int position) {
|
||||||
|
AlertMessage alert = alertsList.get(position);
|
||||||
|
|
||||||
|
holder.tvAlertTitle.setText(alert.getTitle() != null ? alert.getTitle() : "");
|
||||||
|
holder.tvAlertMessage.setText(alert.getMessage() != null ? alert.getMessage() : "");
|
||||||
|
|
||||||
|
if (alert.getTimestamp() != null) {
|
||||||
|
holder.tvAlertDate.setText(sdf.format(alert.getTimestamp()));
|
||||||
|
} else {
|
||||||
|
holder.tvAlertDate.setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// VERIFICAÇÃO DE QUEM ESTÁ LOGADO
|
||||||
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
boolean isParent = (user != null && !user.isAnonymous());
|
||||||
|
|
||||||
|
if ("RESOLVED".equalsIgnoreCase(alert.getStatus())) {
|
||||||
|
holder.flAlertIconBg.setBackgroundResource(R.drawable.bg_alert_icon_info);
|
||||||
|
holder.ivAlertIcon.setImageResource(android.R.drawable.ic_dialog_info);
|
||||||
|
holder.ivAlertIcon.setColorFilter(Color.parseColor("#1F6AEF"));
|
||||||
|
|
||||||
|
holder.tvAlertStatus.setBackgroundResource(R.drawable.bg_chip_success);
|
||||||
|
holder.tvAlertStatus.setText("Resolvido");
|
||||||
|
holder.tvAlertStatus.setTextColor(Color.parseColor("#0F7A45"));
|
||||||
|
|
||||||
|
holder.llAlertActions.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
holder.flAlertIconBg.setBackgroundResource(R.drawable.bg_alert_icon_warning);
|
||||||
|
holder.ivAlertIcon.setImageResource(R.drawable.ic_alert_warning);
|
||||||
|
holder.ivAlertIcon.setColorFilter(null);
|
||||||
|
|
||||||
|
holder.tvAlertStatus.setBackgroundResource(R.drawable.bg_chip_warning);
|
||||||
|
holder.tvAlertStatus.setText("Urgente");
|
||||||
|
holder.tvAlertStatus.setTextColor(Color.parseColor("#B45309"));
|
||||||
|
|
||||||
|
// LOGICA DE SEGURANÇA: Só mostra os botões de ação se for o PAI
|
||||||
|
if (isParent) {
|
||||||
|
holder.llAlertActions.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
holder.llAlertActions.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.btnContactar.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||||
|
v.getContext().startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
holder.btnResolver.setOnClickListener(v -> {
|
||||||
|
// DUPLA SEGURANÇA: Verifica novamente no clique
|
||||||
|
if (isParent && alert.getId() != null) {
|
||||||
|
FirebaseFirestore.getInstance().collection("Alerts").document(alert.getId())
|
||||||
|
.update("status", "RESOLVED")
|
||||||
|
.addOnSuccessListener(aVoid -> Toast.makeText(context, "Alerta resolvido!", Toast.LENGTH_SHORT).show());
|
||||||
|
} else if (!isParent) {
|
||||||
|
Toast.makeText(context, "Apenas os pais podem resolver alertas.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return alertsList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AlertViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
FrameLayout flAlertIconBg;
|
||||||
|
ImageView ivAlertIcon;
|
||||||
|
TextView tvAlertTitle, tvAlertMessage, tvAlertStatus, tvAlertDate;
|
||||||
|
LinearLayout llAlertActions;
|
||||||
|
TextView btnContactar, btnResolver;
|
||||||
|
|
||||||
|
public AlertViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
flAlertIconBg = itemView.findViewById(R.id.flAlertIconBg);
|
||||||
|
ivAlertIcon = itemView.findViewById(R.id.ivAlertIcon);
|
||||||
|
tvAlertTitle = itemView.findViewById(R.id.tvAlertTitle);
|
||||||
|
tvAlertMessage = itemView.findViewById(R.id.tvAlertMessage);
|
||||||
|
tvAlertStatus = itemView.findViewById(R.id.tvAlertStatus);
|
||||||
|
tvAlertDate = itemView.findViewById(R.id.tvAlertDate);
|
||||||
|
llAlertActions = itemView.findViewById(R.id.llAlertActions);
|
||||||
|
btnContactar = itemView.findViewById(R.id.btnContactar);
|
||||||
|
btnResolver = itemView.findViewById(R.id.btnResolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.example.pap_findu.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import com.example.pap_findu.R;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.pap_findu.models.AlertMessage;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class HistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
private static final int TYPE_HEADER = 0;
|
||||||
|
private static final int TYPE_EVENT = 1;
|
||||||
|
private List<Object> items = new ArrayList<>();
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
// Construtor atualizado para receber o contexto
|
||||||
|
public HistoryAdapter(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(List<Object> items) {
|
||||||
|
this.items = items;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
if (items.get(position) instanceof String) return TYPE_HEADER;
|
||||||
|
return TYPE_EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
if (viewType == TYPE_HEADER) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_history_header, parent, false);
|
||||||
|
return new HeaderViewHolder(view);
|
||||||
|
} else {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_history_event, parent, false);
|
||||||
|
return new EventViewHolder(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
if (getItemViewType(position) == TYPE_HEADER) {
|
||||||
|
((HeaderViewHolder) holder).tvHeaderDate.setText((String) items.get(position));
|
||||||
|
} else {
|
||||||
|
AlertMessage alert = (AlertMessage) items.get(position);
|
||||||
|
EventViewHolder ev = (EventViewHolder) holder;
|
||||||
|
|
||||||
|
ev.tvEventTitle.setText(alert.getTitle() != null ? alert.getTitle() : "Evento");
|
||||||
|
ev.tvEventDesc.setText(alert.getMessage() != null ? alert.getMessage() : "");
|
||||||
|
|
||||||
|
if (alert.getTimestamp() != null) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
|
||||||
|
ev.tvEventTime.setText(sdf.format(alert.getTimestamp()));
|
||||||
|
}
|
||||||
|
|
||||||
|
String title = alert.getTitle() != null ? alert.getTitle().toLowerCase() : "";
|
||||||
|
if (title.contains("entrada")) {
|
||||||
|
ev.tvEventIconText.setText("🏠");
|
||||||
|
ev.flEventIconBg.setBackgroundColor(Color.parseColor("#E8F5E9"));
|
||||||
|
} else if (title.contains("saída")) {
|
||||||
|
ev.tvEventIconText.setText("🚪");
|
||||||
|
ev.flEventIconBg.setBackgroundColor(Color.parseColor("#FFF3E0"));
|
||||||
|
} else if (title.contains("movimento") || title.contains("deslocação")) {
|
||||||
|
ev.tvEventIconText.setText("🚶");
|
||||||
|
ev.flEventIconBg.setBackgroundColor(Color.parseColor("#E0E7FF"));
|
||||||
|
} else if (title.contains("paragem")) {
|
||||||
|
ev.tvEventIconText.setText("🛑");
|
||||||
|
ev.flEventIconBg.setBackgroundColor(Color.parseColor("#FEE2E2"));
|
||||||
|
} else {
|
||||||
|
ev.tvEventIconText.setText("📌");
|
||||||
|
ev.flEventIconBg.setBackgroundColor(Color.parseColor("#F3F4F6"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() { return items.size(); }
|
||||||
|
|
||||||
|
static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvHeaderDate;
|
||||||
|
HeaderViewHolder(View itemView) { super(itemView); tvHeaderDate = itemView.findViewById(R.id.tvHeaderDate); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EventViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
FrameLayout flEventIconBg;
|
||||||
|
TextView tvEventIconText, tvEventTitle, tvEventDesc, tvEventTime;
|
||||||
|
EventViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
flEventIconBg = itemView.findViewById(R.id.flEventIconBg);
|
||||||
|
tvEventIconText = itemView.findViewById(R.id.tvEventIconText);
|
||||||
|
tvEventTitle = itemView.findViewById(R.id.tvEventTitle);
|
||||||
|
tvEventDesc = itemView.findViewById(R.id.tvEventDesc);
|
||||||
|
tvEventTime = itemView.findViewById(R.id.tvEventTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package com.example.pap_findu.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_findu.R;
|
||||||
|
import com.google.firebase.firestore.DocumentSnapshot;
|
||||||
|
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ZonesAdapter extends RecyclerView.Adapter<ZonesAdapter.ZoneViewHolder> {
|
||||||
|
|
||||||
|
private List<DocumentSnapshot> zonesList = new ArrayList<>();
|
||||||
|
private boolean isChild = false;
|
||||||
|
|
||||||
|
public void setChildMode(boolean isChild) {
|
||||||
|
this.isChild = isChild;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZonesList(List<DocumentSnapshot> zonesList) {
|
||||||
|
this.zonesList = zonesList;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ZoneViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_zone, parent, false);
|
||||||
|
return new ZoneViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ZoneViewHolder holder, int position) {
|
||||||
|
DocumentSnapshot document = zonesList.get(position);
|
||||||
|
String name = document.getString("name");
|
||||||
|
String address = document.getString("address");
|
||||||
|
Double radius = document.getDouble("radius");
|
||||||
|
|
||||||
|
if (name != null) holder.tvZoneName.setText(name);
|
||||||
|
if (address != null) holder.tvZoneAddress.setText(address);
|
||||||
|
if (radius != null) {
|
||||||
|
holder.tvZoneRadius.setText(radius + "m");
|
||||||
|
} else {
|
||||||
|
holder.tvZoneRadius.setText("200m");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's set an icon dynamically or statically for now
|
||||||
|
holder.tvZoneIcon.setText("\uD83D\uDCCD"); // Pin icon
|
||||||
|
|
||||||
|
if (isChild) {
|
||||||
|
holder.ivDeleteZone.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
holder.ivDeleteZone.setVisibility(View.VISIBLE);
|
||||||
|
holder.ivDeleteZone.setOnClickListener(v -> {
|
||||||
|
new AlertDialog.Builder(v.getContext())
|
||||||
|
.setTitle("Remover Zona")
|
||||||
|
.setMessage("Tem a certeza que deseja eliminar esta zona segura?")
|
||||||
|
.setPositiveButton("Eliminar", (dialog, which) -> {
|
||||||
|
String zoneId = document.getId();
|
||||||
|
FirebaseFirestore.getInstance().collection("SafeZones").document(zoneId)
|
||||||
|
.delete()
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
Toast.makeText(v.getContext(), "Zona removida com sucesso.", Toast.LENGTH_SHORT).show();
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
Toast.makeText(v.getContext(), "Erro ao remover zona.", Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return zonesList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ZoneViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvZoneName, tvZoneAddress, tvZoneRadius, tvZoneIcon;
|
||||||
|
ImageView ivDeleteZone;
|
||||||
|
|
||||||
|
public ZoneViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
tvZoneName = itemView.findViewById(R.id.tvZoneName);
|
||||||
|
tvZoneAddress = itemView.findViewById(R.id.tvZoneAddress);
|
||||||
|
tvZoneRadius = itemView.findViewById(R.id.tvZoneRadius);
|
||||||
|
tvZoneIcon = itemView.findViewById(R.id.tvZoneIcon);
|
||||||
|
ivDeleteZone = itemView.findViewById(R.id.ivDeleteZone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.example.pap_findu.models;
|
||||||
|
|
||||||
|
import com.google.firebase.firestore.DocumentId;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class AlertMessage {
|
||||||
|
|
||||||
|
@DocumentId
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
private String message;
|
||||||
|
private Date timestamp;
|
||||||
|
private String type; // "URGENT" ou "INFO"
|
||||||
|
private String status; // "PENDING" ou "RESOLVED"
|
||||||
|
private String childCode; // Código do filho para isolamento de dados
|
||||||
|
|
||||||
|
// Required empty constructor for Firestore
|
||||||
|
public AlertMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlertMessage(String id, String title, String message, Date timestamp, String type, String status) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.message = message;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.type = type;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(Date timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildCode() {
|
||||||
|
return childCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildCode(String childCode) {
|
||||||
|
this.childCode = childCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
128
app/src/main/res/layout/item_alert.xml
Normal file
128
app/src/main/res/layout/item_alert.xml
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cardBackgroundColor="#FFFFFF"
|
||||||
|
app:cardCornerRadius="22dp"
|
||||||
|
app:strokeColor="#E1E5EF"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/flAlertIconBg"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:background="@drawable/bg_alert_icon_warning">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivAlertIcon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="14dp"
|
||||||
|
android:src="@drawable/ic_alert_warning" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAlertTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Alert Title"
|
||||||
|
android:textColor="#111827"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAlertMessage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:text="Alert Message"
|
||||||
|
android:textColor="#6B7280" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAlertStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_chip_warning"
|
||||||
|
android:paddingHorizontal="10dp"
|
||||||
|
android:paddingVertical="4dp"
|
||||||
|
android:text="Urgente"
|
||||||
|
android:textColor="#B45309"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAlertDate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Data e Hora"
|
||||||
|
android:textColor="#0F172A"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llAlertActions"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnContactar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="Contactar"
|
||||||
|
android:textColor="#3B82F6"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:drawableStartCompat="@android:drawable/ic_menu_call"
|
||||||
|
app:drawableTint="#3B82F6"
|
||||||
|
android:drawablePadding="6dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnResolver"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="Marcar Resolvido"
|
||||||
|
android:textColor="#10B981"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:drawableStartCompat="@android:drawable/ic_menu_save"
|
||||||
|
app:drawableTint="#10B981"
|
||||||
|
android:drawablePadding="6dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
71
app/src/main/res/layout/item_history_event.xml
Normal file
71
app/src/main/res/layout/item_history_event.xml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
app:cardBackgroundColor="#FFFFFF"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:strokeColor="#E1E5EF"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/flEventIconBg"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/bg_zone_icon_primary">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEventIconText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="🏠"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEventTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Chegou a Casa"
|
||||||
|
android:textColor="#0F172A"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEventDesc"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Localização segura confirmada"
|
||||||
|
android:textColor="#6B7280"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEventTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="13:30"
|
||||||
|
android:textColor="#9CA3AF"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
20
app/src/main/res/layout/item_history_header.xml
Normal file
20
app/src/main/res/layout/item_history_header.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
android:paddingTop="24dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvHeaderDate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Hoje, 12 Dezembro"
|
||||||
|
android:textColor="#6B7280"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
105
app/src/main/res/layout/item_zone.xml
Normal file
105
app/src/main/res/layout/item_zone.xml
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cardBackgroundColor="#FFFFFF"
|
||||||
|
app:cardCornerRadius="24dp"
|
||||||
|
app:strokeColor="#E1E5EF"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:background="@drawable/bg_zone_icon_primary">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvZoneIcon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="📍"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvZoneName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Zone Name"
|
||||||
|
android:textColor="#0F172A"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvZoneAddress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:text="Zone Address"
|
||||||
|
android:textColor="#6B7280" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvZoneStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_chip_success"
|
||||||
|
android:text="Safe"
|
||||||
|
android:textColor="#0F7A45"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivDeleteZone"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:src="@android:drawable/ic_menu_delete"
|
||||||
|
app:tint="#EF4444"
|
||||||
|
android:visibility="visible" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvZoneRadiusLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_chip_muted"
|
||||||
|
android:text="Radius"
|
||||||
|
android:textColor="#4B5563" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvZoneRadius"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="200m"
|
||||||
|
android:textColor="#9CA3AF" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
Reference in New Issue
Block a user