diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index bd635d3..d7aed23 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -39,8 +39,9 @@ dependencies {
implementation ("com.google.android.gms:play-services-maps:18.2.0")
implementation ("com.google.android.gms:play-services-location:21.0.1")
implementation ("com.google.android.libraries.places:places:3.3.0")
+ implementation("com.google.firebase:firebase-firestore:24.10.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
-}
\ No newline at end of file
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c5c0844..3909bf4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -33,7 +33,7 @@
android:value="@string/api_key" />
@@ -43,4 +43,4 @@
-
\ No newline at end of file
+
diff --git a/app/src/main/java/com/example/map/MainActivity.java b/app/src/main/java/com/example/map/MapFragment.java
similarity index 68%
rename from app/src/main/java/com/example/map/MainActivity.java
rename to app/src/main/java/com/example/map/MapFragment.java
index a7434a6..626e5ae 100644
--- a/app/src/main/java/com/example/map/MainActivity.java
+++ b/app/src/main/java/com/example/map/MapFragment.java
@@ -1,20 +1,14 @@
package com.example.map;
import android.Manifest;
+import android.content.Intent;
import android.content.pm.PackageManager;
-import android.graphics.Color;
-import android.location.Address;
-import android.location.Geocoder;
import android.location.Location;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -28,30 +22,31 @@
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
+import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
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.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
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.android.gms.maps.model.PointOfInterest;
-import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
-import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
-import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
-import com.google.android.material.textfield.TextInputEditText;
import com.google.android.libraries.places.widget.AutocompleteSupportFragment;
import com.google.android.libraries.places.widget.listener.PlaceSelectionListener;
-import com.google.android.libraries.places.api.model.RectangularBounds;
-import com.google.android.libraries.places.api.net.PlacesClient;
-import com.google.android.gms.common.api.Status;
-//import com.google.android.libraries.places.widget.model.AutocompletePrediction;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.Timestamp;
+import com.google.firebase.firestore.CollectionReference;
+import com.google.firebase.firestore.FirebaseFirestore;
+import com.google.firebase.firestore.GeoPoint;
+import com.google.firebase.firestore.ListenerRegistration;
+import com.google.firebase.firestore.QueryDocumentSnapshot;
import org.json.JSONException;
import org.json.JSONObject;
@@ -72,7 +67,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
-public class MainActivity extends AppCompatActivity implements OnMapReadyCallback , GoogleMap.OnPoiClickListener {
+public class MapFragment extends AppCompatActivity implements OnMapReadyCallback , GoogleMap.OnPoiClickListener {
private GoogleMap mMap;
private static final String FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION;
@@ -81,18 +76,49 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
private static final float DEFAULT_ZOOM = 15f;
private Boolean mLocationPermissionsGranted = false;
private FusedLocationProviderClient mfusedLocationProviderClient;
- private static final String TAG = "MainActivity";
+ private static final String TAG = "MapReport";
private Toolbar toolbar;
private LinearLayout legendLayout; // Add this line
- private ImageButton legendButton; // Add this line
private List crimeData = new ArrayList<>();
private double currentLat = 0;
private double currentLng = 0;
-
+ private FirebaseFirestore db;
+ private ListenerRegistration reportListener;
+ private boolean isShowingNearbyPlaces = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
+ setContentView(R.layout.fragment_map);
+ FirebaseApp.initializeApp(this);
+ // Initialize Firestore
+ db = FirebaseFirestore.getInstance();
+ // Reference to the "report" collection
+ CollectionReference reportCollectionRef = db.collection("report");
+
+ // Fetch all documents from the "report" collection
+ reportCollectionRef.get()
+ .addOnSuccessListener(queryDocumentSnapshots -> {
+ for (QueryDocumentSnapshot document : queryDocumentSnapshots) {
+ // Convert each document to a Report object
+
+ Report report = document.toObject(Report.class);
+
+ // Access the fields of the Report object
+ String desc = report.getDesc();
+ GeoPoint location = report.getLocation();
+ Timestamp timestamp = report.getTimestamp();
+ String user = report.getUserId();
+ Log.d(TAG, "desc: " + desc);
+ Log.d(TAG, "location " + location.toString());
+ Log.d(TAG, "timestamp: " + timestamp.toString());
+ Log.d(TAG, "user: " + user);
+ // Use the data as needed
+ // For example, you can update UI or perform other operations
+ }
+ })
+ .addOnFailureListener(e -> {
+ Log.d(TAG, "Error: " + e.getMessage());
+ });
// Initialize Toolbar
toolbar = findViewById(R.id.toolbar);
@@ -119,6 +145,10 @@ protected void onCreate(Bundle savedInstanceState) {
ImageButton specificButton3 = findViewById(R.id.btnFireDepartment); // Replace with your button's ID
specificButton3.setOnClickListener(v -> onSpecificButtonClicked3());
+
+ // Start listening for report updates
+ startReportListener();
+
}
private void initLegend() {
// Reference to the legend layout
@@ -227,7 +257,7 @@ private void getDeviceLocation() {
moveCamera(new LatLng(currentLat, currentLng), DEFAULT_ZOOM, "My Location");
} else {
Log.d(TAG, "onComplete: current location is null");
- Toast.makeText(MainActivity.this, "Unable to get current location", Toast.LENGTH_SHORT).show();
+ Toast.makeText(MapFragment.this, "Unable to get current location", Toast.LENGTH_SHORT).show();
}
});
}
@@ -254,13 +284,13 @@ private float getMarkerColor(String crimeType) {
Log.d(TAG, "getMarkerColor: Crime Type: " + crimeType);
if ("Violent Crime".equals(crimeType)) {
Log.d(TAG, "getMarkerColor: Setting color to Red for Violent Crime");
- return BitmapDescriptorFactory.HUE_RED;
+ return BitmapDescriptorFactory.HUE_VIOLET;
} else if ("Property Crime".equals(crimeType)) {
Log.d(TAG, "getMarkerColor: Setting color to Blue for Property Crime");
- return BitmapDescriptorFactory.HUE_BLUE;
+ return BitmapDescriptorFactory.HUE_VIOLET;
}
Log.d(TAG, "getMarkerColor: Setting default color (Green)");
- return BitmapDescriptorFactory.HUE_GREEN; // Default color
+ return BitmapDescriptorFactory.HUE_RED; // Default color
}
private void iniMap() {
@@ -304,7 +334,13 @@ public void onMapReady(@NonNull GoogleMap googleMap) {
mMap.getUiSettings().setZoomControlsEnabled(true);
mMap.getUiSettings().setCompassEnabled(true);
-
+ mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
+ @Override
+ public boolean onMarkerClick(@NonNull Marker marker) {
+ marker.showInfoWindow();
+ return true;
+ }
+ });
// Set custom info window adapter
mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
@Nullable
@@ -315,14 +351,35 @@ public View getInfoContents(@NonNull Marker marker) {
@Override
public View getInfoWindow(Marker marker) {
+ LatLng destinationLatLng = marker.getPosition();
View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_window, null); // Inflate your custom layout
TextView title = infoWindow.findViewById(R.id.infoTitle);
+ TextView lat = infoWindow.findViewById(R.id.Loclat);
+ TextView Long = infoWindow.findViewById(R.id.LocLong);
+
title.setText(marker.getTitle()); // Set the crime type as title
+ lat.setText(Double.toString(destinationLatLng.latitude));
+ Long.setText(Double.toString(destinationLatLng.longitude));
+
// Add more information if needed
return infoWindow;
}
});
+ mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
+ @Override
+ public void onInfoWindowClick(@NonNull Marker marker) {
+ LatLng destinationLatLng = marker.getPosition();
+
+ // Start Google Maps intent for directions
+ Intent intent = new Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("http://maps.google.com/maps?daddr=" + destinationLatLng.latitude + "," + destinationLatLng.longitude)
+ );
+ intent.setPackage("com.google.android.apps.maps");
+ startActivity(intent);
+ }
+ });
for (Crime crime : crimeData) {
Log.d(TAG, "Crime: " + crime.toString()); // This will print the details of each crime
@@ -366,45 +423,6 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
}
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_main, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- int id = item.getItemId();
-
- if (id == R.id.mapNone) {
- setMapTypeWithDelay(GoogleMap.MAP_TYPE_NONE);
- } else if (id == R.id.mapNormal) {
- setMapTypeWithDelay(GoogleMap.MAP_TYPE_NORMAL);
- } else if (id == R.id.mapSatellite) {
- setMapTypeWithDelay(GoogleMap.MAP_TYPE_SATELLITE);
- } else if (id == R.id.mapHybrid) {
- setMapTypeWithDelay(GoogleMap.MAP_TYPE_HYBRID);
- } else if (id == R.id.mapTerrain) {
- setMapTypeWithDelay(GoogleMap.MAP_TYPE_TERRAIN);
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- private void setMapTypeWithDelay(final int mapType) {
- new android.os.Handler().postDelayed(
- () -> {
- if (mMap != null) {
- try {
- mMap.setMapType(mapType);
- } catch (Exception e) {
- Log.e(TAG, "Error changing map type: " + e.getMessage());
- }
- }
- },
- 1000 // 1 second delay, you can adjust this
- );
- }
@Override
public void onPoiClick(PointOfInterest poi) {
try {
@@ -417,7 +435,7 @@ public void onPoiClick(PointOfInterest poi) {
Log.e(TAG, "Error handling POI click: " + e.getMessage());
}
}
- private void parseResult(String data) {
+ private void parseResult(String data, String placeType) {
try {
JSONObject object = new JSONObject(data);
@@ -435,10 +453,15 @@ private void parseResult(String data) {
double lng = Double.parseDouble(Objects.requireNonNull(hashMapList.get("lng")));
String name = hashMapList.get("name");
LatLng latLng = new LatLng(lat, lng);
+ float markerColor = getMarkerColorForPlaceType(placeType);
+
if (i == 0) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 12));
}
- mMap.addMarker(new MarkerOptions().position(latLng).title(name));
+ mMap.addMarker(new MarkerOptions()
+ .position(latLng)
+ .title(name)
+ .icon(BitmapDescriptorFactory.defaultMarker(markerColor))); // Apply the marker color here
}
});
} else {
@@ -455,7 +478,8 @@ private void parseResult(String data) {
}
}
- private void getNearbyPlace(String command) {
+
+ private void getNearbyPlace(String command, String placeType) {
String url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json" +
"?location=" + currentLat + "," + currentLng +
"&radius=5000" + // specify the radius in meters
@@ -472,7 +496,7 @@ private void getNearbyPlace(String command) {
} catch (IOException e) {
throw new RuntimeException(e);
}
- }, executor).thenAccept(this::parseResult)
+ }, executor).thenAccept(data -> parseResult(data, placeType))
.exceptionally(ex -> {
Log.e(TAG, "Exception in CompletableFuture: " + ex.getMessage());
return null;
@@ -505,17 +529,124 @@ private String downloadUrl(String urlString) throws IOException {
// Method to be called when the specific button is clicked
private void onSpecificButtonClicked() {
getDeviceLocation();
- // Replace with desired place type: "police", "hospital", or "fire_station"
- getNearbyPlace("police");
+ if (isShowingNearbyPlaces) {
+ showAllNearbyCrimes();
+ } else {
+ getNearbyPlace("police", "police");
+ isShowingNearbyPlaces = true;
+ }
}
+
private void onSpecificButtonClicked2() {
getDeviceLocation();
- // Replace with desired place type: "police", "hospital", or "fire_station"
- getNearbyPlace("hospital");
+ if (isShowingNearbyPlaces) {
+ showAllNearbyCrimes();
+ } else {
+ getNearbyPlace("hospital", "hospital");
+ isShowingNearbyPlaces = true;
+ }
}
+
private void onSpecificButtonClicked3() {
getDeviceLocation();
- // Replace with desired place type: "police", "hospital", or "fire_station"
- getNearbyPlace("fire%20station");
+ if (isShowingNearbyPlaces) {
+ showAllNearbyCrimes();
+ } else {
+ getNearbyPlace("fire station", "fire_station");
+ isShowingNearbyPlaces = true;
+ }
+ }
+ private float getMarkerColorForPlaceType(String placeType) {
+ switch (placeType) {
+ case "police":
+ return BitmapDescriptorFactory.HUE_BLUE; // Set the color for police markers
+ case "hospital":
+ return BitmapDescriptorFactory.HUE_GREEN; // Set the color for hospital markers
+ case "fire_station":
+ return BitmapDescriptorFactory.HUE_ORANGE; // Set the color for fire station markers
+ default:
+ return BitmapDescriptorFactory.HUE_YELLOW; // Default color
+ }
+ }
+
+ private void showAllNearbyCrimes() {
+ // Assuming crime markers are already added to the map during onMapReady
+ moveCameraToUserLocation();
+ addCrimeMarkers(); // Add crime markers back to the map
+ isShowingNearbyPlaces = false;
+ }
+
+ private void addCrimeMarkers() {
+ for (Crime crime : crimeData) {
+ LatLng crimeLocation = new LatLng(crime.getLatitude(), crime.getLongitude());
+ float markerColor = getMarkerColor(crime.getCrimeType());
+
+ Log.d(TAG, "Adding marker: " + crime.getCrimeType() + " with color: " + markerColor);
+ mMap.addMarker(new MarkerOptions()
+ .position(crimeLocation)
+ .title(crime.getCrimeType())
+ .icon(BitmapDescriptorFactory.defaultMarker(markerColor)));
+ }
}
-}
\ No newline at end of file
+ private void moveCameraToUserLocation() {
+ if (currentLat != 0 && currentLng != 0) {
+ LatLng userLocation = new LatLng(currentLat, currentLng);
+ mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(userLocation, DEFAULT_ZOOM));
+ }
+ }
+
+ private void startReportListener() {
+ try {
+ Log.d(TAG, "startReportListener: Start listening for reports");
+ FirebaseFirestore fStore = FirebaseFirestore.getInstance();
+ CollectionReference reportsRef = fStore.collection("report");
+
+ reportsRef.get()
+ .addOnSuccessListener(queryDocumentSnapshots -> {
+ Toast.makeText(this, "Obtaining Report location nearby", Toast.LENGTH_LONG).show();
+ for (QueryDocumentSnapshot document : queryDocumentSnapshots) {
+ Report report = document.toObject(Report.class);
+
+ // Log details of the report
+
+ // Add marker for the report
+ addReportMarker(report);
+ }
+ })
+ .addOnFailureListener(e -> {
+ Log.e(TAG, "Error fetching reports: ", e);
+ });
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in startReportListener: " + e.getMessage());
+ }
+ }
+
+
+ private void addReportMarker(Report report) {
+ // Access the GeoPoint from the report
+ GeoPoint geoPoint = report.getLocation(); // Assuming report now has a GeoPoint field
+
+ // Create a LatLng object from the GeoPoint
+ LatLng reportLocation = new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude());
+ // Create a purple marker icon
+ BitmapDescriptor markerIcon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_VIOLET);
+
+ // Add the marker to the map
+ mMap.addMarker(new MarkerOptions()
+ .position(reportLocation)
+ .title(report.getDesc())
+ .icon(markerIcon) // Set the marker icon to the purple color
+ );
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ // Stop listening for updates when the activity is destroyed
+ if (reportListener != null) {
+ reportListener.remove();
+ }
+ }
+
+
+}
diff --git a/app/src/main/java/com/example/map/Report.java b/app/src/main/java/com/example/map/Report.java
new file mode 100644
index 0000000..4540ecd
--- /dev/null
+++ b/app/src/main/java/com/example/map/Report.java
@@ -0,0 +1,33 @@
+package com.example.map;
+
+import com.google.firebase.Timestamp;
+import com.google.firebase.firestore.GeoPoint;
+
+public class Report {
+ private String desc;
+ private GeoPoint location;
+ private Timestamp timestamp;
+ private String user;
+
+ // Constructor, getters, and setters
+
+ public Report() {
+ // Default constructor required for Firestore
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public GeoPoint getLocation() {
+ return location;
+ }
+
+ public Timestamp getTimestamp() {
+ return timestamp;
+ }
+
+ public String getUser() {
+ return user;
+ }
+}
diff --git a/app/src/main/res/drawable/ic_purple.png b/app/src/main/res/drawable/ic_purple.png
new file mode 100644
index 0000000..03a92db
Binary files /dev/null and b/app/src/main/res/drawable/ic_purple.png differ
diff --git a/app/src/main/res/drawable/orange.png b/app/src/main/res/drawable/orange.png
new file mode 100644
index 0000000..a611d33
Binary files /dev/null and b/app/src/main/res/drawable/orange.png differ
diff --git a/app/src/main/res/layout/custom_info_window.xml b/app/src/main/res/layout/custom_info_window.xml
index 948d29d..2f2636a 100644
--- a/app/src/main/res/layout/custom_info_window.xml
+++ b/app/src/main/res/layout/custom_info_window.xml
@@ -11,6 +11,22 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"/>
-
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/fragment_map.xml
similarity index 76%
rename from app/src/main/res/layout/activity_main.xml
rename to app/src/main/res/layout/fragment_map.xml
index 63ca89d..4f040ca 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/fragment_map.xml
@@ -11,7 +11,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/autocomplete_fragment_container"
- tools:context=".MainActivity" />
+ android:layout_marginTop="4dp"
+ tools:context=".MapFragment" />
+
+
-
-
+
+
@@ -104,7 +107,7 @@
+ android:src="@drawable/red" />
+ android:src="@drawable/ic_purple" />
+
+
+
+
+
+
+
+
+