diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..530de1f
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..6c93e6d
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/com/example/acloc/MainActivity.java b/app/src/main/java/com/example/acloc/MainActivity.java
new file mode 100644
index 0000000..d7edf67
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/MainActivity.java
@@ -0,0 +1,275 @@
+package com.example.acloc;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.view.menu.MenuBuilder;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.example.acloc.activities.ManageRolesActivity;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.dialog.AlertChangePasswordDialog;
+import com.example.acloc.dialog.AlertViewOrUpdateProfileDialog;
+import com.example.acloc.fragments.FavoriteFragment;
+import com.example.acloc.fragments.MapFragment;
+import com.example.acloc.fragments.MyReportsFragment;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.util.Locale;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class MainActivity extends AppCompatActivity {
+ private static final String TAG = MainActivity.class.getSimpleName();
+ private RelativeLayout rlMainActivity;
+ private BottomNavigationView bottomNavigationView;
+ private Toolbar toolbar;
+ private Dialog dialog;
+ private Context context;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Get saved language from SharedPreferences
+ String savedLanguage = SharedPref.getLanguage(this);
+
+ // Check if current language matches saved
+ if (!Locale.getDefault().getLanguage().equals(savedLanguage)) {
+ setLocale(savedLanguage); // Apply only if needed
+ }
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ initToolbar();
+ initUI();
+ initObj();
+ initListeners();
+ // Load default fragment
+ loadDefaultFragment(savedInstanceState);
+ getRoleUuids();
+ }
+
+ private void initToolbar() {
+ toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ }
+
+ private void initUI() {
+ rlMainActivity = findViewById(R.id.rlMainActivity);
+ bottomNavigationView = findViewById(R.id.bottomNavView);
+ }
+
+ @SuppressLint("RestrictedApi")
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_dashboard, menu);
+ if (menu instanceof MenuBuilder) {
+ MenuBuilder m = (MenuBuilder) menu;
+ m.setOptionalIconsVisible(true);
+ }
+
+ // show/hide the "Manage Roles" menu item
+ MenuItem manageNotesItem = menu.findItem(R.id.menu_manageRoles);
+ if (!"admin".equalsIgnoreCase(SharedPref.getRole(context))) {
+ manageNotesItem.setVisible(false); // hide for non-admins
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ int id = item.getItemId();
+
+ if (id == R.id.menu_profile) {
+ dialog = new AlertViewOrUpdateProfileDialog(context)
+ .openProfileDialog();
+ return true;
+ } else if (id == R.id.menu_changePassword) {
+ dialog = new AlertChangePasswordDialog(context)
+ .openChangePasswordDialog();
+ } else if (id == R.id.menu_changeLanguage) {
+ changeLanguage();
+ } else if (id == R.id.menu_manageRoles) {
+ Helper.goTo(MainActivity.this, ManageRolesActivity.class);
+ } else if (id == R.id.menu_logout) {
+ AlertDialog dialog = DialogUtils.logoutDialog(context);
+ dialog.show();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void changeLanguage() {
+ String currentLanguage = Locale.getDefault().getLanguage();
+ String newLanguage = currentLanguage.equals("es") ? "en" : "es";
+
+ AlertDialog dialog = DialogUtils.confirmationDialog(
+ this,
+ getString(R.string.change_language_confirmation),
+ (dialogInterface, i) -> {
+ SharedPref.setLanguage(this, newLanguage); // Save to SharedPref
+ setLocale(newLanguage); // Apply language
+
+ // Restart app to apply change
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ finish();
+ }
+ );
+ dialog.show();
+ }
+
+ // Method to update the language dynamically
+ private void setLocale(String lang) {
+ Locale locale = new Locale(lang);
+ Locale.setDefault(locale);
+
+ Configuration config = new Configuration();
+ config.setLocale(locale);
+
+ getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
+ }
+
+ private void initObj() {
+ context = this;
+ }
+
+ private void initListeners() {
+ // Set item selected listener for bottom navigation
+ bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ Fragment selectedFragment = null;
+ String title = "";
+
+ int id = item.getItemId();
+ if (id == R.id.menu_myReports) {
+ selectedFragment = new MyReportsFragment();
+ title = getString(R.string.My_Report);
+ } else if (id == R.id.menu_map) {
+ selectedFragment = new MapFragment();
+ title = getString(R.string.Map);
+ } else if (id == R.id.menu_favorite) {
+ selectedFragment = new FavoriteFragment();
+ title = getString(R.string.Favorite);
+ }
+
+ if (selectedFragment != null) {
+ loadFragment(selectedFragment, title);
+ }
+ return true;
+ }
+ });
+ }
+
+ private void loadFragment(Fragment fragment, String title) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.flMainContainer, fragment);
+ transaction.commit();
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setTitle(title); // Correct way to set title with support action bar
+ }
+ }
+
+ private void loadDefaultFragment(Bundle savedInstanceState) {
+ if (savedInstanceState == null) {
+ Fragment defaultFragment = new MapFragment();
+ String defaultTitle = getString(R.string.Map);
+
+ loadFragment(defaultFragment, defaultTitle);
+ bottomNavigationView.setSelectedItemId(R.id.menu_map);
+ }
+ }
+
+ // to change the fragment
+ public void openFragmentFromChild(Fragment fragment, String title, int navItemId) {
+ Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.flMainContainer);
+ if (currentFragment != null && currentFragment.getClass().equals(fragment.getClass())) {
+ return;
+ }
+
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
+ transaction.replace(R.id.flMainContainer, fragment);
+ transaction.commit();
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setTitle(title);
+ }
+
+ bottomNavigationView.setSelectedItemId(navItemId);
+ }
+
+ private void getRoleUuids() {
+ DialogUtils.showLoadingDialog(context, "");
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.getRoles(token);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+
+ if (data != null && data.has("role")) {
+ JsonArray rolesArray = data.getAsJsonArray("role");
+
+ for (JsonElement element : rolesArray) {
+ JsonObject roleObj = element.getAsJsonObject();
+ String roleName = roleObj.get("name").getAsString();
+ String roleUuid = roleObj.get("uuid").getAsString();
+
+ if ("admin".equalsIgnoreCase(roleName)) {
+ SharedPref.setAdminRoleUuid(context, roleUuid);
+ } else if ("viewer".equalsIgnoreCase(roleName)) {
+ SharedPref.setViewerRoleUuid(context, roleUuid);
+ }
+ }
+ Log.d(TAG, "Admin UUID: " + SharedPref.getAdminRoleUuid(context));
+ Log.d(TAG, "Viewer UUID: " + SharedPref.getViewerRoleUuid(context));
+ } else {
+ Log.d(TAG, "No roles found.");
+ }
+ } else {
+ Log.d(TAG, "Failed to load roles. Try again.");
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Get Roles Failure: ", t);
+// Helper.makeSnackBar(rlMainActivity, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/activities/AddNewPlaceActivity.java b/app/src/main/java/com/example/acloc/activities/AddNewPlaceActivity.java
new file mode 100644
index 0000000..b907eb1
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/activities/AddNewPlaceActivity.java
@@ -0,0 +1,266 @@
+package com.example.acloc.activities;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatButton;
+import androidx.appcompat.widget.Toolbar;
+
+import com.example.acloc.R;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Place;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.gson.JsonObject;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class AddNewPlaceActivity extends AppCompatActivity implements View.OnClickListener {
+ private static final String TAG = AddNewPlaceActivity.class.getSimpleName();
+ private RelativeLayout rlAddPlace;
+ private ImageView ivPlacePhoto;
+ private TextInputEditText etPlaceName, etLatitude, etLongitude, etAddress, etPlaceDescription;
+ private AppCompatButton btnSubmit;
+ private Dialog dialog;
+ private Context context;
+ private double lat, lng;
+ private String placeName, address;
+
+ private Place entity;
+ private String place_uuid;
+ private static final int PICK_IMAGE_REQUEST = 200;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_add_new_place);
+ initToolbar();
+ initUI();
+ initObj();
+ setListeners();
+ loadIntentData();
+ setDataToText();
+ }
+
+ private void initToolbar() {
+ try {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setTitle(getString(R.string.Add_Place));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AddNewPlaceActivity", e);
+ }
+ }
+
+ private void initUI() {
+ rlAddPlace = findViewById(R.id.rlAddPlace);
+ ivPlacePhoto = findViewById(R.id.ivPlacePhoto);
+ etPlaceName = findViewById(R.id.etPlaceName);
+ etLatitude = findViewById(R.id.etLatitude);
+ etLongitude = findViewById(R.id.etLongitude);
+ etAddress = findViewById(R.id.etAddress);
+ etPlaceDescription = findViewById(R.id.etPlaceDescription);
+ btnSubmit = findViewById(R.id.btnSubmit);
+ }
+
+ private void loadIntentData() {
+ entity = (Place) getIntent().getSerializableExtra(Constants.PLACE);
+ place_uuid = entity.getUuid(); // setting place uuid first
+ if (entity != null) {
+ Log.d(TAG,
+ "PLACE ENTITY DATA \n " +
+ "Place uuid: " + entity.getUuid() + "\n " +
+ "Place name: " + entity.getName() + "\n " +
+ "Place address: " + entity.getAddress() + "\n " +
+ "Place created by: " + entity.getCreatedBy() + "\n " +
+ "Place latitude: " + entity.getLatitude() + "\n " +
+ "Place longitude: " + entity.getLongitude() + "\n " +
+ "Place description: " + entity.getDescription() + "\n "
+ );
+ }
+ }
+
+ private void setListeners() {
+ ivPlacePhoto.setOnClickListener(this);
+ btnSubmit.setOnClickListener(this);
+ }
+
+ private void initObj() {
+ context = this;
+ entity = new Place();
+ }
+
+ private void setDataToText() {
+ etPlaceName.setText(entity.getName());
+ etLatitude.setText(entity.getLatitude());
+ etLongitude.setText(entity.getLongitude());
+ etAddress.setText(entity.getAddress());
+ etPlaceDescription.setText(entity.getDescription());
+
+ }
+
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.ivPlacePhoto) {
+ onClickIvPlacePhoto();
+ } else if (id == R.id.btnSubmit) {
+ onClickBtnSubmit();
+ }
+ }
+
+ private void onClickIvPlacePhoto() {
+ // Open the gallery to select an image
+ Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ intent.setType("image/*"); // Restrict to images only
+ startActivityForResult(intent, PICK_IMAGE_REQUEST);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {
+ // Get the image URI
+ Uri selectedImageUri = data.getData();
+
+ // Set the image in the ImageView
+ ivPlacePhoto.setImageURI(selectedImageUri);
+ }
+ }
+
+ private void onClickBtnSubmit() {
+ View[] views = {etPlaceName, etLatitude, etLongitude, etAddress, etPlaceDescription};
+ if (Helper.isEmptyFieldValidation(views)) {
+ setInputDataToEntity();
+
+ if (place_uuid != null && !place_uuid.isEmpty()) {
+ // Update existing place
+ updatePlaceRetrofit(place_uuid, entity.getName(), entity.getDescription(),
+ entity.getAddress(), entity.getLatitude(), entity.getLongitude(), entity.getCreatedBy());
+ } else {
+ // Insert new place
+ insertPlaceRetrofit(entity.getName(), entity.getDescription(),
+ entity.getAddress(), entity.getLatitude(), entity.getLongitude(), entity.getCreatedBy());
+ }
+ }
+ }
+
+ private void setInputDataToEntity() {
+ entity.setName(Helper.getStringFromInput(etPlaceName));
+ entity.setDescription(Helper.getStringFromInput(etPlaceDescription));
+ entity.setAddress(Helper.getStringFromInput(etAddress));
+ entity.setLatitude(Helper.getStringFromInput(etLatitude));
+ entity.setLongitude(Helper.getStringFromInput(etLongitude));
+ entity.setCreatedBy(SharedPref.getUserUid(context));
+ entity.setUuid(place_uuid);
+ }
+
+ private void insertPlaceRetrofit(String name, String description, String address,
+ String latitude, String longitude, String createdBy) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Please_wait));
+
+ JsonObject placeBody = new JsonObject();
+ placeBody.addProperty("name", name);
+ placeBody.addProperty("description", description);
+ placeBody.addProperty("address", address);
+ placeBody.addProperty("latitude", latitude);
+ placeBody.addProperty("longitude", longitude);
+ placeBody.addProperty("createdBy", createdBy);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.insertPlace(token, placeBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+ if (data != null && data.has("places")) {
+ JsonObject placeObject = data.getAsJsonArray("places").get(0).getAsJsonObject();
+ place_uuid = placeObject.get("uuid").getAsString();
+ Log.d(TAG, "Place UUID: " + place_uuid);
+ Helper.makeSnackBar(rlAddPlace, context.getString(R.string.Place_inserted_successfully));
+ rlAddPlace.postDelayed(() -> {
+ finish();
+ }, 500);
+
+ } else {
+ Helper.makeSnackBar(rlAddPlace, context.getString(R.string.Failed_to_extract_place_Try_again));
+ }
+ } else {
+ Helper.makeSnackBar(rlAddPlace, context.getString(R.string.Insert_failed_Server_error));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Insert Place Error: ", t);
+ Helper.makeSnackBar(rlAddPlace, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void updatePlaceRetrofit(String uuid, String name, String description, String address,
+ String latitude, String longitude, String createdBy) {
+
+ DialogUtils.showLoadingDialog(context, getString(R.string.Updating_place));
+
+ JsonObject placeBody = new JsonObject();
+ placeBody.addProperty("name", name);
+ placeBody.addProperty("description", description);
+ placeBody.addProperty("address", address);
+ placeBody.addProperty("latitude", latitude);
+ placeBody.addProperty("longitude", longitude);
+ placeBody.addProperty("createdBy", createdBy);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.updatePlace(token, uuid, placeBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful()) {
+ Helper.makeSnackBar(rlAddPlace, getString(R.string.Place_updated_successfully));
+ rlAddPlace.postDelayed(() -> {
+ finish();
+ }, 500);
+ } else {
+ Helper.makeSnackBar(rlAddPlace, getString(R.string.Update_failed_Server_error_Try_again));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Update Place Error: ", t);
+ Helper.makeSnackBar(rlAddPlace, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/activities/AddReportActivity.java b/app/src/main/java/com/example/acloc/activities/AddReportActivity.java
new file mode 100644
index 0000000..55b8966
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/activities/AddReportActivity.java
@@ -0,0 +1,348 @@
+package com.example.acloc.activities;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PorterDuff;
+import android.net.Uri;
+
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatButton;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.content.ContextCompat;
+
+import com.example.acloc.R;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Place;
+import com.example.acloc.model.Report;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.gson.JsonObject;
+
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class AddReportActivity extends AppCompatActivity implements View.OnClickListener {
+
+ public static final String TAG = AddReportActivity.class.getSimpleName();
+ private RelativeLayout rlAddReport;
+ private TextInputEditText etDescription, etPlaceName;
+ private ImageView ivReportPhoto;
+ private ImageView ivThumbsUp, ivThumbsAverage, ivThumbsDown;
+ private AppCompatButton btnSubmit;
+ private Context context;
+ private Dialog dialog;
+ private Place placeEntity;
+ private Report reportEntity;
+ private String report_type_uuid, place_uuid;
+ private int reportRating;
+ private String report_uuid;
+
+ private static final int PICK_IMAGE_REQUEST = 100;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_add_report);
+
+ initToolbar();
+ initUI();
+ loadIntentData();
+ initListener();
+ initObj();
+ }
+
+ private void initToolbar() {
+ try {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setTitle(getString(R.string.Add_Report));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AddReportActivity", e);
+ }
+ }
+
+ private void initUI() {
+ rlAddReport = findViewById(R.id.rlAddReport);
+ ivReportPhoto = findViewById(R.id.ivReportPhoto);
+ etPlaceName = findViewById(R.id.etPlaceName);
+ etDescription = findViewById(R.id.etDescription);
+ ivThumbsUp = findViewById(R.id.ivThumbsUp);
+ ivThumbsAverage = findViewById(R.id.ivThumbsAverage);
+ ivThumbsDown = findViewById(R.id.ivThumbsDown);
+ btnSubmit = findViewById(R.id.btnSubmit);
+ }
+
+ private void loadIntentData() {
+ placeEntity = (Place) getIntent().getSerializableExtra(Constants.PLACE);
+ if (placeEntity != null) {
+ place_uuid = placeEntity.getUuid();
+ etPlaceName.setText(placeEntity.getName()); //Just to display place name in report
+// Log.d(TAG,
+// "PLACE ENTITY DATA \n " +
+// "Place uuid: " + placeEntity.getUuid() + "\n " +
+// "Place name: " + placeEntity.getName() + "\n " +
+// "Place address: " + placeEntity.getAddress() + "\n " +
+// "Place created by: " + placeEntity.getCreatedBy() + "\n " +
+// "Place latitude: " + placeEntity.getLatitude() + "\n " +
+// "Place longitude: " + placeEntity.getLongitude() + "\n " +
+// "Place description: " + placeEntity.getDescription() + "\n "
+// );
+ }
+
+ reportEntity = (Report) getIntent().getSerializableExtra(Constants.REPORT);
+ if (reportEntity != null) {
+ report_uuid = reportEntity.getUuid(); // setting place uuid first
+ place_uuid = reportEntity.getPlaceUuid();
+ Log.d(TAG, "" +
+ "place uuid: " + reportEntity.getPlaceUuid() +
+ "\n fkplace " + reportEntity.getFkPlace());
+ setDataToEditText();
+ }
+ }
+
+ private void setDataToEditText() {
+ etPlaceName.setText(reportEntity.getPlaceName());
+ etDescription.setText(reportEntity.getDescription());
+ int rating = reportEntity.getReportRating();
+ if (rating == 1) {
+ ivThumbsDown.setColorFilter(ContextCompat.getColor(this, R.color.red), PorterDuff.Mode.SRC_IN);
+ ivThumbsUp.setColorFilter(null); // reset the other
+ ivThumbsAverage.setColorFilter(null);
+ reportRating = Constants.BAD_RATING; //1
+ } else if (rating == 2) {
+ ivThumbsAverage.setColorFilter(ContextCompat.getColor(this, R.color.yellow), PorterDuff.Mode.SRC_IN);
+ ivThumbsUp.setColorFilter(null); // reset the others
+ ivThumbsDown.setColorFilter(null);
+ reportRating = Constants.AVERAGE_RATING; //2
+ } else if (rating == 3) {
+ ivThumbsUp.setColorFilter(ContextCompat.getColor(this, R.color.green), PorterDuff.Mode.SRC_IN);
+ ivThumbsDown.setColorFilter(null); // reset the other
+ ivThumbsAverage.setColorFilter(null);
+ reportRating = Constants.GOOD_RATING; //3
+ }
+ }
+
+ private void initListener() {
+ ivThumbsUp.setOnClickListener(this);
+ ivThumbsAverage.setOnClickListener(this);
+ ivThumbsDown.setOnClickListener(this);
+ ivReportPhoto.setOnClickListener(this);
+ btnSubmit.setOnClickListener(this);
+ }
+
+ private void initObj() {
+ context = this;
+ reportEntity = new Report();
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.ivThumbsUp) {
+ onClickThumbsUp();
+ } else if (id == R.id.ivThumbsAverage) {
+ onClickThumbsAverage();
+ } else if (id == R.id.ivThumbsDown) {
+ onClickThumbsDown();
+ } else if (id == R.id.ivReportPhoto) {
+ onClickIvReportPhoto();
+ } else if (id == R.id.btnSubmit) {
+ onClickBtnSubmit();
+ }
+ }
+
+ private void onClickIvReportPhoto() {
+ // Open the gallery to select an image
+ Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ intent.setType("image/*"); // Restrict to images only
+ startActivityForResult(intent, PICK_IMAGE_REQUEST);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {
+ // Get the image URI
+ Uri selectedImageUri = data.getData();
+
+ // Set the image in the ImageView
+ ivReportPhoto.setImageURI(selectedImageUri);
+ }
+ }
+
+ private void onClickThumbsUp() {
+ ivThumbsUp.setColorFilter(ContextCompat.getColor(this, R.color.green), PorterDuff.Mode.SRC_IN);
+ ivThumbsDown.setColorFilter(null); // reset the other
+ ivThumbsAverage.setColorFilter(null);
+ reportRating = Constants.GOOD_RATING; //3
+ }
+
+ private void onClickThumbsAverage() {
+ ivThumbsAverage.setColorFilter(ContextCompat.getColor(this, R.color.yellow), PorterDuff.Mode.SRC_IN);
+ ivThumbsUp.setColorFilter(null); // reset the others
+ ivThumbsDown.setColorFilter(null);
+ reportRating = Constants.AVERAGE_RATING; //2
+ }
+
+ private void onClickThumbsDown() {
+ ivThumbsDown.setColorFilter(ContextCompat.getColor(this, R.color.red), PorterDuff.Mode.SRC_IN);
+ ivThumbsUp.setColorFilter(null); // reset the other
+ ivThumbsAverage.setColorFilter(null);
+ reportRating = Constants.BAD_RATING; //1
+
+ }
+
+ private void onClickBtnSubmit() {
+ View[] views = {etPlaceName, etDescription};
+ if (Helper.isEmptyFieldValidation(views) && isValidateRating()) {
+ setInputDataToEntity();
+
+ if (report_uuid != null && !report_uuid.isEmpty()) {
+ // Update existing report
+ updateReportRetrofit(
+ report_uuid,
+ place_uuid,
+ SharedPref.getUserUid(context),
+ String.valueOf(reportEntity.getReportRating()),
+ reportEntity.getDescription(),
+ reportEntity.getCreatedBy()
+ );
+ } else {
+ insertReportRetrofit(
+ placeEntity.getUuid(),
+ SharedPref.getUserUid(context),
+ String.valueOf(reportEntity.getReportRating()),
+ reportEntity.getDescription(),
+ reportEntity.getCreatedBy()
+ );
+ }
+ }
+ }
+
+ private void setInputDataToEntity() {
+ reportEntity.setDescription(Helper.getStringFromInput(etDescription));
+ reportEntity.setReportRating(reportRating);
+ reportEntity.setCreatedBy(SharedPref.getUserUid(context));
+ }
+
+ private boolean isValidateRating() {
+ if (reportRating != 0) {
+ return true;
+ } else {
+ Helper.makeSnackBar(rlAddReport, getString(R.string.Please_select_rating));
+ return false;
+ }
+ }
+
+ private void insertReportRetrofit(String placeUuid, String userUuid, String rating,
+ String description, String createdBy) {
+
+ DialogUtils.showLoadingDialog(context, getString(R.string.Please_wait));
+
+ JsonObject reportBody = new JsonObject();
+ reportBody.addProperty("place_uuid", placeUuid);
+ reportBody.addProperty("user_uuid", userUuid);
+ reportBody.addProperty("rating", rating);
+ reportBody.addProperty("description", description);
+ reportBody.addProperty("createdBy", createdBy);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.insertReport(token, reportBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+ if (data != null && data.has("reports")) {
+ JsonObject reportObject = data.getAsJsonArray("reports").get(0).getAsJsonObject();
+ String report_uuid = reportObject.get("uuid").getAsString();
+ Log.d(TAG, "Report UUID: " + report_uuid);
+ Helper.makeSnackBar(rlAddReport, getString(R.string.Report_submitted_successfully));
+ rlAddReport.postDelayed(() -> {
+ finish();
+ }, 500);
+ // Delay .5 sec
+// rlAddReport.postDelayed(() -> {
+// Helper.goToAndFinish(AddReportActivity.this, MainActivity.class);
+// }, 500);
+ } else {
+ Log.d(TAG, "Failed to extract report UUID.");
+ Helper.makeSnackBar(rlAddReport, getString(R.string.Something_went_wrong_Try_again));
+ }
+ } else {
+ Helper.makeSnackBar(rlAddReport, getString(R.string.Report_submission_failed_Try_again));
+ Log.e(TAG, "Insert Report Error: " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Insert Report Failure: ", t);
+ Helper.makeSnackBar(rlAddReport, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void updateReportRetrofit(String uuid, String placeUuid, String userUuid, String rating,
+ String description, String createdBy) {
+
+ DialogUtils.showLoadingDialog(context, getString(R.string.Updating_report));
+
+ JsonObject reportBody = new JsonObject();
+ reportBody.addProperty("place_uuid", placeUuid);
+ reportBody.addProperty("user_uuid", userUuid);
+ reportBody.addProperty("rating", rating);
+ reportBody.addProperty("description", description);
+ reportBody.addProperty("createdBy", createdBy);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.updateReport(token, uuid, reportBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful()) {
+ Helper.makeSnackBar(rlAddReport, getString(R.string.Report_updated_successfully));
+ rlAddReport.postDelayed(() -> {
+ finish(); //to go back to the previous activity
+ }, 500);
+ } else {
+ Helper.makeSnackBar(rlAddReport, getString(R.string.Update_failed_Server_error_Try_again));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Update Place Error: ", t);
+ Helper.makeSnackBar(rlAddReport, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/activities/LoginActivity.java b/app/src/main/java/com/example/acloc/activities/LoginActivity.java
new file mode 100644
index 0000000..e814eaa
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/activities/LoginActivity.java
@@ -0,0 +1,201 @@
+package com.example.acloc.activities;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatButton;
+import androidx.appcompat.widget.Toolbar;
+
+import com.example.acloc.MainActivity;
+import com.example.acloc.R;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.User;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
+ private static final String TAG = LoginActivity.class.getSimpleName();
+ private RelativeLayout rlLogin;
+ private TextInputEditText etUsername, etPassword;
+ private AppCompatButton btnLogin;
+ private Context context;
+ private TextView tvRegisterRedirect;
+ private User entity;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+
+ checkForLoginStatusAndNavigate();
+ }
+
+ private void checkForLoginStatusAndNavigate() {
+ boolean loginStatus = SharedPref.getIsLoggedIn(this);
+ boolean isTokenValid = SharedPref.isAccessTokenValid(this);
+
+ if (loginStatus && isTokenValid) {
+ Helper.goTo(this, MainActivity.class);
+ finish();
+ } else {
+ // Optionally force logout if token expired
+ SharedPref.setIsLoggedIn(this, false);
+ SharedPref.setAccessToken(this, ""); // clear token
+ setContentView(R.layout.activity_login);
+ initToolbar();
+ initUI();
+ initObj();
+ initListeners();
+ }
+ }
+
+
+ private void initToolbar() {
+ try {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setTitle(getString(R.string.LOGIN));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in LoginActivity", e);
+ }
+ }
+
+ private void initUI() {
+ rlLogin = findViewById(R.id.rlLogin);
+ etUsername = findViewById(R.id.etUsername);
+ etPassword = findViewById(R.id.etPassword);
+ btnLogin = findViewById(R.id.btnLogin);
+ tvRegisterRedirect = findViewById(R.id.tvRegisterRedirect);
+ }
+
+ private void initListeners() {
+ btnLogin.setOnClickListener(this);
+ tvRegisterRedirect.setOnClickListener(this);
+ }
+
+ private void initObj() {
+ context = this;
+ entity = new User();
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.tvRegisterRedirect) {
+ onClickRegisterRedirect();
+ } else if (id == R.id.btnLogin) {
+ onClickBtnLogin();
+ }
+ }
+
+ private void onClickRegisterRedirect() {
+ Helper.goToAndFinish(LoginActivity.this, RegisterActivity.class);
+ }
+
+ private void onClickBtnLogin() {
+ View[] views = {etUsername, etPassword};
+ if (Helper.isEmptyFieldValidation(views)) {
+ setInputDataToEntity();
+ loginUserWithRetrofit();
+ }
+ }
+
+ private void setInputDataToEntity() {
+ entity.setUsername(Helper.getStringFromInput(etUsername));
+ entity.setPassword(Helper.getStringFromInput(etPassword));
+ }
+
+ private void loginUserWithRetrofit() {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Please_wait));
+
+ JsonObject jsonParam = new JsonObject();
+ jsonParam.addProperty("username", entity.getUsername());
+ jsonParam.addProperty("password", entity.getPassword());
+
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+ Call call = apiService.loginUser(jsonParam);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject json = response.body();
+
+ if (json.has("user")) {
+ JsonObject userObj = json.getAsJsonObject("user");
+
+ // 1. Extract user data from _data
+ if (userObj.has("_data")) {
+ JsonObject data = userObj.getAsJsonObject("_data");
+
+ String uuid = data.has("uuid") ? data.get("uuid").getAsString() : "";
+ String username = data.has("username") ? data.get("username").getAsString() : "";
+ String email = data.has("email") ? data.get("email").getAsString() : "";
+ String role = data.has("role") ? data.get("role").getAsString() : "";
+
+ SharedPref.setUuid(context, uuid);
+ SharedPref.setUsername(context, username);
+ SharedPref.setUserEmail(context, email);
+ SharedPref.setRole(context, role);
+ }
+
+ // 2. Extract tokens
+ String accessToken = userObj.has("accessToken") ? userObj.get("accessToken").getAsString() : "";
+ String refreshToken = userObj.has("refreshToken") ? userObj.get("refreshToken").getAsString() : "";
+
+ if (!accessToken.isEmpty()) SharedPref.setAccessToken(context, accessToken);
+ if (!refreshToken.isEmpty())
+ SharedPref.setRefreshToken(context, refreshToken);
+
+ Helper.makeSnackBar(rlLogin, getString(R.string.Login_Successful));
+ SharedPref.setIsLoggedIn(context, true);
+ Helper.goToAndFinish(LoginActivity.this, MainActivity.class);
+ } else {
+ Helper.makeSnackBar(rlLogin, getString(R.string.Invalid_Credentials_Please_try_again));
+ }
+ } else {
+ try {
+ if (response.errorBody() != null) {
+ String errorBody = response.errorBody().string();
+ Log.e(TAG, "Login Error: " + errorBody);
+ Helper.makeSnackBar(rlLogin, getString(R.string.Login_failed) + errorBody + " Try again");
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Login error reading response", e);
+// Helper.makeSnackBar(rlLogin, getString(R.string.Something_went_wrong));
+ Helper.makeSnackBar(rlLogin, e.toString() + " Try again");
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Login API call failed", t);
+// Helper.makeSnackBar(rlLogin, getString(R.string.Something_went_wrong));
+ Helper.makeSnackBar(rlLogin, "API Failure: " + t.toString() + " Try again");
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/activities/ManageRolesActivity.java b/app/src/main/java/com/example/acloc/activities/ManageRolesActivity.java
new file mode 100644
index 0000000..7607ea7
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/activities/ManageRolesActivity.java
@@ -0,0 +1,247 @@
+package com.example.acloc.activities;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.RelativeLayout;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.acloc.R;
+import com.example.acloc.adapter.UserAdapter;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.User;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.android.material.textfield.TextInputLayout;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class ManageRolesActivity extends AppCompatActivity {
+
+ public static final String TAG = ManageRolesActivity.class.getSimpleName();
+ private RelativeLayout rlManageRoles;
+ private TextInputLayout tilSearch;
+ private TextInputEditText etSearchUser;
+ private RecyclerView rvUsers;
+ private List userList;
+ private UserAdapter adapter;
+ private Context context;
+ private User userEntity;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_manage_roles);
+
+ initToolbar();
+ initUI();
+ initListener();
+ initObj();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (adapter != null) {
+ setDataVisibility(false);
+ userList.clear();
+ }
+ if (context != null) loadData();
+ }
+
+ private void setDataVisibility(boolean isDataAvailable) {
+ if (isDataAvailable) {
+ rvUsers.setVisibility(View.VISIBLE);
+ } else {
+ // rvReports.setVisibility(View.GONE);
+
+ }
+ }
+
+ private void initToolbar() {
+ try {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setTitle(getString(R.string.Manage_Roles));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in ManageRoles", e);
+ }
+ }
+
+ private void initUI() {
+ rlManageRoles = findViewById(R.id.rlManageRoles);
+ tilSearch = findViewById(R.id.tilSearch);
+ etSearchUser = findViewById(R.id.etSearchUser);
+ rvUsers = findViewById(R.id.rvUsers);
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private void initListener() {
+
+ //Search drawable on click
+ etSearchUser.setOnTouchListener((v, event) -> {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ int drawableEnd = etSearchUser.getCompoundDrawables()[2] != null
+ ? etSearchUser.getWidth() - etSearchUser.getPaddingEnd()
+ : 0;
+
+ if (event.getRawX() >= (etSearchUser.getRight() - etSearchUser.getCompoundDrawables()[2].getBounds().width())) {
+ // Search icon clicked
+ filterUserList(Helper.getStringFromInput(etSearchUser));
+ return true;
+ }
+ }
+ return false;
+ });
+
+ //Enter
+ etSearchUser.setOnEditorActionListener((v, actionId, event) -> {
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ filterUserList(Helper.getStringFromInput(etSearchUser));
+ return true;
+ }
+ return false;
+ });
+ }
+
+ private void initObj() {
+ context = this;
+ userEntity = new User();
+ }
+
+ private void loadData() {
+ try {
+ if (userList == null) {
+ userList = new ArrayList<>();
+ }
+ getAllUsers();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in ManageRoles", e);
+ Helper.makeSnackBar(rlManageRoles, getString(R.string.Something_went_wrong_Try_again));
+ }
+ }
+
+ private void setUpRecyclerView(List userList) {
+ try {
+ if (adapter != null) {
+ adapter.updateUserList(userList);
+ } else {
+ adapter = new UserAdapter(context, userList);
+ rvUsers.setAdapter(adapter);
+ rvUsers.setLayoutManager(Helper.getVerticalManager(context));
+ adapter.notifyDataSetChanged();
+ }
+ if (userList != null && !userList.isEmpty()) {
+ setDataVisibility(true); // Data available
+ } else {
+ setDataVisibility(false); // No data
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in ManageRoles", e);
+ Helper.showToast(context, getString(R.string.Something_went_wrong_Try_again));
+ setDataVisibility(false);
+ }
+ }
+
+ private void filterUserList(String query) {
+ if (TextUtils.isEmpty(query)) {
+ setUpRecyclerView(userList); // Show full list if query is empty
+ return;
+ }
+
+ List filteredList = new ArrayList<>();
+ for (User user : userList) {
+ if (user.getUsername() != null && user.getUsername().toLowerCase().contains(query.toLowerCase())) {
+ filteredList.add(user);
+ }
+ }
+
+ setUpRecyclerView(filteredList);
+ }
+
+ public void getAllUsers() {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Loading_users));
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.getAllUsers(token);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+
+ if (data != null && data.has("users")) {
+ userList.clear(); // clear previous list
+
+ for (JsonElement element : data.getAsJsonArray("users")) {
+ JsonObject userObj = element.getAsJsonObject();
+ String username = userObj.get("username").getAsString();
+ // Skip the current logged-in user
+ if (username.equals(SharedPref.getUserName(context))) {
+ continue;
+ }
+
+ User user = new User();
+ user.setUsername(username);
+ user.setEmail(userObj.has("email") && !userObj.get("email").isJsonNull()
+ ? userObj.get("email").getAsString() : null);
+ user.setUuid(userObj.get("uuid").getAsString());
+ user.setFkRole(userObj.get("fk_role").getAsString());
+ user.setRole(userObj.get("role").getAsString());
+
+ userList.add(user);
+ }
+
+ if (!userList.isEmpty()) {
+ setUpRecyclerView(userList);
+ } else {
+ setDataVisibility(false);
+ }
+
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlManageRoles, getString(R.string.No_users_found));
+ }
+
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlManageRoles, getString(R.string.Failed_to_load_users_Try_again));
+ Log.e(TAG, "Get users error: " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Get users failure: ", t);
+ Helper.makeSnackBar(rlManageRoles, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/activities/PlaceDetailActivity.java b/app/src/main/java/com/example/acloc/activities/PlaceDetailActivity.java
new file mode 100644
index 0000000..494ad16
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/activities/PlaceDetailActivity.java
@@ -0,0 +1,430 @@
+package com.example.acloc.activities;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatButton;
+import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.acloc.R;
+import com.example.acloc.adapter.PlaceReportsAdapter;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Place;
+import com.example.acloc.model.Report;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textview.MaterialTextView;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class PlaceDetailActivity extends AppCompatActivity implements View.OnClickListener {
+ public static final String TAG = PlaceDetailActivity.class.getSimpleName();
+ private RelativeLayout rlPlaceDetails;
+ private MaterialTextView etPlaceName, etAddress, etPlaceDescription;
+ private ImageView ivFavorite, ivEdit;
+ private AppCompatButton btnSubmit;
+ private Dialog dialog;
+ private Context context;
+ private Place placeEntity;
+ private String place_uuid;
+ private boolean isFavorite = false; // Track favorite current state default false
+ private RecyclerView rvReports;
+ private TextView tvNoData;
+ private PlaceReportsAdapter adapter;
+ private Report reportEntity;
+ private List reportList;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_place_detail);
+
+ initToolbar();
+ initUI();
+ loadIntentData(); // load data
+ initListener(); // set fav, edit btn On Clicks
+ initObj();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (adapter != null) {
+ setDataVisibility(false);
+ adapter.clearReports(); // Safe way to clear and notify
+ }
+
+ if (context != null) loadData();
+ }
+
+
+ private void setDataVisibility(boolean isDataAvailable) {
+ if (isDataAvailable) {
+ rvReports.setVisibility(View.VISIBLE);
+ tvNoData.setVisibility(View.GONE);
+ } else {
+ tvNoData.setVisibility(View.VISIBLE);
+ // rvReports.setVisibility(View.GONE);
+
+ }
+ }
+
+ private void initToolbar() {
+ try {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setTitle(getString(R.string.Place_Details));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in PlaceDetailActivity", e);
+ }
+ }
+
+ private void initUI() {
+ rlPlaceDetails = findViewById(R.id.rlPlaceDetails);
+ etPlaceName = findViewById(R.id.etPlaceName);
+ etPlaceDescription = findViewById(R.id.etPlaceDescription);
+ etAddress = findViewById(R.id.etAddress);
+ ivFavorite = findViewById(R.id.ivFavorite);
+ ivEdit = findViewById(R.id.ivEdit);
+ btnSubmit = findViewById(R.id.btnSubmit);
+ rvReports = findViewById(R.id.rvReports);
+ tvNoData = findViewById(R.id.tvNoData);
+ }
+
+ private void initListener() {
+ ivFavorite.setOnClickListener(this);
+ ivEdit.setOnClickListener(this);
+ btnSubmit.setOnClickListener(this);
+ }
+
+ private void initObj() {
+ context = this;
+ checkIfPlaceIsFavorite(SharedPref.getUserUid(context), place_uuid);
+ }
+
+ private void loadIntentData() {
+ placeEntity = (Place) getIntent().getSerializableExtra(Constants.PLACE);
+ if (placeEntity != null) {
+ place_uuid = placeEntity.getUuid();
+ etPlaceName.setText(placeEntity.getName());
+ etAddress.setText(placeEntity.getAddress());
+ etPlaceDescription.setText(placeEntity.getDescription());
+ Log.d(TAG,
+ "Place uuid: " + placeEntity.getUuid() + "\n " +
+ "Place name: " + placeEntity.getName() + "\n " +
+ "Place name: " + placeEntity.getAddress() + "\n " +
+ "Place created by: " + placeEntity.getCreatedBy() + "\n " +
+ "Place latitude: " + placeEntity.getLatitude() + "\n " +
+ "Place longitude: " + placeEntity.getLongitude() + "\n " +
+ "Place description: " + placeEntity.getDescription() + "\n "
+ );
+ }
+ }
+
+ private void loadData() {
+ try {
+ if (reportList == null) {
+ reportList = new ArrayList<>();
+ }
+ getReportsByPlaceUuid(placeEntity.getUuid());
+ } catch (Exception e) {
+ Log.e(TAG, "Error in PlaceDetailActivity", e);
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Something_went_wrong_Try_again));
+ }
+ }
+
+ private void setUpRecyclerView(List latestReports) {
+ try {
+ if (adapter != null) {
+ adapter.updateReportsList(latestReports);
+ } else {
+ adapter = new PlaceReportsAdapter(context, latestReports);
+ rvReports.setAdapter(adapter);
+ rvReports.setLayoutManager(Helper.getVerticalManager(context));
+ adapter.notifyDataSetChanged();
+ }
+ if (latestReports != null && !latestReports.isEmpty()) {
+ setDataVisibility(true); // Data available
+ } else {
+ setDataVisibility(false); // No data
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in PlaceDetailActivity", e);
+ Helper.showToast(context, getString(R.string.Something_went_wrong_Try_again));
+ setDataVisibility(false);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.ivFavorite) {
+ onClickFavorite();
+ } else if (id == R.id.ivEdit) {
+ onClickBtnEdit();
+ } else if (id == R.id.btnSubmit) {
+ onClickBtnSubmit();
+ }
+ }
+
+ private void onClickBtnEdit() {
+ Helper.goTo(context, AddNewPlaceActivity.class, Constants.PLACE, placeEntity);
+ }
+
+ private void onClickBtnSubmit() {
+ Helper.goTo(context, AddReportActivity.class, Constants.PLACE, placeEntity);
+ }
+
+ private void onClickFavorite() {
+ if (isFavorite) {
+ removePlaceFromFavorites(SharedPref.getUserUid(context), place_uuid);
+ } else {
+ addPlaceToFavorites(SharedPref.getUserUid(context), place_uuid);
+ }
+ }
+
+ private void addPlaceToFavorites(String userUuid, String placeUuid) {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Adding_to_favorites));
+
+ JsonObject body = new JsonObject();
+ body.addProperty("place_uuid", placeUuid);
+ Log.d(TAG, "Request Body: " + body.toString());
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.addPlaceToFavorites(token, userUuid, body);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful()) {
+ Log.d(TAG, "Place added to fav!");
+ isFavorite = true; // Update state
+ updateFavoriteIcon(); // Update icon
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Place_added_to_favorites));
+ } else {
+ Log.d(TAG, "Failed to add favorite!");
+ try {
+ if (response.errorBody() != null) {
+ String errorBody = response.errorBody().string();
+ Log.e(TAG, "Error Body: " + errorBody);
+
+ // Try restoring if it's a duplicate (409 Conflict)
+ if (response.code() == 409 && errorBody.contains("ER_DUP_ENTRY")) {
+ Log.d(TAG, "Duplicate entry detected, trying to restore...");
+ restorePlaceToFavorites(userUuid, placeUuid);
+ return;
+ }
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "ERROR: ", e);
+ }
+
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Failed_to_add_favorite_Server_error));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Add Favorite Error: ", t);
+ Helper.makeSnackBar(rlPlaceDetails, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void restorePlaceToFavorites(String userUuid, String placeUuid) {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Restoring_favorite));
+ JsonObject body = new JsonObject();
+ body.addProperty("place_uuid", placeUuid);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.restorePlaceToFavorites(token, userUuid, body);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful()) {
+ Log.d(TAG, "Place restored to favorites!");
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Place_restored_to_favorites));
+ } else {
+ Log.e(TAG, "Restore failed. Response code: " + response.code());
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Failed_to_restore_favorite));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Restore Favorite Error: ", t);
+ Helper.makeSnackBar(rlPlaceDetails, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void removePlaceFromFavorites(String userUuid, String placeUuid) {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Removing_from_favorites));
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.removePlaceFromFavorites(token, userUuid, placeUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful()) {
+ Log.d(TAG, "Place removed from fav!");
+ isFavorite = false; // Update state
+ updateFavoriteIcon(); // Update icon
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Removed_from_favorites));
+ } else {
+ Log.d(TAG, "Failed to remove from fav");
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Failed_to_remove_from_favorites));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Remove Favorite Error: ", t);
+ Helper.makeSnackBar(rlPlaceDetails, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void checkIfPlaceIsFavorite(String userUuid, String currentPlaceUuid) {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Checking_favorite_status));
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.getFavoritePlaces(token, userUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject body = response.body();
+ JsonArray favoritesArray = body.getAsJsonObject("_data").getAsJsonArray("favorites");
+
+ isFavorite = false;
+ for (JsonElement item : favoritesArray) {
+ JsonObject favoriteObj = item.getAsJsonObject();
+ String favPlaceUuid = favoriteObj.get("place_uuid").getAsString();
+ if (favPlaceUuid.equals(currentPlaceUuid)) {
+ isFavorite = true;
+ break;
+ }
+ }
+ updateFavoriteIcon();
+
+ } else {
+ Log.e(TAG, "Failed to fetch favorites. Code: " + response.code());
+ isFavorite = false;
+ updateFavoriteIcon();
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Error fetching favorite places", t);
+ isFavorite = false;
+ updateFavoriteIcon();
+ }
+ });
+ }
+
+ private void updateFavoriteIcon() {
+ if (isFavorite) {
+ ivFavorite.setImageResource(R.drawable.ic_favorite);
+ } else {
+ ivFavorite.setImageResource(R.drawable.ic_favorite_border);
+ }
+ }
+
+ private void getReportsByPlaceUuid(String placeUuid) {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Loading_reports));
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ // Make the API call to fetch reports by place UUID
+ Call call = apiService.getPlaceReports(token, placeUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+
+ if (data != null && data.has("reports")) {
+ reportList.clear(); // Clear previous list
+
+ for (JsonElement element : data.getAsJsonArray("reports")) {
+ JsonObject reportObject = element.getAsJsonObject();
+ Report report = new Report();
+
+ report.setUuid(reportObject.get("uuid").getAsString());
+ report.setReportRating(reportObject.get("rating").getAsInt());
+ report.setDescription(reportObject.get("description").getAsString());
+ report.setPlaceName(reportObject.get("place_name").getAsString());
+ report.setPlaceUuid(reportObject.get("place_uuid").getAsString());
+
+ reportList.add(report);
+ }
+ // to get latest report first
+ Collections.reverse(reportList);
+ List latestReports = reportList.size() > 5 ? reportList.subList(0, 5) : reportList;
+
+ // Update the RecyclerView with latest reports
+ setUpRecyclerView(latestReports);
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.No_reports_found));
+ }
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Failed_to_load_reports_Try_again_));
+ Log.e(TAG, "Get Reports Error: " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Get Reports Failure: ", t);
+ Helper.makeSnackBar(rlPlaceDetails, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/activities/RegisterActivity.java b/app/src/main/java/com/example/acloc/activities/RegisterActivity.java
new file mode 100644
index 0000000..0e3ec8e
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/activities/RegisterActivity.java
@@ -0,0 +1,169 @@
+package com.example.acloc.activities;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatButton;
+import androidx.appcompat.widget.Toolbar;
+
+import com.example.acloc.R;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.User;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class RegisterActivity extends AppCompatActivity implements View.OnClickListener {
+ private static final String TAG = RegisterActivity.class.getSimpleName();
+ private RelativeLayout rlRegister;
+ private TextInputEditText etName, etEmail, etPassword;
+ private AppCompatButton btnRegister;
+ private TextView tvLoginRedirect;
+ private Context context;
+ private User entity;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_register);
+
+ initToolbar();
+ initUI();
+ initObj();
+ initListeners();
+ }
+
+ private void initToolbar() {
+ try {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setTitle(getString(R.string.REGISTER));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in RegisterActivity", e);
+ }
+ }
+
+ private void initUI() {
+ rlRegister = findViewById(R.id.rlRegister);
+ etName = findViewById(R.id.etName);
+ etEmail = findViewById(R.id.etEmail);
+ etPassword = findViewById(R.id.etPassword);
+ btnRegister = findViewById(R.id.btnRegister);
+ tvLoginRedirect = findViewById(R.id.tvLoginRedirect);
+ }
+
+ private void initListeners() {
+ btnRegister.setOnClickListener(this);
+ tvLoginRedirect.setOnClickListener(this);
+ }
+
+ private void initObj() {
+ context = this;
+ entity = new User();
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.tvLoginRedirect) {
+ onClickLoginRedirect();
+ } else if (id == R.id.btnRegister) {
+ onClickBtnRegister();
+ }
+ }
+
+ private void onClickLoginRedirect() {
+ Helper.goToAndFinish(RegisterActivity.this, LoginActivity.class);
+ }
+
+ private void onClickBtnRegister() {
+ View[] views = {etName, etEmail, etPassword};
+ if (Helper.isEmptyFieldValidation(views) && Helper.isEmailValid(etEmail) && Helper.isPasswordValid(etPassword)) {
+ setInputDataToEntity();
+ registerUserWithRetrofit();
+ }
+ }
+
+ private void setInputDataToEntity() {
+ entity.setUsername(Helper.getStringFromInput(etName));
+ entity.setEmail(Helper.getStringFromInput(etEmail));
+ entity.setPassword(Helper.getStringFromInput(etPassword));
+ }
+
+ private void registerUserWithRetrofit() {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Please_wait));
+
+ JsonObject jsonParam = new JsonObject();
+ jsonParam.addProperty("username", entity.getUsername());
+ jsonParam.addProperty("email", entity.getEmail());
+ jsonParam.addProperty("password", entity.getPassword());
+ jsonParam.addProperty("fk_role", Constants.ADMIN); //Default Admin for now (BUT acc to api it will be Viewer)
+
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+ Call call = apiService.registerUser(jsonParam);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject json = response.body();
+
+ if (json.has("code") && json.get("code").getAsInt() == 409) {
+ Helper.makeSnackBar(rlRegister, getString(R.string.User_already_exists_Please_login));
+ return;
+ }
+
+ if (json.has("_data") && json.getAsJsonObject("_data").has("message")) {
+ Helper.makeSnackBar(rlRegister, getString(R.string.Registration_Successful));
+ Helper.goToAndFinish(RegisterActivity.this, LoginActivity.class);
+ } else {
+ Helper.makeSnackBar(rlRegister, getString(R.string.Invalid_Credentials_Please_try_again));
+ }
+ } else {
+ try {
+ if (response.errorBody() != null) {
+ String errorBody = response.errorBody().string();
+ Log.e(TAG, "Error Response: " + errorBody);
+ Helper.makeSnackBar(rlRegister, "Server Error " + errorBody + " Try again");
+ } else {
+// Helper.makeSnackBar(rlRegister, getString(R.string.Something_went_wrong));
+ Helper.makeSnackBar(rlRegister, "Server Error " + " Try again");
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading errorBody", e);
+// Helper.makeSnackBar(rlRegister, getString(R.string.Something_went_wrong));
+ Helper.makeSnackBar(rlRegister, "API Failure: " + e.toString() + " Try again");
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "API Failure", t);
+// Helper.makeSnackBar(rlRegister, getString(R.string.Something_went_wrong));
+ Helper.makeSnackBar(rlRegister, "API Failure: " + t.toString() + " Try again");
+ }
+
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/adapter/FavoriteAdapter.java b/app/src/main/java/com/example/acloc/adapter/FavoriteAdapter.java
new file mode 100644
index 0000000..4e2e086
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/adapter/FavoriteAdapter.java
@@ -0,0 +1,199 @@
+package com.example.acloc.adapter;
+
+import android.annotation.SuppressLint;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.acloc.R;
+import com.example.acloc.activities.PlaceDetailActivity;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Favorite;
+import com.example.acloc.model.Place;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class FavoriteAdapter extends RecyclerView.Adapter {
+
+ public static final String TAG = FavoriteAdapter.class.getSimpleName();
+ private final Context context;
+ private List favoriteList;
+
+ public FavoriteAdapter(Context context, List favoriteList) {
+ this.context = context;
+ this.favoriteList = favoriteList;
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ public void updateFavoriteList(List favoriteList) {
+ try {
+ if (favoriteList != null) {
+ this.favoriteList = favoriteList;
+ notifyDataSetChanged();
+ }
+ } catch (Exception exception) {
+ Log.e(TAG, "Error in FavoriteAdapter", exception);
+ }
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View detailItem = inflater.inflate(R.layout.list_view_favorite, parent, false);
+ return new ViewHolder(detailItem);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull FavoriteAdapter.ViewHolder holder, int position) {
+ try {
+ if (!favoriteList.isEmpty()) {
+ Favorite favorite = favoriteList.get(position);
+ holder.tvPlaceName.setText(favorite.getPlaceName());
+ holder.tvDescription.setText(favorite.getPlaceDescription());
+
+ // handle favorite on click
+ holder.ivFavorite.setOnClickListener(v -> {
+ removePlaceFromFavorites(SharedPref.getUserUid(context), favorite.getPlaceUuid(), favorite.getUuid());
+ });
+
+ holder.itemView.setOnClickListener(v -> getPlaceByUuid(favorite.getPlaceUuid()));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in Favorite Adapter", e);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return favoriteList.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView tvPlaceName, tvDescription;
+ private final ImageView ivFavorite;
+
+ public ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ tvPlaceName = itemView.findViewById(R.id.tvPlaceName);
+ tvDescription = itemView.findViewById(R.id.tvDescription);
+ ivFavorite = itemView.findViewById(R.id.ivFavorite);
+ }
+ }
+
+ private void removePlaceFromFavorites(String userUuid, String placeUuid, String favoriteUuid) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Removing_from_favorites));
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.removePlaceFromFavorites(token, userUuid, placeUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ View rootView = ((Activity) context).findViewById(android.R.id.content);
+
+ if (response.isSuccessful()) {
+ // Find position of the favorite to be removed
+ int positionToRemove = -1;
+ for (int i = 0; i < favoriteList.size(); i++) {
+ if (favoriteList.get(i).getUuid().equals(favoriteUuid)) {
+ positionToRemove = i;
+ break;
+ }
+ }
+
+ // Remove the favorite from the list
+ if (positionToRemove != -1) {
+ favoriteList.remove(positionToRemove);
+ notifyItemRemoved(positionToRemove); // Notify the adapter that the item was removed
+ }
+
+ Log.d(TAG, "Favorite removed!");
+ Helper.makeSnackBar(rootView, context.getString(R.string.Favorite_removed));
+ } else {
+ Log.d(TAG, "Failed to remove Favorite");
+ Helper.makeSnackBar(rootView, context.getString(R.string.Failed_to_remove_Favorite));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ View rootView = ((Activity) context).findViewById(android.R.id.content);
+ Log.e(TAG, "Remove Favorite Error: ", t);
+ Helper.makeSnackBar(rootView, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void getPlaceByUuid(String placeUuid) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Please_wait));
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.getPlaceFromUuid(token, placeUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+
+ if (data != null && data.has("places")) {
+ Place place = new Place();
+
+ for (JsonElement element : data.getAsJsonArray("places")) {
+ JsonObject placeObject = element.getAsJsonObject();
+
+ place.setUuid(placeObject.get("uuid").getAsString());
+ place.setName(placeObject.get("name").getAsString());
+ place.setDescription(placeObject.get("description").getAsString());
+ place.setAddress(placeObject.get("address").getAsString());
+ place.setLatitude(placeObject.get("latitude").getAsString());
+ place.setLongitude(placeObject.get("longitude").getAsString());
+ }
+ Helper.goTo(context, PlaceDetailActivity.class, Constants.PLACE, place);
+ } else {
+// Helper.makeSnackBar(rlPlaceDetails, getString(R.string.No_reports_found));
+ Log.e(TAG, "No place found");
+
+ }
+ } else {
+// Helper.makeSnackBar(rlPlaceDetails, getString(R.string.Failed_to_load_reports_Try_again_));
+ Log.e(TAG, "Get Reports Error: " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Get Reports Failure: ", t);
+// Helper.makeSnackBar(rlPlaceDetails, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+}
diff --git a/app/src/main/java/com/example/acloc/adapter/MyReportsAdapter.java b/app/src/main/java/com/example/acloc/adapter/MyReportsAdapter.java
new file mode 100644
index 0000000..8d05c56
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/adapter/MyReportsAdapter.java
@@ -0,0 +1,169 @@
+package com.example.acloc.adapter;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.acloc.R;
+import com.example.acloc.activities.AddReportActivity;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Report;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class MyReportsAdapter extends RecyclerView.Adapter {
+ public static final String TAG = MyReportsAdapter.class.getSimpleName();
+ private final Context context;
+ private List reportList;
+
+ public MyReportsAdapter(Context context, List reportList) {
+ this.context = context;
+ this.reportList = reportList;
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ public void updateReportList(List reportList) {
+ try {
+ if (reportList != null) {
+ this.reportList = reportList;
+ notifyDataSetChanged();
+ }
+ } catch (Exception exception) {
+ Log.e(TAG, "Error in MyReportsAdapter", exception);
+ }
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View detailItem = inflater.inflate(R.layout.list_view_my_report, parent, false);
+ return new ViewHolder(detailItem);
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onBindViewHolder(@NonNull MyReportsAdapter.ViewHolder holder, int position) {
+ try {
+ if (!reportList.isEmpty()) {
+ Report report = reportList.get(position);
+ holder.tvPlaceName.setText(report.getPlaceName());
+ holder.tvDescription.setText(report.getDescription());
+ if (report.getReportRating() == 1) {
+ holder.tvRating.setText(context.getString(R.string.Rating_BAD));
+ holder.tvRating.setBackgroundTintList(ContextCompat.getColorStateList(context, R.color.red));
+ } else if (report.getReportRating() == 2) {
+ holder.tvRating.setText(context.getString(R.string.Rating_AVERAGE));
+ holder.tvRating.setBackgroundTintList(ContextCompat.getColorStateList(context, R.color.yellow));
+ } else if (report.getReportRating() == 3) {
+ holder.tvRating.setText(context.getString(R.string.Rating_GOOD));
+ holder.tvRating.setBackgroundTintList(ContextCompat.getColorStateList(context, R.color.green));
+ }
+
+ holder.ivDelete.setOnClickListener(v -> {
+ String confirmationText = context.getString(R.string.Delete_report);
+
+ AlertDialog dialog = DialogUtils.confirmationDialog(
+ context,
+ confirmationText,
+ (dialogInterface, i) -> {
+ removeReport(report.getUuid());
+ }
+ );
+ dialog.show();
+ });
+
+ holder.ivEdit.setOnClickListener(v -> {
+ Helper.goTo(context, AddReportActivity.class, Constants.REPORT, report);
+ });
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in MyReports Adapter", e);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return reportList.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView tvPlaceName, tvDescription, tvRating;
+ private final ImageView ivEdit, ivDelete;
+
+ public ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ tvPlaceName = itemView.findViewById(R.id.tvPlaceName);
+ tvDescription = itemView.findViewById(R.id.tvDescription);
+ tvRating = itemView.findViewById(R.id.tvRating);
+ ivEdit = itemView.findViewById(R.id.ivEdit);
+ ivDelete = itemView.findViewById(R.id.ivDelete);
+ }
+ }
+
+ private void removeReport(String reportUuid) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Removing_Report));
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.removeReport(token, reportUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ View rootView = ((Activity) context).findViewById(android.R.id.content);
+
+ if (response.isSuccessful()) {
+ // Find position of the report to be deleted
+ int positionToRemove = -1;
+ for (int i = 0; i < reportList.size(); i++) {
+ if (reportList.get(i).getUuid().equals(reportUuid)) {
+ positionToRemove = i;
+ break;
+ }
+ }
+
+ // Remove the report from the list
+ if (positionToRemove != -1) {
+ reportList.remove(positionToRemove);
+ notifyItemRemoved(positionToRemove); // Notify the adapter that the item was removed
+ }
+
+ Log.d(TAG, "Report removed!");
+ Helper.makeSnackBar(rootView, context.getString(R.string.Report_removed));
+ } else {
+ Log.d(TAG, "Failed to remove report");
+ Helper.makeSnackBar(rootView, context.getString(R.string.Failed_to_remove_report));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ View rootView = ((Activity) context).findViewById(android.R.id.content);
+ Log.e(TAG, "Remove Report Error: ", t);
+ Helper.makeSnackBar(rootView, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/adapter/PlaceReportsAdapter.java b/app/src/main/java/com/example/acloc/adapter/PlaceReportsAdapter.java
new file mode 100644
index 0000000..10f097e
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/adapter/PlaceReportsAdapter.java
@@ -0,0 +1,93 @@
+package com.example.acloc.adapter;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.acloc.R;
+import com.example.acloc.model.Report;
+
+import java.util.List;
+
+public class PlaceReportsAdapter extends RecyclerView.Adapter {
+ public static final String TAG = PlaceReportsAdapter.class.getSimpleName();
+ private final Context context;
+ private List reportList;
+
+ public PlaceReportsAdapter(Context context, List reportList) {
+ this.context = context;
+ this.reportList = reportList;
+ }
+ public void clearReports() {
+ reportList.clear();
+ notifyDataSetChanged();
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ public void updateReportsList(List reportList) {
+ try {
+ if (reportList != null) {
+ this.reportList = reportList;
+ notifyDataSetChanged();
+ }
+ } catch (Exception exception) {
+ Log.e(TAG, "Error in PlaceReportsAdapter", exception);
+ }
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View detailItem = inflater.inflate(R.layout.list_view_place_reports, parent, false);
+ return new ViewHolder(detailItem);
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void onBindViewHolder(@NonNull PlaceReportsAdapter.ViewHolder holder, int position) {
+ try {
+ if (!reportList.isEmpty()) {
+ Report report = reportList.get(position);
+ holder.tvDescription.setText(report.getDescription());
+ if (report.getReportRating() == 1) {
+ holder.tvRating.setText(context.getString(R.string.Rating_BAD));
+ holder.ivRating.setImageResource(R.drawable.ic_thumbs_down);} else if (report.getReportRating() == 2) {
+ holder.tvRating.setText(context.getString(R.string.Rating_AVERAGE));
+ holder.ivRating.setImageResource(R.drawable.ic_thumb_up_average);
+ } else if (report.getReportRating() == 3) {
+ holder.tvRating.setText(context.getString(R.string.Rating_GOOD));
+ holder.ivRating.setImageResource(R.drawable.ic_thumbs_up);
+ }
+
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in PlaceReportsAdapter", e);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return reportList.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView tvDescription, tvRating;
+ private final ImageView ivRating;
+
+ public ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ tvDescription = itemView.findViewById(R.id.tvDescription);
+ tvRating = itemView.findViewById(R.id.tvRating);
+ ivRating = itemView.findViewById(R.id.ivRating);
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/adapter/UserAdapter.java b/app/src/main/java/com/example/acloc/adapter/UserAdapter.java
new file mode 100644
index 0000000..4edfefc
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/adapter/UserAdapter.java
@@ -0,0 +1,156 @@
+package com.example.acloc.adapter;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.acloc.R;
+import com.example.acloc.activities.ManageRolesActivity;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.User;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.gson.JsonObject;
+
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class UserAdapter extends RecyclerView.Adapter {
+ public static final String TAG = UserAdapter.class.getSimpleName();
+ private final Context context;
+ private List userList;
+
+ public UserAdapter(Context context, List userList) {
+ this.context = context;
+ this.userList = userList;
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ public void updateUserList(List userList) {
+ try {
+ if (userList != null) {
+ this.userList = userList;
+ notifyDataSetChanged();
+ }
+ } catch (Exception exception) {
+ Log.e(TAG, "Error in UserAdapter", exception);
+ }
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View detailItem = inflater.inflate(R.layout.list_view_users, parent, false);
+ return new ViewHolder(detailItem);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull UserAdapter.ViewHolder holder, int position) {
+ try {
+ if (!userList.isEmpty()) {
+ User user = userList.get(position);
+ holder.tvUsername.setText(user.getUsername());
+
+ ArrayAdapter roleAdapter = new ArrayAdapter<>(context, android.R.layout.simple_dropdown_item_1line, Constants.ROLES_OPTIONS);
+ holder.acRole.setAdapter(roleAdapter);
+ holder.acRole.setThreshold(1);
+
+ if (user.getRole() != null) {
+ holder.acRole.setText(user.getRole(), false);
+ }
+
+ // Show dropdown on click or focus
+ holder.acRole.setOnClickListener(v -> holder.acRole.showDropDown());
+ holder.acRole.setOnFocusChangeListener((v, hasFocus) -> {
+ if (hasFocus) holder.acRole.showDropDown();
+ });
+
+ // Handle item selection
+ holder.acRole.setOnItemClickListener((parent, view, posInDropdown, id) -> {
+ String selectedRole = parent.getItemAtPosition(posInDropdown).toString();
+
+ AlertDialog dialog = DialogUtils.confirmationDialog(context, context.getString(R.string.Change_Role_to) + " " + selectedRole, (dialogInterface, i) -> {
+ if (selectedRole.equalsIgnoreCase(Constants.ADMIN)) {
+ updateRole(user.getUuid(), SharedPref.getAdminRoleUuid(context), holder.itemView);
+ } else if (selectedRole.equalsIgnoreCase(Constants.VIEWER)) {
+ updateRole(user.getUuid(), SharedPref.getViewerRoleUuid(context), holder.itemView);
+ }
+ });
+
+ dialog.show();
+ });
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in User Adapter", e);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return userList.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView tvUsername;
+ private AutoCompleteTextView acRole;
+
+ public ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ tvUsername = itemView.findViewById(R.id.tvUsername);
+ acRole = itemView.findViewById(R.id.acRole);
+ }
+ }
+
+ private void updateRole(String uuid, String fkRole, View rootView) {
+
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Updating_Role));
+
+ JsonObject userBody = new JsonObject();
+ userBody.addProperty("role", fkRole);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.updateRole(token, uuid, userBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful()) {
+ Helper.makeSnackBar(rootView, context.getString(R.string.User_Role_update_successfully));
+ // Reload userList after update
+ if (context instanceof ManageRolesActivity) {
+ ((ManageRolesActivity) context).getAllUsers();
+ }
+ } else {
+ Helper.makeSnackBar(rootView, context.getString(R.string.Update_failed_Server_error_Try_again));
+ notifyDataSetChanged(); // Reload previous userList
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Update Role Error: ", t);
+ Helper.makeSnackBar(rootView, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/api/ApiClient.java b/app/src/main/java/com/example/acloc/api/ApiClient.java
new file mode 100644
index 0000000..04e5a6d
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/api/ApiClient.java
@@ -0,0 +1,20 @@
+package com.example.acloc.api;
+
+import static com.example.acloc.utility.Constants.BASE_URL;
+
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+public class ApiClient {
+ private static Retrofit retrofit;
+
+ public static Retrofit getClient() {
+ if (retrofit == null) {
+ retrofit = new Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build();
+ }
+ return retrofit;
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/dialog/AlertChangePasswordDialog.java b/app/src/main/java/com/example/acloc/dialog/AlertChangePasswordDialog.java
new file mode 100644
index 0000000..0920695
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/dialog/AlertChangePasswordDialog.java
@@ -0,0 +1,167 @@
+package com.example.acloc.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.AppCompatButton;
+
+import com.example.acloc.R;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.gson.JsonObject;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class AlertChangePasswordDialog implements View.OnClickListener {
+ private static final String TAG = AlertChangePasswordDialog.class.getSimpleName();
+ private View alertView;
+ private TextInputEditText etOldPassword, etNewPassword;
+ private AppCompatButton btnCancel, btnUpdate;
+ private Dialog dialog;
+ private Context context;
+
+ public AlertChangePasswordDialog(Context context) {
+ this.context = context;
+ }
+
+ public Dialog openChangePasswordDialog() {
+ try {
+ try {
+ LayoutInflater layoutInflater = ((Activity) context).getLayoutInflater();
+ alertView = layoutInflater.inflate(R.layout.alert_dialog_change_password, null);
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
+ if (alertView.getParent() != null) {
+ ((ViewGroup) alertView.getParent()).removeView(alertView);
+ }
+ alertBuilder.setView(alertView);
+
+ initUI();
+ setListeners();
+ dialog = alertBuilder.create();
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AlertChangePasswordDialog: ", e);
+ }
+
+ dialog.show();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AlertChangePasswordDialog: ", e);
+ }
+ return dialog;
+ }
+
+ private void initUI() {
+ etOldPassword = alertView.findViewById(R.id.etOldPassword);
+ etNewPassword = alertView.findViewById(R.id.etNewPassword);
+ btnCancel = alertView.findViewById(R.id.btnCancel);
+ btnUpdate = alertView.findViewById(R.id.btnUpdate);
+ }
+
+ private void setListeners() {
+ btnCancel.setOnClickListener(this);
+ btnUpdate.setOnClickListener(this);
+ }
+
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.btnCancel) {
+ if (dialog != null && dialog.isShowing()) {
+ dialog.dismiss();
+ }
+ } else if (id == R.id.btnUpdate) {
+ onClickBtnUpdate();
+ }
+ }
+
+ private void onClickBtnUpdate() {
+ View[] views = {etOldPassword, etNewPassword};
+ if (Helper.isEmptyFieldValidation(views) && Helper.isPasswordValid(etNewPassword)) {
+ String username = SharedPref.getUserName(context);
+ String oldPassword = Helper.getStringFromInput(etOldPassword);
+ String newPassword = Helper.getStringFromInput(etNewPassword);
+ String uuid = SharedPref.getUserUid(context);
+
+ verifyOldPasswordRetrofit(uuid, username, oldPassword, newPassword);
+ }
+ }
+
+ private void verifyOldPasswordRetrofit(String uuid, String username, String oldPassword, String newPassword) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Verifying_old_password));
+
+ JsonObject loginBody = new JsonObject();
+ loginBody.addProperty("username", username);
+ loginBody.addProperty("password", oldPassword);
+
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+ Call call = apiService.verifyOldPassword(loginBody);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+
+ if (response.isSuccessful() && response.body() != null && response.body().has("user")) {
+ // Proceed to change password
+ changePasswordRetrofit(uuid, newPassword);
+ } else {
+ Helper.makeSnackBar(alertView, context.getString(R.string.Invalid_old_password));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Old password verification failed", t);
+ Helper.makeSnackBar(alertView, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void changePasswordRetrofit(String uuid, String newPassword) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Changing_password));
+
+ JsonObject body = new JsonObject();
+ body.addProperty("password", newPassword);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+ Call call = apiService.changePassword(token, uuid, body);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+
+ if (response.isSuccessful() && response.body() != null &&
+ response.body().toString().contains("User modified")) {
+ Helper.makeSnackBar(alertView, context.getString(R.string.Password_updated_successfully));
+ } else {
+ Helper.makeSnackBar(alertView, context.getString(R.string.Password_update_failed));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Password change failed", t);
+ Helper.makeSnackBar(alertView, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/dialog/AlertViewAddNewPlaceDialog.java b/app/src/main/java/com/example/acloc/dialog/AlertViewAddNewPlaceDialog.java
new file mode 100644
index 0000000..3722e30
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/dialog/AlertViewAddNewPlaceDialog.java
@@ -0,0 +1,234 @@
+package com.example.acloc.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.AppCompatButton;
+
+import com.example.acloc.MainActivity;
+import com.example.acloc.R;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Place;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.gson.JsonObject;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class AlertViewAddNewPlaceDialog implements View.OnClickListener {
+ private static final String TAG = AlertViewAddNewPlaceDialog.class.getSimpleName();
+ private View alertView;
+ private ImageView ivPlacePhoto;
+ private TextInputEditText etPlaceName, etLatitude, etLongitude, etAddress, etPlaceDescription;
+ private AppCompatButton btnSubmit;
+ private Dialog dialog;
+ private Context context;
+ private double lat, lng;
+ private String placeName, address;
+
+ private Place entity;
+ private String place_uuid;
+
+
+ public AlertViewAddNewPlaceDialog(Context context, double lat, double lng, String placeName, String address, String description, String uuid) {
+ this.context = context;
+ this.lat = lat;
+ this.lng = lng;
+ this.placeName = placeName;
+ this.address = address;
+ this.entity = new Place();
+ this.entity.setDescription(description);
+ this.entity.setName(placeName);
+ this.place_uuid = uuid;
+ }
+
+ public Dialog openPlaceDialog() {
+ try {
+ try {
+ LayoutInflater layoutInflater = ((Activity) context).getLayoutInflater();
+ alertView = layoutInflater.inflate(R.layout.alert_dialog_add_new_place, null);
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
+ if (alertView.getParent() != null) {
+ ((ViewGroup) alertView.getParent()).removeView(alertView);
+ }
+ alertBuilder.setView(alertView);
+
+ initUI();
+ setListeners();
+ setDataToText();
+
+ dialog = alertBuilder.create();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AlertPlaceDialog: ", e);
+ }
+
+ dialog.show();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AlertPlaceDialog: ", e);
+ }
+ return dialog;
+ }
+
+ private void initUI() {
+ ivPlacePhoto = alertView.findViewById(R.id.ivPlacePhoto);
+ etPlaceName = alertView.findViewById(R.id.etPlaceName);
+ etLatitude = alertView.findViewById(R.id.etLatitude);
+ etLongitude = alertView.findViewById(R.id.etLongitude);
+ etAddress = alertView.findViewById(R.id.etAddress);
+ etPlaceDescription = alertView.findViewById(R.id.etPlaceDescription);
+ btnSubmit = alertView.findViewById(R.id.btnSubmit);
+ }
+
+ private void setListeners() {
+ btnSubmit.setOnClickListener(this);
+ }
+
+ private void setDataToText() {
+ etPlaceName.setText(String.valueOf(placeName));
+ etLatitude.setText(String.valueOf(lat));
+ etLongitude.setText(String.valueOf(lng));
+ etAddress.setText(String.valueOf(address));
+ etPlaceDescription.setText(entity.getDescription());
+ }
+
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.btnSubmit) {
+ onClickBtnSubmit();
+ }
+ }
+
+ private void onClickBtnSubmit() {
+ View[] views = {etPlaceName, etLatitude, etLongitude, etAddress, etPlaceDescription};
+ if (Helper.isEmptyFieldValidation(views)) {
+ setInputDataToEntity();
+
+ if (place_uuid != null && !place_uuid.isEmpty()) {
+ // Update existing place
+ updatePlaceRetrofit(place_uuid, entity.getName(), entity.getDescription(),
+ entity.getAddress(), entity.getLatitude(), entity.getLongitude(), entity.getCreatedBy());
+ } else {
+ // Insert new place
+ insertPlaceRetrofit(entity.getName(), entity.getDescription(),
+ entity.getAddress(), entity.getLatitude(), entity.getLongitude(), entity.getCreatedBy());
+ }
+// Helper.goTo(context, AddReportActivity.class, Constants.PLACE, entity);
+ }
+ }
+
+ private void setInputDataToEntity() {
+ entity.setName(Helper.getStringFromInput(etPlaceName));
+ entity.setDescription(Helper.getStringFromInput(etPlaceDescription));
+ entity.setAddress(Helper.getStringFromInput(etAddress));
+ entity.setLatitude(Helper.getStringFromInput(etLatitude));
+ entity.setLongitude(Helper.getStringFromInput(etLongitude));
+ entity.setCreatedBy(SharedPref.getUserUid(context));
+ entity.setUuid(place_uuid);
+ }
+
+ private void insertPlaceRetrofit(String name, String description, String address,
+ String latitude, String longitude, String createdBy) {
+ DialogUtils.showLoadingDialog(context, "Please wait...");
+
+ JsonObject placeBody = new JsonObject();
+ placeBody.addProperty("name", name);
+ placeBody.addProperty("description", description);
+ placeBody.addProperty("address", address);
+ placeBody.addProperty("latitude", latitude);
+ placeBody.addProperty("longitude", longitude);
+ placeBody.addProperty("createdBy", createdBy);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.insertPlace(token, placeBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+ if (data != null && data.has("places")) {
+ JsonObject placeObject = data.getAsJsonArray("places").get(0).getAsJsonObject();
+ place_uuid = placeObject.get("uuid").getAsString();
+ Log.d(TAG, "Place UUID: " + place_uuid);
+ Helper.makeSnackBar(alertView, "Place inserted successfully!");
+ alertView.postDelayed(() -> {
+ Helper.goToAndFinish(alertView.getContext(), MainActivity.class);
+ }, 500);
+
+ } else {
+ Helper.makeSnackBar(alertView, "Failed to extract place UUID.");
+ }
+ } else {
+ Helper.makeSnackBar(alertView, "Insert failed. Server error.");
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Insert Place Error: ", t);
+ Helper.makeSnackBar(alertView, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+ private void updatePlaceRetrofit(String uuid, String name, String description, String address,
+ String latitude, String longitude, String createdBy) {
+
+ DialogUtils.showLoadingDialog(context, "Updating place...");
+
+ JsonObject placeBody = new JsonObject();
+ placeBody.addProperty("name", name);
+ placeBody.addProperty("description", description);
+ placeBody.addProperty("address", address);
+ placeBody.addProperty("latitude", latitude);
+ placeBody.addProperty("longitude", longitude);
+ placeBody.addProperty("createdBy", createdBy);
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.updatePlace(token, uuid, placeBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful()) {
+ Helper.makeSnackBar(alertView, "Place updated successfully!");
+ alertView.postDelayed(() -> {
+ Helper.goToAndFinish(alertView.getContext(), MainActivity.class);
+ }, 500);
+ } else {
+ Helper.makeSnackBar(alertView, "Update failed. Server error.");
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Update Place Error: ", t);
+ Helper.makeSnackBar(alertView, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+
+}
diff --git a/app/src/main/java/com/example/acloc/dialog/AlertViewOrUpdateProfileDialog.java b/app/src/main/java/com/example/acloc/dialog/AlertViewOrUpdateProfileDialog.java
new file mode 100644
index 0000000..2871041
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/dialog/AlertViewOrUpdateProfileDialog.java
@@ -0,0 +1,179 @@
+package com.example.acloc.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.AppCompatButton;
+
+import com.example.acloc.R;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.User;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class AlertViewOrUpdateProfileDialog implements View.OnClickListener {
+ private static final String TAG = AlertViewOrUpdateProfileDialog.class.getSimpleName();
+ private View alertView;
+ private TextInputEditText etUsername, etEmail;
+ private AppCompatButton btnCancel, btnUpdate;
+ private Dialog dialog;
+ private Context context;
+ private User entity;
+
+ public AlertViewOrUpdateProfileDialog(Context context) {
+ this.context = context;
+ this.entity = new User();
+ }
+
+ public Dialog openProfileDialog() {
+ try {
+ try {
+ LayoutInflater layoutInflater = ((Activity) context).getLayoutInflater();
+ alertView = layoutInflater.inflate(R.layout.alert_dialog_profile, null);
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
+ if (alertView.getParent() != null) {
+ ((ViewGroup) alertView.getParent()).removeView(alertView);
+ }
+ alertBuilder.setView(alertView);
+
+ initUI();
+ setListeners();
+ dialog = alertBuilder.create();
+
+ //get user profile
+ getProfile();
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AlertProfileDialog: ", e);
+ }
+
+ dialog.show();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in AlertProfileDialog: ", e);
+ }
+ return dialog;
+ }
+
+ private void setDataToText() {
+ try {
+ if (entity != null) {
+ etUsername.setText(entity.getUsername());
+ etEmail.setText(entity.getEmail());
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in setDataToText: ", e);
+ }
+ }
+
+ private void getProfile() {
+ entity.setUsername(SharedPref.getUserName(context));
+ entity.setEmail(SharedPref.getUserEmail(context));
+ setDataToText();
+ }
+
+ private void initUI() {
+ etUsername = alertView.findViewById(R.id.etUsername);
+ etEmail = alertView.findViewById(R.id.etEmail);
+ btnCancel = alertView.findViewById(R.id.btnCancel);
+ btnUpdate = alertView.findViewById(R.id.btnUpdate);
+ }
+
+ private void setListeners() {
+ btnCancel.setOnClickListener(this);
+ btnUpdate.setOnClickListener(this);
+ }
+
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.btnCancel) {
+ if (dialog != null && dialog.isShowing()) {
+ dialog.dismiss();
+ }
+ } else if (id == R.id.btnUpdate) {
+ onClickBtnUpdate();
+ }
+ }
+
+ private void onClickBtnUpdate() {
+ View[] views = {etUsername, etEmail};
+ if (Helper.isEmptyFieldValidation(views) && Helper.isEmailValid(etEmail)) {
+ setInputDataToEntity();
+ String uuid = SharedPref.getUserUid(context);
+ updateUserWithRetrofit(uuid, entity.getUsername(), entity.getEmail());
+ }
+ }
+
+ private void setInputDataToEntity() {
+ entity.setUsername(Helper.getStringFromInput(etUsername));
+ entity.setEmail(Helper.getStringFromInput(etEmail));
+ }
+
+ private void updateUserWithRetrofit(String uuid, String username, String email) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Updating));
+
+ JsonObject jsonBody = new JsonObject();
+ jsonBody.addProperty("username", username);
+ jsonBody.addProperty("email", email);
+
+ String accessToken = SharedPref.getAccessToken(context);
+ String bearerToken = "Bearer " + accessToken;
+
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+ Call call = apiService.updateUser(bearerToken, uuid, jsonBody);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ if (responseBody.toString().contains("User modified")) {
+ Helper.makeSnackBar(alertView, context.getString(R.string.Profile_Updated_Successfully));
+ SharedPref.setUsername(context, username);
+ SharedPref.setUserEmail(context, email);
+ } else {
+ Helper.makeSnackBar(alertView, context.getString(R.string.Try_again_later));
+ }
+ } else {
+ try {
+ if (response.errorBody() != null) {
+ String error = response.errorBody().string();
+ Log.e(TAG, "Update Failed: " + error);
+ Helper.makeSnackBar(alertView, context.getString(R.string.Update_Failed));
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error parsing error response", e);
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Update API failed", t);
+ Helper.makeSnackBar(alertView, context.getString(R.string.Something_went_wrong_Try_again));
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/fragments/FavoriteFragment.java b/app/src/main/java/com/example/acloc/fragments/FavoriteFragment.java
new file mode 100644
index 0000000..2873184
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/fragments/FavoriteFragment.java
@@ -0,0 +1,196 @@
+package com.example.acloc.fragments;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.example.acloc.R;
+import com.example.acloc.adapter.FavoriteAdapter;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Favorite;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class FavoriteFragment extends Fragment {
+ public static final String TAG = FavoriteFragment.class.getSimpleName();
+ private View view;
+ private FrameLayout rlFavorite;
+ private RecyclerView rvFavorite;
+ private TextView tvNoData;
+ private Context context;
+ private FavoriteAdapter adapter;
+ private Favorite favoriteEntity;
+ private List favoriteList;
+
+ public FavoriteFragment() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ view = inflater.inflate(R.layout.fragment_favorite, container, false);
+ initUI();
+ initObj();
+ initListener();
+ return view;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ this.context = context;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (adapter != null) {
+ setDataVisibility(false);
+ favoriteList.clear();
+ }
+ if (context != null) loadData();
+ }
+
+ private void setDataVisibility(boolean isDataAvailable) {
+ if (isDataAvailable) {
+ rvFavorite.setVisibility(View.VISIBLE);
+ tvNoData.setVisibility(View.GONE);
+ } else {
+ rvFavorite.setVisibility(View.GONE);
+ tvNoData.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void initUI() {
+ rlFavorite = view.findViewById(R.id.rlFavorite);
+ rvFavorite = view.findViewById(R.id.rvFavorite);
+ tvNoData = view.findViewById(R.id.tvNoData);
+ }
+
+ private void initObj() {
+ context = getContext();
+ favoriteEntity = new Favorite();
+ }
+
+ private void initListener() {
+
+ }
+
+ private void loadData() {
+ try {
+ if (favoriteList == null) {
+ favoriteList = new ArrayList<>();
+ }
+ String userUuid = SharedPref.getUserUid(context);
+ getFavoriteByUserUuid(userUuid);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in FavoriteFragment", e);
+ Helper.makeSnackBar(view, getString(R.string.Something_went_wrong_Try_again));
+ }
+ }
+
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void setUpRecyclerView() {
+ try {
+ if (adapter != null) {
+ adapter.updateFavoriteList(favoriteList);
+ } else {
+ adapter = new FavoriteAdapter(context, favoriteList);
+ rvFavorite.setAdapter(adapter);
+ rvFavorite.setLayoutManager(Helper.getVerticalManager(context));
+ adapter.notifyDataSetChanged();
+ }
+ setDataVisibility(true);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in FavoriteFragment", e);
+ Helper.showToast(context, getString(R.string.Something_went_wrong_Try_again));
+ setDataVisibility(false);
+ }
+ }
+
+ private void getFavoriteByUserUuid(String userUuid) {
+ DialogUtils.showLoadingDialog(context, context.getString(R.string.Loading_Favorites));
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.getUserFavorites(token, userUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+
+ if (data != null && data.has("favorites")) {
+ favoriteList.clear(); // Clear previous list
+
+ for (JsonElement element : data.getAsJsonArray("favorites")) {
+ JsonObject favoriteObject = element.getAsJsonObject();
+ Favorite favorite = new Favorite();
+
+ favorite.setUuid(favoriteObject.get("uuid").getAsString());
+ favorite.setActive(favoriteObject.get("active").getAsString());
+ favorite.setPlaceUuid(favoriteObject.get("place_uuid").getAsString());
+ favorite.setPlaceName(favoriteObject.get("place_name").getAsString());
+ favorite.setPlaceAddress(favoriteObject.get("place_address").getAsString());
+ favorite.setPlaceLat(favoriteObject.get("place_latitude").getAsString());
+ favorite.setPlaceLng(favoriteObject.get("place_longitude").getAsString());
+ favorite.setPlaceDescription(favoriteObject.get("place_description").getAsString());
+
+ favoriteList.add(favorite);
+ }
+
+ // Set up recyclerview
+ if (!favoriteList.isEmpty()) {
+ setUpRecyclerView();
+ } else {
+ setDataVisibility(false);
+ }
+
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlFavorite, context.getString(R.string.No_Favorite_found));
+ }
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlFavorite, context.getString(R.string.Failed_to_load_favorite_Try_again));
+ Log.e(TAG, "Get favorite Error: " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Get favorite Failure: ", t);
+ Helper.makeSnackBar(rlFavorite, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/fragments/MapFragment.java b/app/src/main/java/com/example/acloc/fragments/MapFragment.java
new file mode 100644
index 0000000..919e503
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/fragments/MapFragment.java
@@ -0,0 +1,381 @@
+package com.example.acloc.fragments;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+
+import android.provider.Settings;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+
+import com.example.acloc.R;
+import com.example.acloc.activities.AddNewPlaceActivity;
+import com.example.acloc.activities.PlaceDetailActivity;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Place;
+import com.example.acloc.utility.Constants;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+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.SupportMapFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.google.android.material.textfield.TextInputEditText;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+
+public class MapFragment extends Fragment {
+ public static final String TAG = MapFragment.class.getSimpleName();
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001;
+
+ private RelativeLayout rlMap;
+ private View view;
+ private TextInputEditText etSearchLocation;
+ private GoogleMap googleMap;
+ private FusedLocationProviderClient fusedLocationClient;
+ private Geocoder geocoder;
+ private Dialog dialog;
+ private Context context;
+
+ private List placeList = new ArrayList<>();
+
+
+ public MapFragment() {
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ fetchAndShowAllPlaces();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ view = inflater.inflate(R.layout.fragment_map, container, false);
+ initUI();
+ initListener();
+ initObj();
+ return view;
+ }
+
+ private void initUI() {
+ rlMap = view.findViewById(R.id.rlMap);
+ etSearchLocation = view.findViewById(R.id.etSearchLocation);
+ }
+
+ private void initObj() {
+ context = getContext();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private void initListener() {
+ SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager()
+ .findFragmentById(R.id.mapFrame);
+
+ if (mapFragment != null) {
+ mapFragment.getMapAsync(map -> {
+ googleMap = map;
+ initMap();
+ });
+ }
+
+ // Handle search icon click
+ etSearchLocation.setOnTouchListener((v, event) -> {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ Drawable drawableEnd = etSearchLocation.getCompoundDrawables()[2]; // Right drawable
+
+ if (drawableEnd != null) {
+ int drawableWidth = drawableEnd.getBounds().width();
+ int touchAreaStart = etSearchLocation.getWidth() - drawableWidth - etSearchLocation.getPaddingEnd();
+
+ if (event.getX() >= touchAreaStart) {
+ v.performClick(); // important for accessibility
+ String address = Helper.getStringFromInput(etSearchLocation);
+ if (!address.isEmpty()) {
+ searchLocationByAddress(address);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+
+ // trigger on Enter/Done key
+ etSearchLocation.setOnEditorActionListener((v, actionId, event) -> {
+ String address = Helper.getStringFromInput(etSearchLocation);
+ if (!address.isEmpty()) {
+ searchLocationByAddress(address);
+ }
+ return true;
+ });
+ }
+
+ private void initMap() {
+ fetchAndShowAllPlaces();
+
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity());
+ geocoder = new Geocoder(requireContext(), Locale.getDefault());
+
+ if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+
+ googleMap.setMyLocationEnabled(true);
+
+ fusedLocationClient.getLastLocation().addOnSuccessListener(location -> {
+ if (location != null) {
+ LatLng currentLatLng = new LatLng(location.getLatitude(), location.getLongitude());
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 14f));
+ showNearbyPlaces(currentLatLng);
+ }
+ });
+
+ googleMap.setOnMapClickListener(latLng -> {
+ Place existingPlace = getPlaceIfExists(latLng);
+ if (existingPlace != null) {
+ Helper.goTo(getContext(), PlaceDetailActivity.class, Constants.PLACE, existingPlace); //Navigate to PlaceDetailActivity if the place already exists
+ return;
+//
+// dialog = new AlertViewAddNewPlaceDialog(requireContext(),
+// Double.parseDouble(existingPlace.getLatitude()),
+// Double.parseDouble(existingPlace.getLongitude()),
+// existingPlace.getName(),
+// existingPlace.getAddress(),
+// existingPlace.getDescription(),
+// existingPlace.getUuid())
+// .openPlaceDialog();
+ } else {
+ try {
+ List addressList = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
+ if (addressList != null && !addressList.isEmpty()) {
+ Address address = addressList.get(0);
+ String addressLine = address.getAddressLine(0);
+ String placeName = address.getFeatureName();
+
+// dialog = new AlertViewAddNewPlaceDialog(requireContext(),
+// latLng.latitude,
+// latLng.longitude,
+// placeName,
+// addressLine,
+// "", // Empty description
+// null // UUID
+// ).openPlaceDialog();
+
+ Place placeEntity = new Place();
+ placeEntity.setLatitude(String.valueOf(latLng.latitude));
+ placeEntity.setLongitude(String.valueOf(latLng.longitude));
+ placeEntity.setName(placeName);
+ placeEntity.setAddress(addressLine);
+ placeEntity.setDescription(""); // Empty description
+ placeEntity.setUuid(null); // No UUID for new place
+ Helper.goTo(getContext(), AddNewPlaceActivity.class, Constants.PLACE, placeEntity);
+ } else {
+ Place placeEntity = new Place();
+ placeEntity.setLatitude(String.valueOf(latLng.latitude));
+ placeEntity.setLongitude(String.valueOf(latLng.longitude));
+ placeEntity.setName("");
+ placeEntity.setAddress("Address not found");
+ placeEntity.setDescription(""); // Empty description
+ placeEntity.setUuid(null); // No UUID for a new place
+ Helper.goTo(getContext(), AddNewPlaceActivity.class, Constants.PLACE, placeEntity);
+
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ googleMap.setOnMarkerClickListener(marker -> {
+ marker.hideInfoWindow();
+
+ LatLng latLng = marker.getPosition();
+ Place existingPlace = getPlaceIfExists(latLng);
+
+ if (existingPlace != null) {
+ Helper.goTo(getContext(), PlaceDetailActivity.class, Constants.PLACE, existingPlace);
+ return true;
+ }
+
+ return true;
+ });
+
+
+ } else {
+ requestPermissions(new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
+ }
+ }
+
+ private Place getPlaceIfExists(LatLng clickedLatLng) {
+ final float[] result = new float[1];
+ for (Place place : placeList) {
+ double lat = Double.parseDouble(place.getLatitude());
+ double lng = Double.parseDouble(place.getLongitude());
+
+ Location.distanceBetween(
+ clickedLatLng.latitude, clickedLatLng.longitude,
+ lat, lng,
+ result);
+
+ if (result[0] < 10) { // less than 10 meters
+ return place;
+ }
+ }
+ return null;
+ }
+
+ private void showNearbyPlaces(LatLng latLng) {
+ googleMap.clear();
+// googleMap.addMarker(new MarkerOptions()
+// .position(latLng)
+// .title("You are here"));
+ }
+
+ private void searchLocationByAddress(String address) {
+ try {
+ List addresses = geocoder.getFromLocationName(address, 1);
+ if (addresses != null && !addresses.isEmpty()) {
+ Address location = addresses.get(0);
+ LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
+ googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f));
+ showNearbyPlaces(latLng);
+ } else {
+ Helper.makeSnackBar(rlMap, "Location not found");
+ }
+ } catch (IOException e) {
+ Log.d(TAG, getString(R.string.Something_went_wrong_Try_again));
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+
+ if (requestCode == LOCATION_PERMISSION_REQUEST_CODE
+ && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ initMap();
+ } else {
+ boolean showRationale = shouldShowRequestPermissionRationale(android.Manifest.permission.ACCESS_FINE_LOCATION);
+ if (!showRationale) {
+ showPermissionRequiredDialog();
+ } else {
+ Helper.showToast(context, getString(R.string.Location_permission_is_required));
+ } }
+ }
+ private void showPermissionRequiredDialog() {
+ new AlertDialog.Builder(requireContext())
+ .setTitle(getString(R.string.Location_Permission_Required))
+ .setMessage(getString(R.string.Location_permission_rationale))
+ .setCancelable(false)
+ .setPositiveButton(getString(R.string.Go_to_Settings), (dialog, which) -> {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", requireContext().getPackageName(), null);
+ intent.setData(uri);
+ startActivity(intent);
+ })
+ .setNegativeButton(getString(R.string.Exit_App), (dialog, which) -> requireActivity().finish())
+ .show();
+ }
+
+ private void fetchAndShowAllPlaces() {
+ String token = "Bearer " + SharedPref.getAccessToken(context); // get saved token
+
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl("https://locationapi-m13l.onrender.com/")
+ .addConverterFactory(GsonConverterFactory.create())
+ .build();
+
+ ApiService apiService = retrofit.create(ApiService.class);
+ Call call = apiService.getAllPlaces(token);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful() && response.body() != null) {
+ try {
+ JSONObject jsonObject = new JSONObject(response.body().string());
+ JSONArray placesArray = jsonObject.getJSONObject("_data").getJSONArray("places");
+
+ for (int i = 0; i < placesArray.length(); i++) {
+ JSONObject placeObj = placesArray.getJSONObject(i);
+ String name = placeObj.getString("name");
+ String address = placeObj.getString("address");
+ String description = placeObj.optString("description", "");
+ String createdBy = placeObj.optString("createdBy", "");
+ String uuid = placeObj.optString("uuid", "");
+ double lat = placeObj.getDouble("latitude");
+ double lng = placeObj.getDouble("longitude");
+
+ LatLng latLng = new LatLng(lat, lng);
+ googleMap.addMarker(new MarkerOptions()
+ .position(latLng)
+ .title(name)
+ .snippet(address));
+
+ // Add to memory list
+ Place place = new Place();
+ place.setName(name);
+ place.setAddress(address);
+ place.setLatitude(String.valueOf(lat));
+ place.setLongitude(String.valueOf(lng));
+ place.setDescription(description);
+ place.setCreatedBy(createdBy);
+ place.setUuid(uuid);
+
+ placeList.add(place);
+ }
+
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ Helper.makeSnackBar(rlMap, getString(R.string.Failed_to_load_places));
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ t.printStackTrace();
+ Helper.makeSnackBar(rlMap, getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/fragments/MyReportsFragment.java b/app/src/main/java/com/example/acloc/fragments/MyReportsFragment.java
new file mode 100644
index 0000000..9ec16d6
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/fragments/MyReportsFragment.java
@@ -0,0 +1,209 @@
+package com.example.acloc.fragments;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.example.acloc.MainActivity;
+import com.example.acloc.R;
+import com.example.acloc.adapter.MyReportsAdapter;
+import com.example.acloc.api.ApiClient;
+import com.example.acloc.interfaces.ApiService;
+import com.example.acloc.model.Report;
+import com.example.acloc.utility.DialogUtils;
+import com.example.acloc.utility.Helper;
+import com.example.acloc.utility.SharedPref;
+import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class MyReportsFragment extends Fragment implements View.OnClickListener {
+ public static final String TAG = MyReportsFragment.class.getSimpleName();
+ private View view;
+ private FrameLayout rlMyReport;
+ private RecyclerView rvReport;
+ private TextView tvNoData;
+ private ExtendedFloatingActionButton extendedFbReport;
+ private Context context;
+ private MyReportsAdapter adapter;
+ private Report reportEntity;
+ private List reportList;
+
+ public MyReportsFragment() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ view = inflater.inflate(R.layout.fragment_my_reports, container, false);
+ initUI();
+ initObj();
+ initListener();
+ return view;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ this.context = context;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (adapter != null) {
+ setDataVisibility(false);
+ reportList.clear();
+ }
+ if (context != null) loadData();
+ }
+
+ private void setDataVisibility(boolean isDataAvailable) {
+ if (isDataAvailable) {
+ rvReport.setVisibility(View.VISIBLE);
+ tvNoData.setVisibility(View.GONE);
+ } else {
+ rvReport.setVisibility(View.GONE);
+ tvNoData.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void initUI() {
+ rlMyReport = view.findViewById(R.id.rlMyReport);
+ rvReport = view.findViewById(R.id.rvReport);
+ tvNoData = view.findViewById(R.id.tvNoData);
+ extendedFbReport = view.findViewById(R.id.extendedFbReport);
+ }
+
+ private void initObj() {
+ context = getContext();
+ reportEntity = new Report();
+ }
+
+ private void initListener() {
+ extendedFbReport.setOnClickListener(this);
+ }
+
+ private void loadData() {
+ try {
+ if (reportList == null) {
+ reportList = new ArrayList<>();
+ }
+ String userUuid = SharedPref.getUserUid(context);
+ getReportsByUserUuid(userUuid);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in MyReportsFragment", e);
+ Helper.makeSnackBar(view, getString(R.string.Something_went_wrong_Try_again));
+ }
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void setUpRecyclerView() {
+ try {
+ if (adapter != null) {
+ adapter.updateReportList(reportList);
+ } else {
+ adapter = new MyReportsAdapter(context, reportList);
+ rvReport.setAdapter(adapter);
+ rvReport.setLayoutManager(Helper.getVerticalManager(context));
+ adapter.notifyDataSetChanged();
+ }
+ setDataVisibility(true);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in MyReportFragment", e);
+ Helper.makeSnackBar(rlMyReport, getString(R.string.Something_went_wrong_Try_again));
+ setDataVisibility(false);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.extendedFbReport) {
+ onClickExtendedFbReport();
+ }
+ }
+
+ //Open Map Fragment to add new place report
+ private void onClickExtendedFbReport() {
+ ((MainActivity) requireActivity())
+ .openFragmentFromChild(new MapFragment(), getString(R.string.Map), R.id.menu_map);
+ }
+
+ private void getReportsByUserUuid(String userUuid) {
+ DialogUtils.showLoadingDialog(context, getString(R.string.Loading_reports));
+
+ String token = "Bearer " + SharedPref.getAccessToken(context);
+ ApiService apiService = ApiClient.getClient().create(ApiService.class);
+
+ Call call = apiService.getUserReports(token, userUuid);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ DialogUtils.dismissDialog();
+ if (response.isSuccessful() && response.body() != null) {
+ JsonObject responseBody = response.body();
+ JsonObject data = responseBody.getAsJsonObject("_data");
+
+ if (data != null && data.has("reports")) {
+ reportList.clear(); // Clear previous list
+
+ for (JsonElement element : data.getAsJsonArray("reports")) {
+ JsonObject reportObject = element.getAsJsonObject();
+ Report report = new Report();
+
+ report.setUuid(reportObject.get("uuid").getAsString());
+ report.setReportRating(reportObject.get("rating").getAsInt());
+ report.setDescription(reportObject.get("description").getAsString());
+ report.setPlaceName(reportObject.get("place_name").getAsString());
+ report.setPlaceUuid(reportObject.get("place_uuid").getAsString());
+
+ reportList.add(report);
+ }
+
+ // Set up recyclerview
+ if (!reportList.isEmpty()) {
+ setUpRecyclerView();
+ } else {
+ setDataVisibility(false);
+ }
+
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlMyReport, getString(R.string.No_reports_found));
+ }
+ } else {
+ setDataVisibility(false);
+ Helper.makeSnackBar(rlMyReport, getString(R.string.Failed_to_load_reports_Try_again_));
+ Log.e(TAG, "Get Reports Error: " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ DialogUtils.dismissDialog();
+ Log.e(TAG, "Get Reports Failure: ", t);
+ Helper.makeSnackBar(rlMyReport, context.getString(R.string.Network_error_Try_again));
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/interfaces/ApiService.java b/app/src/main/java/com/example/acloc/interfaces/ApiService.java
new file mode 100644
index 0000000..cdb7e87
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/interfaces/ApiService.java
@@ -0,0 +1,139 @@
+package com.example.acloc.interfaces;
+
+import com.google.gson.JsonObject;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.DELETE;
+import retrofit2.http.GET;
+import retrofit2.http.Header;
+import retrofit2.http.POST;
+import retrofit2.http.PUT;
+import retrofit2.http.Path;
+
+public interface ApiService {
+ @POST("signin")
+ Call registerUser(@Body JsonObject userData);
+
+ @POST("login")
+ Call loginUser(@Body JsonObject body);
+
+ @PUT("users/{uuid}")
+ Call updateUser(
+ @Header("Authorization") String bearerToken,
+ @Path("uuid") String uuid,
+ @Body JsonObject body
+ );
+
+ @POST("login")
+ Call verifyOldPassword(@Body JsonObject loginBody);
+
+ @PUT("users/{uuid}")
+ Call changePassword(
+ @Header("Authorization") String token,
+ @Path("uuid") String uuid,
+ @Body JsonObject body
+ );
+
+ @POST("places")
+ Call insertPlace(
+ @Header("Authorization") String token,
+ @Body JsonObject body
+ );
+
+ @POST("report_types")
+ Call insertReportType(
+ @Header("Authorization") String token,
+ @Body JsonObject body
+ );
+
+ @PUT("places/{uuid}")
+ Call updatePlace(@Header("Authorization") String token,
+ @Path("uuid") String uuid,
+ @Body JsonObject placeData);
+
+
+ @GET("places")
+ Call getAllPlaces(@Header("Authorization") String token);
+
+ @POST("users/{user_uuid}/places")
+ Call addPlaceToFavorites(
+ @Header("Authorization") String token,
+ @Path("user_uuid") String userUuid,
+ @Body JsonObject placeBody
+ );
+
+ @DELETE("users/{user_uuid}/places/{place_uuid}")
+ Call removePlaceFromFavorites(
+ @Header("Authorization") String token,
+ @Path("user_uuid") String userUuid,
+ @Path("place_uuid") String placeUuid
+ );
+
+ @GET("users/{user_uuid}/places")
+ Call getFavoritePlaces(
+ @Header("Authorization") String token,
+ @Path("user_uuid") String userUuid
+ );
+
+ @PUT("users/{user_uuid}/places")
+ Call restorePlaceToFavorites(
+ @Header("Authorization") String authToken,
+ @Path("user_uuid") String userUuid,
+ @Body JsonObject body
+ );
+
+ @POST("reports")
+ Call insertReport(
+ @Header("Authorization") String token,
+ @Body JsonObject reportBody
+ );
+
+ @GET("users/{user_uuid}/reports")
+ Call getUserReports(
+ @Header("Authorization") String token,
+ @Path("user_uuid") String userUuid
+ );
+
+ @DELETE("reports/{report_uuid}")
+ Call removeReport(
+ @Header("Authorization") String token,
+ @Path("report_uuid") String reportUuid
+ );
+
+ @PUT("/reports/{uuid}")
+ Call updateReport(@Header("Authorization") String token,
+ @Path("uuid") String uuid,
+ @Body JsonObject placeData);
+
+ @GET("places/{place_uuid}/reports")
+ Call getPlaceReports(
+ @Header("Authorization") String token,
+ @Path("place_uuid") String placeUuid
+ );
+
+ @GET("users/{user_uuid}/places")
+ Call getUserFavorites(
+ @Header("Authorization") String token,
+ @Path("user_uuid") String userUuid
+ );
+
+ @GET("roles")
+ Call getRoles(@Header("Authorization") String token);
+
+ @GET("users")
+ Call getAllUsers(@Header("Authorization") String token);
+
+
+ @PUT("users/{uuid}")
+ Call updateRole(@Header("Authorization") String token,
+ @Path("uuid") String uuid,
+ @Body JsonObject userData);
+
+ @GET("places/{uuid}")
+ Call getPlaceFromUuid(
+ @Header("Authorization") String token,
+ @Path("uuid") String uuid
+ );
+}
diff --git a/app/src/main/java/com/example/acloc/model/Favorite.java b/app/src/main/java/com/example/acloc/model/Favorite.java
new file mode 100644
index 0000000..7cfbe82
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/model/Favorite.java
@@ -0,0 +1,79 @@
+package com.example.acloc.model;
+
+import java.io.Serializable;
+
+public class Favorite implements Serializable {
+ String uuid, active, placeUuid, placeName, placeAddress, placeLat, placeLng, placeDescription, userUsername;
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getActive() {
+ return active;
+ }
+
+ public void setActive(String active) {
+ this.active = active;
+ }
+
+ public String getPlaceUuid() {
+ return placeUuid;
+ }
+
+ public void setPlaceUuid(String placeUuid) {
+ this.placeUuid = placeUuid;
+ }
+
+ public String getPlaceName() {
+ return placeName;
+ }
+
+ public void setPlaceName(String placeName) {
+ this.placeName = placeName;
+ }
+
+ public String getPlaceAddress() {
+ return placeAddress;
+ }
+
+ public void setPlaceAddress(String placeAddress) {
+ this.placeAddress = placeAddress;
+ }
+
+ public String getPlaceLat() {
+ return placeLat;
+ }
+
+ public void setPlaceLat(String placeLat) {
+ this.placeLat = placeLat;
+ }
+
+ public String getPlaceLng() {
+ return placeLng;
+ }
+
+ public void setPlaceLng(String placeLng) {
+ this.placeLng = placeLng;
+ }
+
+ public String getPlaceDescription() {
+ return placeDescription;
+ }
+
+ public void setPlaceDescription(String placeDescription) {
+ this.placeDescription = placeDescription;
+ }
+
+ public String getUserUsername() {
+ return userUsername;
+ }
+
+ public void setUserUsername(String userUsername) {
+ this.userUsername = userUsername;
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/model/Place.java b/app/src/main/java/com/example/acloc/model/Place.java
new file mode 100644
index 0000000..849dcc4
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/model/Place.java
@@ -0,0 +1,63 @@
+package com.example.acloc.model;
+
+import java.io.Serializable;
+
+public class Place implements Serializable {
+ String uuid, name, description, address, latitude, longitude, createdBy;
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getLatitude() {
+ return latitude;
+ }
+
+ public void setLatitude(String latitude) {
+ this.latitude = latitude;
+ }
+
+ public String getLongitude() {
+ return longitude;
+ }
+
+ public void setLongitude(String longitude) {
+ this.longitude = longitude;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/model/Report.java b/app/src/main/java/com/example/acloc/model/Report.java
new file mode 100644
index 0000000..8cb6ac8
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/model/Report.java
@@ -0,0 +1,81 @@
+package com.example.acloc.model;
+
+import java.io.Serializable;
+
+public class Report implements Serializable {
+ String uuid, fkUser, fkPlace, fkReportType, description, createdBy;
+ String placeName, placeUuid;
+ int reportRating;
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getFkUser() {
+ return fkUser;
+ }
+
+ public void setFkUser(String fkUser) {
+ this.fkUser = fkUser;
+ }
+
+ public String getFkPlace() {
+ return fkPlace;
+ }
+
+ public void setFkPlace(String fkPlace) {
+ this.fkPlace = fkPlace;
+ }
+
+ public String getFkReportType() {
+ return fkReportType;
+ }
+
+ public void setFkReportType(String fkReportType) {
+ this.fkReportType = fkReportType;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public int getReportRating() {
+ return reportRating;
+ }
+
+ public void setReportRating(int reportRating) {
+ this.reportRating = reportRating;
+ }
+
+ public String getPlaceName() {
+ return placeName;
+ }
+
+ public void setPlaceName(String placeName) {
+ this.placeName = placeName;
+ }
+
+ public String getPlaceUuid() {
+ return placeUuid;
+ }
+
+ public void setPlaceUuid(String placeUuid) {
+ this.placeUuid = placeUuid;
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/model/User.java b/app/src/main/java/com/example/acloc/model/User.java
new file mode 100644
index 0000000..9fd67a0
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/model/User.java
@@ -0,0 +1,56 @@
+package com.example.acloc.model;
+
+import java.io.Serializable;
+
+public class User implements Serializable {
+ private String username, email, password;
+ private String role, uuid, fkRole ;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getFkRole() {
+ return fkRole;
+ }
+
+ public void setFkRole(String fkRole) {
+ this.fkRole = fkRole;
+ }
+}
diff --git a/app/src/main/java/com/example/acloc/utility/Constants.java b/app/src/main/java/com/example/acloc/utility/Constants.java
new file mode 100644
index 0000000..740d5d7
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/utility/Constants.java
@@ -0,0 +1,20 @@
+package com.example.acloc.utility;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class Constants {
+ public static final String SHARED_PREF = "ACLOC";
+ public static final String ADMIN = "admin";
+ public static final String VIEWER = "viewer";
+ public static final String PLACE = "Place";
+ public static final String REPORT = "Report";
+ public static final String SOMETHING_WENT_WRONG = "Something went wrong";
+
+ public static final int GOOD_RATING = 3;
+ public static final int AVERAGE_RATING = 2;
+ public static final int BAD_RATING = 1;
+
+ public static final List ROLES_OPTIONS = Arrays.asList("admin", "viewer");
+ public static final String BASE_URL = "https://locationapi-m13l.onrender.com/";
+}
diff --git a/app/src/main/java/com/example/acloc/utility/DialogUtils.java b/app/src/main/java/com/example/acloc/utility/DialogUtils.java
new file mode 100644
index 0000000..f105ccf
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/utility/DialogUtils.java
@@ -0,0 +1,85 @@
+package com.example.acloc.utility;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.view.Window;
+import android.widget.TextView;
+
+import com.example.acloc.R;
+import com.example.acloc.activities.LoginActivity;
+
+public class DialogUtils {
+ private static Dialog dialog;
+
+ public static void showLoadingDialog(Context context, String message) {
+ if (dialog != null && dialog.isShowing()) {
+ return;
+ }
+
+ dialog = new Dialog(context);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.setContentView(R.layout.loading);
+ dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ dialog.setCancelable(false);
+
+ TextView tvLoadingText = dialog.findViewById(R.id.tvLoadingText);
+ tvLoadingText.setText(message);
+
+ dialog.show();
+ }
+
+ public static void dismissDialog() {
+ if (dialog != null && dialog.isShowing()) {
+ dialog.dismiss();
+ dialog = null;
+ }
+ }
+
+ public static AlertDialog logoutDialog(final Context context) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+
+ builder.setMessage(context.getString(R.string.Are_you_sure_you_want_to_logout));
+
+ builder.setPositiveButton("Yes", (dialog, id) -> {
+ dialog.cancel();
+ int flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK;
+ SharedPref.setIsLoggedIn(context,false);
+ SharedPref.deleteAll(context);
+ Helper.goToWithFlags(context, LoginActivity.class, flags);
+ });
+
+ builder.setNegativeButton("No", (dialog, id) -> dialog.cancel());
+ return builder.create();
+ }
+
+// public static AlertDialog confirmationDialog(final Context context, String confirmationText
+// , DialogInterface.OnClickListener onDeleteClickListener) {
+// AlertDialog.Builder builder = new AlertDialog.Builder(context);
+// if (confirmationText == null) confirmationText = "perform this operation";
+// builder.setMessage("Are you sure you want to " + confirmationText + "?" +
+// "\nWARNING: This action cannot be undone");
+// builder.setPositiveButton("Yes", onDeleteClickListener);
+// builder.setNegativeButton("No", (dialog, id) -> dialog.cancel());
+// return builder.create();
+// }
+
+ public static AlertDialog confirmationDialog(final Context context, String confirmationText
+ , DialogInterface.OnClickListener onDeleteClickListener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ if (confirmationText == null) confirmationText = "perform this operation";
+ String message = context.getString(R.string.confirmation_message, confirmationText);
+ builder.setMessage(message);
+ builder.setPositiveButton(context.getString(R.string.yes), onDeleteClickListener);
+ builder.setNegativeButton(context.getString(R.string.no), (dialog, id) -> dialog.cancel());
+ return builder.create();
+ }
+
+
+}
diff --git a/app/src/main/java/com/example/acloc/utility/Helper.java b/app/src/main/java/com/example/acloc/utility/Helper.java
new file mode 100644
index 0000000..8d81066
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/utility/Helper.java
@@ -0,0 +1,353 @@
+package com.example.acloc.utility;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.Toast;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.google.android.material.snackbar.Snackbar;
+import com.google.android.material.textfield.MaterialAutoCompleteTextView;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.android.material.textfield.TextInputLayout;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Objects;
+
+public class Helper {
+
+ private static final String TAG = Helper.class.getSimpleName();
+
+ public static void showToast(Context context, String message) {
+ try {
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ } catch (Exception ignored) {
+ }
+ }
+
+ public static void makeSnackBar(View view, String message) {
+ try {
+ Snackbar.make(view, message, Snackbar.LENGTH_LONG).show();
+ } catch (Exception ignored) {
+ showToast(view.getContext(), message);
+ }
+ }
+
+ public static void goTo(Context context, Class> activity) {
+ Intent intent = new Intent(context, activity);
+ context.startActivity(intent);
+ }
+
+ public static void goToAndFinish(Context context, Class> activity) {
+ Intent intent = new Intent(context, activity);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ context.startActivity(intent);
+
+ // No need to finish explicitly here, flags handle it
+ if (context instanceof Activity) {
+ ((Activity) context).finish();
+ }
+ }
+
+ public static void goTo(Context context, Class> activity, String key, Serializable object) {
+ Intent intent = new Intent(context, activity);
+ intent.putExtra(key, object);
+ context.startActivity(intent);
+
+// if (context instanceof Activity) {
+// ((Activity) context).finish();
+// }
+ }
+
+ public static int getIntValueFromString(String value) {
+ try {
+ return Integer.parseInt(value);
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ public static double getDoubleValueFromString(String value) {
+ try {
+ return Double.parseDouble(value);
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ public static String getStringFromInput(View view) {
+ try {
+ if (view instanceof TextInputEditText) {
+ TextInputEditText editText = (TextInputEditText) view;
+ return Objects.requireNonNull(editText.getText()).toString().trim();
+ } else if (view instanceof MaterialAutoCompleteTextView) {
+ MaterialAutoCompleteTextView editText = (MaterialAutoCompleteTextView) view;
+ return Objects.requireNonNull(editText.getText()).toString();
+ }
+ } catch (Exception e) {
+ return null;
+ }
+ return null;
+ }
+
+ public static boolean isEmptyFieldValidation(TextInputEditText editText) {
+ boolean isValidate = true;
+ try {
+ TextInputLayout textInputLayout = null;
+ ViewParent parent = editText.getParent().getParent();
+ if (parent instanceof TextInputLayout) {
+ textInputLayout = (TextInputLayout) parent;
+ }
+ if (Objects.requireNonNull(editText.getText()).toString().trim().isEmpty()) {
+ if (textInputLayout != null) {
+ textInputLayout.isHelperTextEnabled();
+ textInputLayout.setError("Please " + textInputLayout.getHint());
+ textInputLayout.setErrorEnabled(true);
+ } else {
+ editText.setError("Empty");
+ }
+ isValidate = false;
+ } else {
+ if (textInputLayout != null) {
+ textInputLayout.setErrorEnabled(false);
+ } else {
+ editText.setError(null);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in Helper Class: ", e);
+ isValidate = false;
+ }
+ return isValidate;
+ }
+
+ public static boolean isEmptyFieldValidation(View[] inputFields) {
+ boolean isValidate = true;
+ try {
+ for (View view : inputFields) {
+ TextInputLayout textInputLayout = null;
+ ViewParent parent = view.getParent().getParent();
+ if (parent instanceof TextInputLayout) {
+ textInputLayout = (TextInputLayout) parent;
+ }
+
+ String inputText = "";
+ if (view instanceof TextInputEditText) {
+ inputText = Objects.requireNonNull(((TextInputEditText) view).getText()).toString().trim();
+ } else if (view instanceof MaterialAutoCompleteTextView) {
+ inputText = Objects.requireNonNull(((MaterialAutoCompleteTextView) view).getText()).toString().trim();
+ }
+
+ if (inputText.isEmpty()) {
+ if (textInputLayout != null) {
+ textInputLayout.setError("Please " + textInputLayout.getHint());
+ textInputLayout.setErrorEnabled(true);
+ } else {
+ if (view instanceof TextInputEditText) {
+ ((TextInputEditText) view).setError("Empty");
+ } else if (view instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) view).setError("Empty");
+ }
+ }
+ isValidate = false;
+ } else {
+ if (textInputLayout != null) {
+ textInputLayout.setErrorEnabled(false);
+ } else {
+ if (view instanceof TextInputEditText) {
+ ((TextInputEditText) view).setError(null);
+ } else if (view instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) view).setError(null);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in validation: ", e);
+ isValidate = false;
+ }
+ return isValidate;
+ }
+
+ public static boolean isEmailValid(View emailView) {
+ boolean isValidate = true;
+ try {
+ TextInputLayout textInputLayout = null;
+ ViewParent parent = emailView.getParent().getParent();
+ if (parent instanceof TextInputLayout) {
+ textInputLayout = (TextInputLayout) parent;
+ }
+
+ String emailText = "";
+ if (emailView instanceof TextInputEditText) {
+ emailText = Objects.requireNonNull(((TextInputEditText) emailView).getText()).toString().trim();
+ } else if (emailView instanceof MaterialAutoCompleteTextView) {
+ emailText = Objects.requireNonNull(((MaterialAutoCompleteTextView) emailView).getText()).toString().trim();
+ }
+
+ // Regex for email validation
+ if (!emailText.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$")) {
+ if (textInputLayout != null) {
+ textInputLayout.setError("Invalid Email Address");
+ textInputLayout.setErrorEnabled(true);
+ } else {
+ if (emailView instanceof TextInputEditText) {
+ ((TextInputEditText) emailView).setError("Invalid Email Address");
+ } else if (emailView instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) emailView).setError("Invalid Email Address");
+ }
+ }
+ isValidate = false;
+ } else {
+ if (textInputLayout != null) {
+ textInputLayout.setErrorEnabled(false);
+ } else {
+ if (emailView instanceof TextInputEditText) {
+ ((TextInputEditText) emailView).setError(null);
+ } else if (emailView instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) emailView).setError(null);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in email validation: ", e);
+ isValidate = false;
+ }
+ return isValidate;
+ }
+
+ public static boolean isPasswordValid(View passwordView) {
+ boolean isValidate = true;
+ try {
+ TextInputLayout textInputLayout = null;
+ ViewParent parent = passwordView.getParent().getParent();
+ if (parent instanceof TextInputLayout) {
+ textInputLayout = (TextInputLayout) parent;
+ }
+
+ String passwordText = "";
+ if (passwordView instanceof TextInputEditText) {
+ passwordText = Objects.requireNonNull(((TextInputEditText) passwordView).getText()).toString().trim();
+ } else if (passwordView instanceof MaterialAutoCompleteTextView) {
+ passwordText = Objects.requireNonNull(((MaterialAutoCompleteTextView) passwordView).getText()).toString().trim();
+ }
+
+ // Regex for strong password validation
+ String passwordPattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{6,}$";
+
+ if (!passwordText.matches(passwordPattern)) {
+ if (textInputLayout != null) {
+ textInputLayout.setError("Password must be at least 6 characters, include 1 uppercase, 1 lowercase, 1 digit, and 1 special character.");
+ textInputLayout.setErrorEnabled(true);
+ } else {
+ if (passwordView instanceof TextInputEditText) {
+ ((TextInputEditText) passwordView).setError("Password must be at least 6 characters, include 1 uppercase, 1 lowercase, 1 digit, and 1 special character.");
+ } else if (passwordView instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) passwordView).setError("Password must be at least 6 characters, include 1 uppercase, 1 lowercase, 1 digit, and 1 special character.");
+ }
+ }
+ isValidate = false;
+ } else {
+ if (textInputLayout != null) {
+ textInputLayout.setErrorEnabled(false);
+ } else {
+ if (passwordView instanceof TextInputEditText) {
+ ((TextInputEditText) passwordView).setError(null);
+ } else if (passwordView instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) passwordView).setError(null);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e("Helper", "Error in password validation: ", e);
+ isValidate = false;
+ }
+ return isValidate;
+ }
+
+ public static boolean isContactValid(View contactView) {
+ boolean isValidate = true;
+ try {
+ TextInputLayout textInputLayout = null;
+ ViewParent parent = contactView.getParent().getParent();
+ if (parent instanceof TextInputLayout) {
+ textInputLayout = (TextInputLayout) parent;
+ }
+
+ String contactText = "";
+ if (contactView instanceof TextInputEditText) {
+ contactText = Objects.requireNonNull(((TextInputEditText) contactView).getText()).toString().trim();
+ } else if (contactView instanceof MaterialAutoCompleteTextView) {
+ contactText = Objects.requireNonNull(((MaterialAutoCompleteTextView) contactView).getText()).toString().trim();
+ }
+
+ // Regex for validating contact (Assuming 10-digit phone number)
+ if (!contactText.matches("^[0-9]{10}$")) {
+ if (textInputLayout != null) {
+ textInputLayout.setError("Invalid Contact Number");
+ textInputLayout.setErrorEnabled(true);
+ } else {
+ if (contactView instanceof TextInputEditText) {
+ ((TextInputEditText) contactView).setError("Invalid Contact Number");
+ } else if (contactView instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) contactView).setError("Invalid Contact Number");
+ }
+ }
+ isValidate = false;
+ } else {
+ if (textInputLayout != null) {
+ textInputLayout.setErrorEnabled(false);
+ } else {
+ if (contactView instanceof TextInputEditText) {
+ ((TextInputEditText) contactView).setError(null);
+ } else if (contactView instanceof MaterialAutoCompleteTextView) {
+ ((MaterialAutoCompleteTextView) contactView).setError(null);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e("ValidationHelper", "Error in contact validation: ", e);
+ isValidate = false;
+ }
+ return isValidate;
+ }
+
+ public static String getCurrentDate() {
+ try {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
+ Date date = new Date();
+ return dateFormat.format(date);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in getting current date: ", e);
+ return null;
+ }
+ }
+
+ public static String getCurrentTime() {
+ try {
+ SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
+ Date date = new Date();
+ return timeFormat.format(date);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in getting current time: ", e);
+ return null;
+ }
+ }
+
+ public static void goToWithFlags(Context context, Class> activity, int flags) {
+ Intent intent = new Intent(context, activity);
+ intent.setFlags(flags);
+ context.startActivity(intent);
+ }
+
+ public static LinearLayoutManager getVerticalManager(Context context) {
+ return new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
+ }
+}
+
diff --git a/app/src/main/java/com/example/acloc/utility/KeyGeneratorUtils.java b/app/src/main/java/com/example/acloc/utility/KeyGeneratorUtils.java
new file mode 100644
index 0000000..28b1b6d
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/utility/KeyGeneratorUtils.java
@@ -0,0 +1,32 @@
+package com.example.acloc.utility;
+
+import java.security.SecureRandom;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class KeyGeneratorUtils {
+
+ private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ private static final int RANDOM_STRING_LENGTH = 8;
+ private static final SecureRandom random = new SecureRandom();
+
+ /**
+ * Generates a unique key using timestamp + random string
+ * @return Unique string key
+ */
+ public static String uniqueKeyGenerator() {
+ // Get current timestamp in yyyyMMddHHmmssSSS format
+ String timeStamp = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault()).format(new Date());
+
+ // Generate random alphanumeric string
+ StringBuilder randomStr = new StringBuilder(RANDOM_STRING_LENGTH);
+ for (int i = 0; i < RANDOM_STRING_LENGTH; i++) {
+ int index = random.nextInt(CHARACTERS.length());
+ randomStr.append(CHARACTERS.charAt(index));
+ }
+
+ // Combine timestamp and random string
+ return timeStamp + "_" + randomStr.toString();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/acloc/utility/SharedPref.java b/app/src/main/java/com/example/acloc/utility/SharedPref.java
new file mode 100644
index 0000000..7cf1ecc
--- /dev/null
+++ b/app/src/main/java/com/example/acloc/utility/SharedPref.java
@@ -0,0 +1,173 @@
+package com.example.acloc.utility;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Base64;
+import android.util.Log;
+
+import org.json.JSONObject;
+
+import java.util.Locale;
+
+public class SharedPref {
+ public static final String TAG = "SharedPref";
+
+ private static final String IS_LOGGED_IN = "isLoggedIn";
+ private static final String USER_UUID = "userUuid"; // Key for UID storage
+ private static final String ADMIN_ROLE_UUID = "adminRoleUuid";
+ private static final String VIEWER_ROLE_UUID = "viewerRoleUuid";
+ private static final String USER_NAME = "userName";
+ private static final String USER_EMAIL = "userEmail";
+ private static final String ROLE = "role";
+ private static final String ACCESS_TOKEN = "AccessToken";
+ private static final String ACCESS_TOKEN_EXPIRY = "AccessTokenExpiry";
+ private static final String REFRESH_TOKEN = "RefreshToken";
+ private static final String LANGUAGE_KEY = "language_key"; // Key to store selected language
+
+ public static SharedPreferences sharedPreferences(Context con) {
+ return con.getSharedPreferences(Constants.SHARED_PREF, Context.MODE_PRIVATE);
+ }
+
+ public static void setIsLoggedIn(Context con, boolean value) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putBoolean(IS_LOGGED_IN, value);
+ editor.apply();
+ }
+
+ public static boolean getIsLoggedIn(Context con) {
+ return sharedPreferences(con).getBoolean(IS_LOGGED_IN, false);
+ }
+
+ public static void setUuid(Context con, String uid) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(USER_UUID, uid);
+ editor.apply();
+ }
+
+ public static String getUserUid(Context con) {
+ return sharedPreferences(con).getString(USER_UUID, "");
+ }
+
+ public static void setUsername(Context con, String username) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(USER_NAME, username);
+ editor.apply();
+ }
+
+ public static String getUserName(Context con) {
+ return sharedPreferences(con).getString(USER_NAME, "");
+ }
+
+ public static void setUserEmail(Context con, String userEmail) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(USER_EMAIL, userEmail);
+ editor.apply();
+ }
+
+ public static String getUserEmail(Context con) {
+ return sharedPreferences(con).getString(USER_EMAIL, "");
+ }
+
+ public static void setRole(Context con, String userEmail) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(ROLE, userEmail);
+ editor.apply();
+ }
+
+ public static String getRole(Context con) {
+ return sharedPreferences(con).getString(ROLE, "");
+ }
+
+ public static void setAccessToken(Context con, String token) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(ACCESS_TOKEN, token);
+
+ // Decode token to get expiry (exp) field
+ try {
+ if (token != null && !token.trim().isEmpty()) {
+ String[] split = token.split("\\.");
+ if (split.length >= 2) {
+ String payload = new String(Base64.decode(split[1], Base64.DEFAULT));
+ JSONObject jsonObject = new JSONObject(payload);
+
+ if (jsonObject.has("exp")) {
+ long exp = jsonObject.getLong("exp"); // in seconds
+ long expiryMillis = exp * 1000; // convert to milliseconds
+ editor.putLong(ACCESS_TOKEN_EXPIRY, expiryMillis);
+ } else {
+ editor.putLong(ACCESS_TOKEN_EXPIRY, 0);
+ }
+ } else {
+ editor.putLong(ACCESS_TOKEN_EXPIRY, 0);
+ }
+ } else {
+ editor.putLong(ACCESS_TOKEN_EXPIRY, 0);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to decode token", e);
+ editor.putLong(ACCESS_TOKEN_EXPIRY, 0); // fallback
+ }
+
+ editor.apply();
+ }
+
+
+ public static String getAccessToken(Context con) {
+ return sharedPreferences(con).getString(ACCESS_TOKEN, "");
+ }
+
+ public static boolean isAccessTokenValid(Context con) {
+ long expiryTime = sharedPreferences(con).getLong(ACCESS_TOKEN_EXPIRY, 0);
+ long currentTime = System.currentTimeMillis();
+ return currentTime < expiryTime;
+ }
+
+ public static void setRefreshToken(Context con, String refreshToken) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(REFRESH_TOKEN, refreshToken);
+ editor.apply();
+ }
+
+ public static String getRefreshToken(Context con) {
+ return sharedPreferences(con).getString(REFRESH_TOKEN, "");
+ }
+
+ public static void deleteAll(Context context) {
+ SharedPreferences.Editor editor = sharedPreferences(context).edit();
+ editor.clear();
+ editor.apply();
+ }
+
+ // Method to save selected language
+ public static void setLanguage(Context context, String language) {
+ SharedPreferences.Editor editor = sharedPreferences(context).edit();
+ editor.putString(LANGUAGE_KEY, language);
+ editor.apply();
+ }
+
+ // Method to retrieve saved language, default to system language if not set
+ public static String getLanguage(Context context) {
+ return sharedPreferences(context).getString(LANGUAGE_KEY, Locale.getDefault().getLanguage());
+ }
+
+
+ public static void setViewerRoleUuid(Context con, String uid) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(VIEWER_ROLE_UUID, uid);
+ editor.apply();
+ }
+
+ public static String getViewerRoleUuid(Context con) {
+ return sharedPreferences(con).getString(VIEWER_ROLE_UUID, "");
+ }
+
+ public static void setAdminRoleUuid(Context con, String uid) {
+ SharedPreferences.Editor editor = sharedPreferences(con).edit();
+ editor.putString(ADMIN_ROLE_UUID, uid);
+ editor.apply();
+ }
+
+ public static String getAdminRoleUuid(Context con) {
+ return sharedPreferences(con).getString(ADMIN_ROLE_UUID, "");
+ }
+}
diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml
new file mode 100644
index 0000000..9f83b8f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_add_light.xml b/app/src/main/res/drawable/ic_add_light.xml
new file mode 100644
index 0000000..2ae27b8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_light.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_add_location.xml b/app/src/main/res/drawable/ic_add_location.xml
new file mode 100644
index 0000000..9e099e9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_location.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml
new file mode 100644
index 0000000..1b10afe
--- /dev/null
+++ b/app/src/main/res/drawable/ic_delete.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dropdown.xml b/app/src/main/res/drawable/ic_dropdown.xml
new file mode 100644
index 0000000..a77e331
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dropdown.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml
new file mode 100644
index 0000000..9fc6aac
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_edit_location.xml b/app/src/main/res/drawable/ic_edit_location.xml
new file mode 100644
index 0000000..623bc14
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit_location.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_email.xml b/app/src/main/res/drawable/ic_email.xml
new file mode 100644
index 0000000..a3335d4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_email.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_favorite.xml b/app/src/main/res/drawable/ic_favorite.xml
new file mode 100644
index 0000000..ce3be0b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_favorite.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_favorite_border.xml b/app/src/main/res/drawable/ic_favorite_border.xml
new file mode 100644
index 0000000..b2d6f0c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_favorite_border.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml
new file mode 100644
index 0000000..20cb4d6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml
new file mode 100644
index 0000000..643d3fc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_language.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..ca3826a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml
new file mode 100644
index 0000000..7eb4ae9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml
new file mode 100644
index 0000000..4edadb0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_lock.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_logout.xml b/app/src/main/res/drawable/ic_logout.xml
new file mode 100644
index 0000000..c22a96f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_logout.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_manage_roles.xml b/app/src/main/res/drawable/ic_manage_roles.xml
new file mode 100644
index 0000000..3a2e3ab
--- /dev/null
+++ b/app/src/main/res/drawable/ic_manage_roles.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_map.xml b/app/src/main/res/drawable/ic_map.xml
new file mode 100644
index 0000000..58f2947
--- /dev/null
+++ b/app/src/main/res/drawable/ic_map.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_person.xml b/app/src/main/res/drawable/ic_person.xml
new file mode 100644
index 0000000..ddc8322
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_phone.xml b/app/src/main/res/drawable/ic_phone.xml
new file mode 100644
index 0000000..2862a96
--- /dev/null
+++ b/app/src/main/res/drawable/ic_phone.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_report.xml b/app/src/main/res/drawable/ic_report.xml
new file mode 100644
index 0000000..b6a6b48
--- /dev/null
+++ b/app/src/main/res/drawable/ic_report.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml
new file mode 100644
index 0000000..d29c6ea
--- /dev/null
+++ b/app/src/main/res/drawable/ic_search.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumb_down.xml b/app/src/main/res/drawable/ic_thumb_down.xml
new file mode 100644
index 0000000..0621911
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_down.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumb_down_border.xml b/app/src/main/res/drawable/ic_thumb_down_border.xml
new file mode 100644
index 0000000..6a26286
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_down_border.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumb_up.xml b/app/src/main/res/drawable/ic_thumb_up.xml
new file mode 100644
index 0000000..faa4659
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_up.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumb_up_average.xml b/app/src/main/res/drawable/ic_thumb_up_average.xml
new file mode 100644
index 0000000..ccb79a7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_up_average.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumb_up_border.xml b/app/src/main/res/drawable/ic_thumb_up_border.xml
new file mode 100644
index 0000000..e57a113
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_up_border.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumbs_down.xml b/app/src/main/res/drawable/ic_thumbs_down.xml
new file mode 100644
index 0000000..cea7fa0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumbs_down.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumbs_up.xml b/app/src/main/res/drawable/ic_thumbs_up.xml
new file mode 100644
index 0000000..cd531c6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumbs_up.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_add_location.png b/app/src/main/res/drawable/logo_add_location.png
new file mode 100644
index 0000000..9925dd1
Binary files /dev/null and b/app/src/main/res/drawable/logo_add_location.png differ
diff --git a/app/src/main/res/drawable/logo_location.png b/app/src/main/res/drawable/logo_location.png
new file mode 100644
index 0000000..f06e037
Binary files /dev/null and b/app/src/main/res/drawable/logo_location.png differ
diff --git a/app/src/main/res/drawable/place_header.jpg b/app/src/main/res/drawable/place_header.jpg
new file mode 100644
index 0000000..2effa42
Binary files /dev/null and b/app/src/main/res/drawable/place_header.jpg differ
diff --git a/app/src/main/res/drawable/rating.xml b/app/src/main/res/drawable/rating.xml
new file mode 100644
index 0000000..1482ff0
--- /dev/null
+++ b/app/src/main/res/drawable/rating.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/rectangle.xml b/app/src/main/res/drawable/rectangle.xml
new file mode 100644
index 0000000..36e5733
--- /dev/null
+++ b/app/src/main/res/drawable/rectangle.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_add_new_place.xml b/app/src/main/res/layout/activity_add_new_place.xml
new file mode 100644
index 0000000..2d33566
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_new_place.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_add_report.xml b/app/src/main/res/layout/activity_add_report.xml
new file mode 100644
index 0000000..105944a
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_report.xml
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 0000000..c654d61
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..5cb9fa6
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_manage_roles.xml b/app/src/main/res/layout/activity_manage_roles.xml
new file mode 100644
index 0000000..436f08d
--- /dev/null
+++ b/app/src/main/res/layout/activity_manage_roles.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_place_detail.xml b/app/src/main/res/layout/activity_place_detail.xml
new file mode 100644
index 0000000..809c04d
--- /dev/null
+++ b/app/src/main/res/layout/activity_place_detail.xml
@@ -0,0 +1,234 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_register.xml b/app/src/main/res/layout/activity_register.xml
new file mode 100644
index 0000000..dc50f7f
--- /dev/null
+++ b/app/src/main/res/layout/activity_register.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/alert_dialog_add_new_place.xml b/app/src/main/res/layout/alert_dialog_add_new_place.xml
new file mode 100644
index 0000000..c58a688
--- /dev/null
+++ b/app/src/main/res/layout/alert_dialog_add_new_place.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/alert_dialog_change_password.xml b/app/src/main/res/layout/alert_dialog_change_password.xml
new file mode 100644
index 0000000..98fa1d6
--- /dev/null
+++ b/app/src/main/res/layout/alert_dialog_change_password.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/alert_dialog_profile.xml b/app/src/main/res/layout/alert_dialog_profile.xml
new file mode 100644
index 0000000..aa8a405
--- /dev/null
+++ b/app/src/main/res/layout/alert_dialog_profile.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_favorite.xml b/app/src/main/res/layout/fragment_favorite.xml
new file mode 100644
index 0000000..ec2bec1
--- /dev/null
+++ b/app/src/main/res/layout/fragment_favorite.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_map.xml b/app/src/main/res/layout/fragment_map.xml
new file mode 100644
index 0000000..71f6f57
--- /dev/null
+++ b/app/src/main/res/layout/fragment_map.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_my_reports.xml b/app/src/main/res/layout/fragment_my_reports.xml
new file mode 100644
index 0000000..80751de
--- /dev/null
+++ b/app/src/main/res/layout/fragment_my_reports.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_view_favorite.xml b/app/src/main/res/layout/list_view_favorite.xml
new file mode 100644
index 0000000..0688a48
--- /dev/null
+++ b/app/src/main/res/layout/list_view_favorite.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/list_view_my_report.xml b/app/src/main/res/layout/list_view_my_report.xml
new file mode 100644
index 0000000..4a60f70
--- /dev/null
+++ b/app/src/main/res/layout/list_view_my_report.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/list_view_place_reports.xml b/app/src/main/res/layout/list_view_place_reports.xml
new file mode 100644
index 0000000..589a0e5
--- /dev/null
+++ b/app/src/main/res/layout/list_view_place_reports.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/list_view_users.xml b/app/src/main/res/layout/list_view_users.xml
new file mode 100644
index 0000000..be81b06
--- /dev/null
+++ b/app/src/main/res/layout/list_view_users.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/loading.xml b/app/src/main/res/layout/loading.xml
new file mode 100644
index 0000000..89f2af8
--- /dev/null
+++ b/app/src/main/res/layout/loading.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml
new file mode 100644
index 0000000..f2516df
--- /dev/null
+++ b/app/src/main/res/layout/toolbar.xml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_bottom_navigation.xml b/app/src/main/res/menu/menu_bottom_navigation.xml
new file mode 100644
index 0000000..608d269
--- /dev/null
+++ b/app/src/main/res/menu/menu_bottom_navigation.xml
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_dashboard.xml b/app/src/main/res/menu/menu_dashboard.xml
new file mode 100644
index 0000000..a33bc4a
--- /dev/null
+++ b/app/src/main/res/menu/menu_dashboard.xml
@@ -0,0 +1,35 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..b580b49
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..f140925
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..f2ba4d8
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..6936ee9
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..a56ba49
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..aa1b049
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..19404d0
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..c7646e1
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..42565f3
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..e2be485
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..5304018
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..757535e
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..d6c9573
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..aebc5cb
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..5ef4b8b
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..654a645
--- /dev/null
+++ b/app/src/main/res/values-es/strings.xml
@@ -0,0 +1,155 @@
+
+ AcLoc - Aplicación de Ubicaciones Accesibles
+
+
+ INICIAR SESIÓN
+ REGISTRARSE
+ GUARDAR LUGAR
+ ACTUALIZAR
+ CANCELAR
+ ENTREGAR
+ AGREGAR INFORME
+
+
+ Agregar Informe
+ Detalles del Lugar
+ Agregar Lugar
+
+
+ Introduzca nombre de usuario
+ Introducir contraseña
+ Introducir correo electrónico
+ Introducir nombre
+ Introduzca ubicación
+ Ingrese el contacto
+ ¿Ya tienes una cuenta?
+ Por favor, inicie sesión
+ Por favor, regístrese
+ ¿No tienes una cuenta?
+ Mi Informe
+ Mapa
+ Favorito
+ Perfil
+ Cambiar contraseña
+ Gestionar roles
+ Cambiar idioma
+ Cerrar sesión
+ Ingrese el nombre del lugar
+ Ingrese latitud
+ Ingrese longitud
+ Ingrese dirección
+ Ingrese la descripción del lugar
+ Buscar lugar
+ Ingrese contraseña anterior
+ Ingrese nueva contraseña
+ Ingrese descripción
+ Nombre del Lugar
+ Dirección
+ Descripción del Lugar
+ Descripción del Informe
+ Informe
+ Buscar Usuario
+ Usuario
+ Rol
+ Cambiar Rol
+
+
+ BUENO
+ REGULAR
+ MALO
+
+
+ cambiar el idioma a inglés
+ ¿Estás seguro de que quieres cerrar sesión?
+ ¿Estás seguro de que deseas %1$s?\nADVERTENCIA: Esta acción no se puede deshacer
+ Sí
+ No
+ Se requiere permiso de ubicación
+ Esta aplicación requiere permiso de ubicación para funcionar correctamente. Por favor, habilítalo en Configuración.
+ Ir a Configuración
+ Salir de la aplicación
+
+
+ ¡Algo salió mal!
+ Error de inicio de sesión
+ Credenciales inválidas. Por favor, inténtalo de nuevo.
+ Inicio de sesión exitoso
+ Por favor espera...
+ El usuario ya existe. Por favor, inicia sesión.
+ ¡Perfil actualizado con éxito!
+ Registro exitoso
+ Intenta de nuevo más tarde
+ Actualización fallida
+ Algo salió mal. Intenta de nuevo.
+ Actualizando...
+ Contraseña anterior inválida
+ Error de red. Intenta de nuevo.
+ Cambiando la contraseña...
+ ¡Contraseña actualizada con éxito!
+ Falló la actualización de la contraseña.
+ Verificando la contraseña anterior...
+ Cambiar Rol a
+ Actualizando Roles...
+ ¡Rol de usuario actualizado con éxito!
+ Actualización fallida. Error del servidor. Intenta de nuevo.
+ Calificación: MALA
+ Calificación: REGULAR
+ Calificación: BUENA
+ Eliminar informe
+ Eliminando informe...
+ ¡Informe eliminado!
+ No se pudo eliminar el informe
+ Eliminando de favoritos...
+ Favorito eliminado
+ No se pudo eliminar el favorito
+ Cargando favoritos...
+ No se encontraron favoritos.
+ No se pudieron cargar los favoritos. Intenta de nuevo.
+ Se requiere permiso de ubicación
+ No se pudieron cargar los lugares
+ Cargando informes...
+ No se encontraron informes.
+ No se pudieron cargar los informes. Intenta de nuevo.
+ ¡Lugar actualizado con éxito!
+ Actualizando lugar...
+ ¡Lugar agregado con éxito!
+ No se pudo obtener el lugar. Intenta de nuevo
+ Error al insertar. Error del servidor.
+ Error al enviar el informe. Intenta de nuevo
+ ¡Informe enviado con éxito!
+ ¡Por favor selecciona una calificación!
+ Actualizando informe...
+ ¡Informe actualizado con éxito!
+ No se pudieron cargar los usuarios. Intenta de nuevo.
+ No se encontraron usuarios
+ Cargando usuarios...
+ Agregando a favoritos...
+ ¡Lugar agregado a favoritos!
+ No se pudo agregar a favoritos. Error del servidor.
+ Restaurando favorito...
+ ¡Lugar restaurado a favoritos!
+ No se pudo restaurar el favorito.
+ Eliminado de favoritos
+ No se pudo eliminar de favoritos
+ Verificando estado de favorito...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AIzaSyCzcuZhHd-GqadewAiCbh4NuaBhO2AcPOY
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..678135f
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3b639ea
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,59 @@
+
+
+ #FF000000
+ #FFFFFFFF
+
+
+ #0D47A1
+ #4D8EF4
+
+
+ #42A5F5
+ #5AB1F6
+
+
+ #2E7D32
+ #FFA000
+ #C62828
+
+
+ #FAFAFA
+ #121212
+
+
+ #FFFFFF
+ #040404
+
+
+ #FFFFFF
+ #FFFFFF
+
+ #000000
+ #FFFFFF
+
+ #212121
+
+ #E0E0E0
+
+ #212121
+ #F5F5F5
+
+
+ #C62828
+ #F44336
+
+
+ #FF4081
+ #C51162
+
+ #e8edf6
+ #232324
+
+
+ #919395
+ #4CAF50
+ #FFC107
+ #F44336
+ #e8edf6
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..7766af4
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,51 @@
+
+
+ 16dp
+ 8dp
+ 6dp
+ 5dp
+ 14dp
+ 60dp
+ 4dp
+ 30dp
+ 10sp
+ 10sp
+ 15sp
+ 16sp
+ 18sp
+ 25sp
+ 50sp
+ 20sp
+ 10dp
+ 10dp
+ 20dp
+ 2dp
+ 8dp
+ 85dp
+
+
+
+ 25dp
+ 25dp
+ 50dp
+ 50dp
+
+ 100dp
+ 100dp
+
+ 150dp
+ 150dp
+
+ 70dp
+ 70dp
+
+ 40dp
+ 8dp
+
+ 16dp
+ 16dp
+ 16dp
+ 16dp
+ 8dp
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..c5d5899
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8baf61e
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,158 @@
+
+ AcLoc - Accessible Locations Application
+
+
+ LOGIN
+ REGISTER
+ SAVE PLACE
+ UPDATE
+ CANCEL
+ SUBMIT
+ ADD REPORT
+
+
+ Add Report
+ Place Details
+ Add Place
+
+
+
+ Enter Username
+ Enter Password
+ Enter Email
+ Enter Name
+ Enter Location
+ Enter Contact
+ "Already have an Account?
+ Please login
+ Please register
+ Don\'t have an Account?
+ Favorite
+ My Report
+ Map
+ Profile
+ Change Password
+ Manage Roles
+ Change Language
+ Logout
+ Enter Place Name
+ Enter Latitude
+ Enter Longitude
+ Enter Address
+ Enter Place Description
+ Search Place
+ Enter Old Password
+ Enter New Password
+ Place Name
+ Address
+ Place Description
+ Enter Description
+ Report Description
+ Report
+ Search User
+ User
+ Role
+ Change Role
+
+
+ GOOD
+ AVERAGE
+ BAD
+
+
+ change language to Spanish
+ Are you sure you want to logout?
+ Are you sure you want to %1$s?\nWARNING: This action cannot be undone
+ Yes
+ No
+ Location permission required
+ This app requires location permission to function properly. Please enable it in Settings.
+ Go to Settings
+ Exit App
+
+
+
+ Something went wrong!!!
+ Login failed
+ Invalid Credentials. Please try again.
+ Login Successful
+ Please wait...
+ "User already exists. Please login.
+ Profile Updated Successfully!
+ Registration Successful
+ Try again later
+ Update Failed
+ Something went wrong. Try again.
+ Updating...
+ Invalid old password
+ Network error. Try again.
+ Changing password...
+ Password updated successfully!
+ Password update failed.
+ Verifying old password...
+ change Role to
+ Updating Roles...
+ User Role updated successfully!
+ Update failed. Server error. Try again
+ Rating: BAD
+ Rating: AVERAGE
+ Rating: GOOD
+ Delete report
+ Removing Report...
+ Report removed!
+ Failed to remove report
+ Removing from favorites...
+ Favorite removed
+ Failed to remove Favorite
+ Loading favorites...
+ No Favorite found.
+ Failed to load favorite. Try again.
+ Location permission is required
+ Failed to load places
+ Loading reports...
+ No reports found.
+ Failed to load reports. Try again.
+ Place updated successfully!
+ Updating place...
+ Place inserted successfully!
+ Failed to extract place.Try again
+ Insert failed. Server error.
+ Report submission failed. Try again
+ Report submitted successfully!
+ Please select rating!
+ Updating report...
+ Report updated successfully!
+ Failed to load users. Try again.
+ No users found
+ Loading users...
+ Adding to favorites...
+ Place added to favorites!
+ Failed to add favorite. Server error.
+ Restoring favorite...
+ Place restored to favorites!
+ Failed to restore favorite.
+ Removed from favorites
+ Failed to remove from favorites
+ Checking favorite status...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AIzaSyCzcuZhHd-GqadewAiCbh4NuaBhO2AcPOY
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..ed444ad
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file