First working version of the app.

This commit is contained in:
Pau 2025-05-12 21:18:26 +02:00
parent 3c105efe7b
commit 6012256a17
112 changed files with 7690 additions and 0 deletions

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AcLoc"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".activity.ManageRolesActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".activity.AddNewPlaceActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".activity.PlaceDetailActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".activity.AddReportActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".activity.LoginActivity"
android:exported="true"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.RegisterActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".MainActivity"
android:exported="false"
android:screenOrientation="portrait" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_api_key" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -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<JsonObject> call = apiService.getRoles(token);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Get Roles Failure: ", t);
// Helper.makeSnackBar(rlMainActivity, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<JsonObject> call = apiService.insertPlace(token, placeBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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<JsonObject> call = apiService.updatePlace(token, uuid, placeBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Update Place Error: ", t);
Helper.makeSnackBar(rlAddPlace, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<JsonObject> call = apiService.insertReport(token, reportBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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<JsonObject> call = apiService.updateReport(token, uuid, reportBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Update Place Error: ", t);
Helper.makeSnackBar(rlAddReport, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<JsonObject> call = apiService.loginUser(jsonParam);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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");
}
});
}
}

View File

@ -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<User> 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<User> 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<User> 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<JsonObject> call = apiService.getAllUsers(token);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Get users failure: ", t);
Helper.makeSnackBar(rlManageRoles, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<Report> 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<Report> 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<JsonObject> call = apiService.addPlaceToFavorites(token, userUuid, body);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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<JsonObject> call = apiService.restorePlaceToFavorites(token, userUuid, body);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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<Void> call = apiService.removePlaceFromFavorites(token, userUuid, placeUuid);
call.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> 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<Void> 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<JsonObject> call = apiService.getFavoritePlaces(token, userUuid);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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<JsonObject> call = apiService.getPlaceReports(token, placeUuid);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<Report> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Get Reports Failure: ", t);
Helper.makeSnackBar(rlPlaceDetails, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<JsonObject> call = apiService.registerUser(jsonParam);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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");
}
});
}
}

View File

@ -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<FavoriteAdapter.ViewHolder> {
public static final String TAG = FavoriteAdapter.class.getSimpleName();
private final Context context;
private List<Favorite> favoriteList;
public FavoriteAdapter(Context context, List<Favorite> favoriteList) {
this.context = context;
this.favoriteList = favoriteList;
}
@SuppressLint("NotifyDataSetChanged")
public void updateFavoriteList(List<Favorite> 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<Void> call = apiService.removePlaceFromFavorites(token, userUuid, placeUuid);
call.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> 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<Void> 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<JsonObject> call = apiService.getPlaceFromUuid(token, placeUuid);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Get Reports Failure: ", t);
// Helper.makeSnackBar(rlPlaceDetails, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<MyReportsAdapter.ViewHolder> {
public static final String TAG = MyReportsAdapter.class.getSimpleName();
private final Context context;
private List<Report> reportList;
public MyReportsAdapter(Context context, List<Report> reportList) {
this.context = context;
this.reportList = reportList;
}
@SuppressLint("NotifyDataSetChanged")
public void updateReportList(List<Report> 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<Void> call = apiService.removeReport(token, reportUuid);
call.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> 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<Void> 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));
}
});
}
}

View File

@ -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<PlaceReportsAdapter.ViewHolder> {
public static final String TAG = PlaceReportsAdapter.class.getSimpleName();
private final Context context;
private List<Report> reportList;
public PlaceReportsAdapter(Context context, List<Report> reportList) {
this.context = context;
this.reportList = reportList;
}
public void clearReports() {
reportList.clear();
notifyDataSetChanged();
}
@SuppressLint("NotifyDataSetChanged")
public void updateReportsList(List<Report> 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);
}
}
}

View File

@ -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<UserAdapter.ViewHolder> {
public static final String TAG = UserAdapter.class.getSimpleName();
private final Context context;
private List<User> userList;
public UserAdapter(Context context, List<User> userList) {
this.context = context;
this.userList = userList;
}
@SuppressLint("NotifyDataSetChanged")
public void updateUserList(List<User> 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<String> 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<JsonObject> call = apiService.updateRole(token, uuid, userBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Update Role Error: ", t);
Helper.makeSnackBar(rootView, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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;
}
}

View File

@ -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<JsonObject> call = apiService.verifyOldPassword(loginBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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<JsonObject> call = apiService.changePassword(token, uuid, body);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Password change failed", t);
Helper.makeSnackBar(alertView, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<JsonObject> call = apiService.insertPlace(token, placeBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> 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<JsonObject> call = apiService.updatePlace(token, uuid, placeBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Update Place Error: ", t);
Helper.makeSnackBar(alertView, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<JsonObject> call = apiService.updateUser(bearerToken, uuid, jsonBody);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Update API failed", t);
Helper.makeSnackBar(alertView, context.getString(R.string.Something_went_wrong_Try_again));
}
});
}
}

View File

@ -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<Favorite> 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<JsonObject> call = apiService.getUserFavorites(token, userUuid);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Get favorite Failure: ", t);
Helper.makeSnackBar(rlFavorite, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<Place> 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<Address> 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<Address> 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<ResponseBody> call = apiService.getAllPlaces(token);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> 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<ResponseBody> call, Throwable t) {
t.printStackTrace();
Helper.makeSnackBar(rlMap, getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<Report> 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<JsonObject> call = apiService.getUserReports(token, userUuid);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> 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<JsonObject> call, Throwable t) {
DialogUtils.dismissDialog();
Log.e(TAG, "Get Reports Failure: ", t);
Helper.makeSnackBar(rlMyReport, context.getString(R.string.Network_error_Try_again));
}
});
}
}

View File

@ -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<JsonObject> registerUser(@Body JsonObject userData);
@POST("login")
Call<JsonObject> loginUser(@Body JsonObject body);
@PUT("users/{uuid}")
Call<JsonObject> updateUser(
@Header("Authorization") String bearerToken,
@Path("uuid") String uuid,
@Body JsonObject body
);
@POST("login")
Call<JsonObject> verifyOldPassword(@Body JsonObject loginBody);
@PUT("users/{uuid}")
Call<JsonObject> changePassword(
@Header("Authorization") String token,
@Path("uuid") String uuid,
@Body JsonObject body
);
@POST("places")
Call<JsonObject> insertPlace(
@Header("Authorization") String token,
@Body JsonObject body
);
@POST("report_types")
Call<JsonObject> insertReportType(
@Header("Authorization") String token,
@Body JsonObject body
);
@PUT("places/{uuid}")
Call<JsonObject> updatePlace(@Header("Authorization") String token,
@Path("uuid") String uuid,
@Body JsonObject placeData);
@GET("places")
Call<ResponseBody> getAllPlaces(@Header("Authorization") String token);
@POST("users/{user_uuid}/places")
Call<JsonObject> addPlaceToFavorites(
@Header("Authorization") String token,
@Path("user_uuid") String userUuid,
@Body JsonObject placeBody
);
@DELETE("users/{user_uuid}/places/{place_uuid}")
Call<Void> removePlaceFromFavorites(
@Header("Authorization") String token,
@Path("user_uuid") String userUuid,
@Path("place_uuid") String placeUuid
);
@GET("users/{user_uuid}/places")
Call<JsonObject> getFavoritePlaces(
@Header("Authorization") String token,
@Path("user_uuid") String userUuid
);
@PUT("users/{user_uuid}/places")
Call<JsonObject> restorePlaceToFavorites(
@Header("Authorization") String authToken,
@Path("user_uuid") String userUuid,
@Body JsonObject body
);
@POST("reports")
Call<JsonObject> insertReport(
@Header("Authorization") String token,
@Body JsonObject reportBody
);
@GET("users/{user_uuid}/reports")
Call<JsonObject> getUserReports(
@Header("Authorization") String token,
@Path("user_uuid") String userUuid
);
@DELETE("reports/{report_uuid}")
Call<Void> removeReport(
@Header("Authorization") String token,
@Path("report_uuid") String reportUuid
);
@PUT("/reports/{uuid}")
Call<JsonObject> updateReport(@Header("Authorization") String token,
@Path("uuid") String uuid,
@Body JsonObject placeData);
@GET("places/{place_uuid}/reports")
Call<JsonObject> getPlaceReports(
@Header("Authorization") String token,
@Path("place_uuid") String placeUuid
);
@GET("users/{user_uuid}/places")
Call<JsonObject> getUserFavorites(
@Header("Authorization") String token,
@Path("user_uuid") String userUuid
);
@GET("roles")
Call<JsonObject> getRoles(@Header("Authorization") String token);
@GET("users")
Call<JsonObject> getAllUsers(@Header("Authorization") String token);
@PUT("users/{uuid}")
Call<JsonObject> updateRole(@Header("Authorization") String token,
@Path("uuid") String uuid,
@Body JsonObject userData);
@GET("places/{uuid}")
Call<JsonObject> getPlaceFromUuid(
@Header("Authorization") String token,
@Path("uuid") String uuid
);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<String> ROLES_OPTIONS = Arrays.asList("admin", "viewer");
public static final String BASE_URL = "https://locationapi-m13l.onrender.com/";
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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, "");
}
}

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,2C8.14,2 5,5.14 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.86 -3.14,-7 -7,-7zM16,10h-3v3h-2v-3L8,10L8,8h3L11,5h2v3h3v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FF0000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8,9h8v10L8,19L8,9zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M3,10h11v2H3V10zM3,8h11V6H3V8zM3,16h7v-2H3V16zM18.01,12.87l0.71,-0.71c0.39,-0.39 1.02,-0.39 1.41,0l0.71,0.71c0.39,0.39 0.39,1.02 0,1.41l-0.71,0.71L18.01,12.87zM17.3,13.58l-5.3,5.3V21h2.12l5.3,-5.3L17.3,13.58z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M13.95,13H9V8.05l5.61,-5.61C13.78,2.16 12.9,2 12,2c-4.2,0 -8,3.22 -8,8.2c0,3.32 2.67,7.25 8,11.8c5.33,-4.55 8,-8.48 8,-11.8c0,-1.01 -0.16,-1.94 -0.45,-2.8L13.95,13z"/>
<path android:fillColor="@android:color/white" android:pathData="M11,11l2.12,0l6.16,-6.16l-2.12,-2.12l-6.16,6.16z"/>
<path android:fillColor="@android:color/white" android:pathData="M20.71,2L20,1.29C19.8,1.1 19.55,1 19.29,1c-0.13,0 -0.48,0.07 -0.71,0.29l-0.72,0.72l2.12,2.12l0.72,-0.72C21.1,3.02 21.1,2.39 20.71,2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#BE0808" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#BE0808" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/>
</vector>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M10,8m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"/>
<path android:fillColor="@android:color/white" android:pathData="M10.67,13.02C10.45,13.01 10.23,13 10,13c-2.42,0 -4.68,0.67 -6.61,1.82C2.51,15.34 2,16.32 2,17.35V20h9.26C10.47,18.87 10,17.49 10,16C10,14.93 10.25,13.93 10.67,13.02z"/>
<path android:fillColor="@android:color/white" android:pathData="M20.75,16c0,-0.22 -0.03,-0.42 -0.06,-0.63l1.14,-1.01l-1,-1.73l-1.45,0.49c-0.32,-0.27 -0.68,-0.48 -1.08,-0.63L18,11h-2l-0.3,1.49c-0.4,0.15 -0.76,0.36 -1.08,0.63l-1.45,-0.49l-1,1.73l1.14,1.01c-0.03,0.21 -0.06,0.41 -0.06,0.63s0.03,0.42 0.06,0.63l-1.14,1.01l1,1.73l1.45,-0.49c0.32,0.27 0.68,0.48 1.08,0.63L16,21h2l0.3,-1.49c0.4,-0.15 0.76,-0.36 1.08,-0.63l1.45,0.49l1,-1.73l-1.14,-1.01C20.72,16.42 20.75,16.22 20.75,16zM17,18c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S18.1,18 17,18z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48V20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48V3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM15,19l-6,-2.11V5l6,2.11V19z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,14v-2.47l6.88,-6.88c0.2,-0.2 0.51,-0.2 0.71,0l1.77,1.77c0.2,0.2 0.2,0.51 0,0.71L8.47,14L6,14zM18,14h-7.5l2,-2L18,12v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M22,4h-2c-0.55,0 -1,0.45 -1,1v9c0,0.55 0.45,1 1,1h2V4zM2.17,11.12c-0.11,0.25 -0.17,0.52 -0.17,0.8V13c0,1.1 0.9,2 2,2h5.5l-0.92,4.65c-0.05,0.22 -0.02,0.46 0.08,0.66 0.23,0.45 0.52,0.86 0.88,1.22L10,22l6.41,-6.41c0.38,-0.38 0.59,-0.89 0.59,-1.42V6.34C17,5.05 15.95,4 14.66,4h-8.1c-0.71,0 -1.36,0.37 -1.72,0.97l-2.67,6.15z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M10.89,18.28l0.57,-2.89c0.12,-0.59 -0.04,-1.2 -0.42,-1.66 -0.38,-0.46 -0.94,-0.73 -1.54,-0.73L4,13v-1.08L6.57,6h8.09c0.18,0 0.34,0.16 0.34,0.34v7.84l-4.11,4.1M10,22l6.41,-6.41c0.38,-0.38 0.59,-0.89 0.59,-1.42L17,6.34C17,5.05 15.95,4 14.66,4h-8.1c-0.71,0 -1.36,0.37 -1.72,0.97l-2.67,6.15c-0.11,0.25 -0.17,0.52 -0.17,0.8L2,13c0,1.1 0.9,2 2,2h5.5l-0.92,4.65c-0.05,0.22 -0.02,0.46 0.08,0.66 0.23,0.45 0.52,0.86 0.88,1.22L10,22zM20,15h2L22,4h-2c-0.55,0 -1,0.45 -1,1v9c0,0.55 0.45,1 1,1z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/holo_green_dark" android:pathData="M2,20h2c0.55,0 1,-0.45 1,-1v-9c0,-0.55 -0.45,-1 -1,-1L2,9v11zM21.83,12.88c0.11,-0.25 0.17,-0.52 0.17,-0.8L22,11c0,-1.1 -0.9,-2 -2,-2h-5.5l0.92,-4.65c0.05,-0.22 0.02,-0.46 -0.08,-0.66 -0.23,-0.45 -0.52,-0.86 -0.88,-1.22L14,2 7.59,8.41C7.21,8.79 7,9.3 7,9.83v7.84C7,18.95 8.05,20 9.34,20h8.11c0.7,0 1.36,-0.37 1.72,-0.97l2.66,-6.15z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFC107" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M2,20h2c0.55,0 1,-0.45 1,-1v-9c0,-0.55 -0.45,-1 -1,-1L2,9v11zM21.83,12.88c0.11,-0.25 0.17,-0.52 0.17,-0.8L22,11c0,-1.1 -0.9,-2 -2,-2h-5.5l0.92,-4.65c0.05,-0.22 0.02,-0.46 -0.08,-0.66 -0.23,-0.45 -0.52,-0.86 -0.88,-1.22L14,2 7.59,8.41C7.21,8.79 7,9.3 7,9.83v7.84C7,18.95 8.05,20 9.34,20h8.11c0.7,0 1.36,-0.37 1.72,-0.97l2.66,-6.15z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M13.11,5.72l-0.57,2.89c-0.12,0.59 0.04,1.2 0.42,1.66 0.38,0.46 0.94,0.73 1.54,0.73H20v1.08L17.43,18H9.34c-0.18,0 -0.34,-0.16 -0.34,-0.34V9.82l4.11,-4.1M14,2L7.59,8.41C7.21,8.79 7,9.3 7,9.83v7.83C7,18.95 8.05,20 9.34,20h8.1c0.71,0 1.36,-0.37 1.72,-0.97l2.67,-6.15c0.11,-0.25 0.17,-0.52 0.17,-0.8V11c0,-1.1 -0.9,-2 -2,-2h-5.5l0.92,-4.65c0.05,-0.22 0.02,-0.46 -0.08,-0.66 -0.23,-0.45 -0.52,-0.86 -0.88,-1.22L14,2zM4,9H2v11h2c0.55,0 1,-0.45 1,-1v-9c0,-0.55 -0.45,-1 -1,-1z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#F44336" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M22,4h-2c-0.55,0 -1,0.45 -1,1v9c0,0.55 0.45,1 1,1h2V4zM2.17,11.12c-0.11,0.25 -0.17,0.52 -0.17,0.8V13c0,1.1 0.9,2 2,2h5.5l-0.92,4.65c-0.05,0.22 -0.02,0.46 0.08,0.66 0.23,0.45 0.52,0.86 0.88,1.22L10,22l6.41,-6.41c0.38,-0.38 0.59,-0.89 0.59,-1.42V6.34C17,5.05 15.95,4 14.66,4h-8.1c-0.71,0 -1.36,0.37 -1.72,0.97l-2.67,6.15z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#4CAF50" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M2,20h2c0.55,0 1,-0.45 1,-1v-9c0,-0.55 -0.45,-1 -1,-1L2,9v11zM21.83,12.88c0.11,-0.25 0.17,-0.52 0.17,-0.8L22,11c0,-1.1 -0.9,-2 -2,-2h-5.5l0.92,-4.65c0.05,-0.22 0.02,-0.46 -0.08,-0.66 -0.23,-0.45 -0.52,-0.86 -0.88,-1.22L14,2 7.59,8.41C7.21,8.79 7,9.3 7,9.83v7.84C7,18.95 8.05,20 9.34,20h8.11c0.7,0 1.36,-0.37 1.72,-0.97l2.66,-6.15z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="?attr/colorPrimary"/>
<corners android:radius="50px"/>
<stroke android:width="1dip" android:color="?attr/colorPrimary"/>
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@android:color/transparent"/>
<corners android:radius="1px"/>
<stroke android:width="1dip" android:color="?attr/colorPrimary"/>
</shape>

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlAddPlace"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.AddNewPlaceActivity">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/btnSubmit"
android:layout_below="@id/toolbar"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:padding="@dimen/layout_padding">
<LinearLayout
android:id="@+id/llInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/ivPlacePhoto"
android:layout_width="@dimen/image_view_width"
android:layout_height="@dimen/image_view_height"
android:layout_gravity="center"
android:layout_margin="@dimen/layout_padding"
android:background="@drawable/rectangle"
android:importantForAccessibility="no"
android:importantForAutofill="no"
android:padding="@dimen/content_padding"
android:src="@drawable/logo_add_location" />
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPlaceName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Place_Name"
android:importantForAutofill="no"
android:inputType="text"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etLatitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:hint="@string/Enter_Latitude"
android:importantForAutofill="no"
android:inputType="text"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etLongitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:hint="@string/Enter_Longitude"
android:importantForAutofill="no"
android:inputType="text"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Address"
android:importantForAutofill="no"
android:inputType="textImeMultiLine"
android:maxLines="4"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPlaceDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Place_Description"
android:importantForAutofill="no"
android:inputType="textImeMultiLine"
android:maxLines="5"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</ScrollView>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnSubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/SUBMIT"
android:textColor="?attr/colorOnPrimary"
tools:ignore="HardcodedText" />
</RelativeLayout>

View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlAddReport"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.AddReportActivity">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/btnSubmit"
android:layout_below="@id/toolbar"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:padding="@dimen/layout_padding">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/ivReportPhoto"
android:layout_width="@dimen/image_view_width"
android:layout_height="@dimen/image_view_height"
android:layout_gravity="center"
android:layout_margin="@dimen/layout_padding"
android:background="@drawable/rectangle"
android:importantForAccessibility="no"
android:importantForAutofill="no"
android:padding="@dimen/content_padding"
android:src="@drawable/logo_add_location" />
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPlaceName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/Enter_Place_Name"
android:importantForAutofill="no"
android:inputType="text"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Description"
android:importantForAutofill="no"
android:inputType="textImeMultiLine"
android:maxLines="5"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:gravity="center"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/ivThumbsUp"
android:layout_width="@dimen/image_icon_width"
android:layout_height="@dimen/image_icon_height"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="Thumbs Up"
android:focusable="true"
android:padding="8dp"
app:tint="?attr/colorOnBackground"
android:src="@drawable/ic_thumb_up_border" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/GOOD"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeInEditText"
android:textStyle="bold" />
</LinearLayout>
<Space
android:layout_width="@dimen/edit_text_padding"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/ivThumbsAverage"
android:layout_width="@dimen/image_icon_width"
android:layout_height="@dimen/image_icon_height"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="Thumbs Average"
android:focusable="true"
android:padding="8dp"
app:tint="?attr/colorOnBackground"
android:src="@drawable/ic_thumb_up_border" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/AVERAGE"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeInEditText"
android:textStyle="bold" />
</LinearLayout>
<Space
android:layout_width="@dimen/edit_text_padding"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/ivThumbsDown"
android:layout_width="@dimen/image_icon_width"
android:layout_height="@dimen/image_icon_height"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="Thumbs Down"
android:focusable="true"
android:padding="8dp"
app:tint="?attr/colorOnBackground"
android:src="@drawable/ic_thumb_down_border" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/BAD"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeInEditText"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnSubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/SUBMIT"
android:textColor="?attr/colorOnPrimary"
tools:ignore="HardcodedText"
/>
</RelativeLayout>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlLogin"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.LoginActivity">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
android:layout_centerInParent="true"
android:fillViewport="true"
android:padding="@dimen/layout_padding">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_email">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Username"
android:importantForAutofill="no"
android:inputType="textPersonName" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:passwordToggleEnabled="true"
app:startIconDrawable="@drawable/ic_lock">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Password"
android:importantForAutofill="no"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/LOGIN"
android:textColor="?attr/colorOnPrimary"
android:textSize="@dimen/textSizeInEditText" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:text="@string/Dont_have_an_Account" />
<TextView
android:id="@+id/tvRegisterRedirect"
android:layout_width="wrap_content"
android:layout_marginStart="@dimen/small_content_padding"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:text="@string/Please_register"
android:textColor="?attr/colorPrimary"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</RelativeLayout>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlMainActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<FrameLayout
android:id="@+id/flMainContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<!-- Bottom Navigation Menu -->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?attr/colorTertiary"
app:itemIconTint="?attr/colorOnBackground"
app:itemTextColor="?attr/colorOnBackground"
app:menu="@menu/menu_bottom_navigation" />
</RelativeLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlManageRoles"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.ManageRolesActivity">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilSearch"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/toolbar"
android:padding="@dimen/content_padding"
app:startIconDrawable="@drawable/ic_person">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etSearchUser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_search"
android:hint="@string/Search_User"
android:imeOptions="actionSearch"
android:importantForAutofill="no"
android:inputType="textPersonName"
android:drawableTint="?attr/colorOnBackground"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvUsers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/content_padding"
android:visibility="visible"
android:layout_below="@id/tilSearch"
tools:listitem="@layout/list_view_users" />
</RelativeLayout>

View File

@ -0,0 +1,234 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlPlaceDetails"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.PlaceDetailActivity">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/btnSubmit"
android:layout_below="@id/toolbar"
android:fillViewport="true"
android:padding="@dimen/layout_padding">
<LinearLayout
android:id="@+id/llInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/ivProductPhoto"
android:layout_width="match_parent"
android:layout_height="@dimen/image_icon_height"
android:layout_gravity="center"
android:background="@drawable/rectangle"
android:importantForAccessibility="no"
android:importantForAutofill="no"
android:scaleType="fitXY"
android:src="@drawable/place_header" />
<!-- Place Name and Icons Row -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal"
android:weightSum="3">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2.5"
android:orientation="vertical">
<!-- Place Name -->
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Place_Name"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeLarge"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/etPlaceName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/Place_Name"
android:importantForAutofill="no"
android:inputType="text"
android:text="@string/Place_Name"
android:textSize="@dimen/textSizeInEditText"
android:textStyle="bold" />
</LinearLayout>
<!-- Favorite & Edit Icons -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/ivFavorite"
android:layout_width="@dimen/small_image_icon_width"
android:layout_height="@dimen/small_image_icon_height"
android:autofillHints="Add to Favorite"
android:contentDescription="Add to Favorite"
android:src="@drawable/ic_favorite_border"
/>
<Space
android:layout_width="wrap_content"
android:layout_height="@dimen/small_padding" />
<ImageView
android:id="@+id/ivEdit"
android:layout_width="@dimen/smallest_image_icon_width"
android:layout_height="@dimen/smallest_image_icon_height"
android:autofillHints="Edit Place"
android:contentDescription="Edit Place"
android:src="@drawable/ic_edit_location"
app:tint="?attr/colorOnBackground" />
</LinearLayout>
</LinearLayout>
<!--Address-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:gravity="center_vertical"
android:orientation="vertical"
android:weightSum="2">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:layout_weight="1"
android:text="@string/Address"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeLarge"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/etAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="false"
android:focusable="false"
android:hint="@string/Address"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:maxLines="5"
android:text="@string/Address"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeSmall" />
</LinearLayout>
<!--Place description-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:gravity="center_vertical"
android:orientation="vertical"
android:weightSum="2">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:layout_weight="1"
android:text="@string/Place_Description"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeLarge"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/etPlaceDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="false"
android:focusable="false"
android:hint="@string/Place_Description"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:maxLines="5"
android:text="@string/Place_Description"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeSmall" />
</LinearLayout>
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/Report"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeLarge"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<TextView
android:id="@+id/tvNoData"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/No_reports_found"
android:textAlignment="center" />
<!-- Report details -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvReports"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
tools:listitem="@layout/list_view_place_reports" />
</LinearLayout>
</ScrollView>
<!-- Add Report btn -->
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnSubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginTop="@dimen/layout_padding"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/ADD_REPORT"
android:textColor="?attr/colorOnPrimary" />
</RelativeLayout>

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlRegister"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.RegisterActivity">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
android:layout_centerInParent="true"
android:fillViewport="true"
android:padding="@dimen/layout_padding">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_person">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Username"
android:importantForAutofill="no"
android:inputType="textPersonName" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_email">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Email"
android:importantForAutofill="no"
android:inputType="textEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:passwordToggleEnabled="true"
app:startIconDrawable="@drawable/ic_lock">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Password"
android:importantForAutofill="no"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnRegister"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/REGISTER"
android:textColor="?attr/colorOnPrimary"
android:textSize="@dimen/textSizeInEditText" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:text="@string/Already_have_an_Account" />
<TextView
android:id="@+id/tvLoginRedirect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
android:text="@string/Please_login"
android:textColor="?attr/colorPrimary"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</RelativeLayout>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/llInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_padding"
android:orientation="vertical">
<ImageView
android:id="@+id/ivPlacePhoto"
android:layout_width="@dimen/image_view_width"
android:layout_height="@dimen/image_view_height"
android:layout_gravity="center"
android:layout_margin="@dimen/layout_padding"
android:background="@drawable/rectangle"
android:importantForAccessibility="no"
android:importantForAutofill="no"
android:padding="@dimen/content_padding"
android:src="@drawable/logo_add_location" />
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPlaceName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Place_Name"
android:importantForAutofill="no"
android:inputType="text"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etLatitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Latitude"
android:importantForAutofill="no"
android:inputType="text"
android:focusable="false"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etLongitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Longitude"
android:importantForAutofill="no"
android:inputType="text"
android:focusable="false"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Address"
android:importantForAutofill="no"
android:inputType="textImeMultiLine"
android:maxLines="4"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etPlaceDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Place_Description"
android:importantForAutofill="no"
android:inputType="textImeMultiLine"
android:maxLines="5"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnSubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:layout_marginTop="@dimen/layout_padding"
android:text="@string/SUBMIT"
android:textColor="?attr/colorOnPrimary"
tools:ignore="HardcodedText" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/llInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_padding"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:passwordToggleEnabled="true"
app:startIconDrawable="@drawable/ic_lock">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etOldPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Old_Password"
android:importantForAutofill="no"
android:inputType="textPassword"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:passwordToggleEnabled="true"
app:startIconDrawable="@drawable/ic_lock">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etNewPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_New_Password"
android:importantForAutofill="no"
android:inputType="textPassword"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/llInput"
android:orientation="horizontal"
android:weightSum="1">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnCancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/CANCEL"
android:textColor="?attr/colorOnPrimary"
tools:ignore="HardcodedText" />
<View
android:layout_width="@dimen/card_stroke"
android:layout_height="match_parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnUpdate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/UPDATE"
android:textColor="?attr/colorOnPrimary"
tools:ignore="HardcodedText" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/llInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_padding"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_person">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Username"
android:importantForAutofill="no"
android:inputType="textPersonName"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_padding"
app:startIconDrawable="@drawable/ic_email">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Enter_Email"
android:importantForAutofill="no"
android:inputType="textEmailAddress"
tools:ignore="HardcodedText" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/llInput"
android:orientation="horizontal"
android:weightSum="1">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnCancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/CANCEL"
android:textColor="?attr/colorOnPrimary"
tools:ignore="HardcodedText" />
<View
android:layout_width="@dimen/card_stroke"
android:layout_height="match_parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnUpdate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:background="?attr/colorPrimary"
android:backgroundTintMode="src_atop"
android:text="@string/UPDATE"
android:textColor="?attr/colorOnPrimary"
tools:ignore="HardcodedText" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rlFavorite"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
tools:context=".fragment.FavoriteFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFavorite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
tools:listitem="@layout/list_view_favorite"
android:layout_marginBottom="@dimen/bottom_menu_height"/>
<TextView
android:id="@+id/tvNoData"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/No_Favorite_found"
android:textAlignment="center"
tools:ignore="HardcodedText" />
</FrameLayout>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/rlMap"
android:layout_marginTop="?attr/actionBarSize"
tools:context=".fragment.MapFragment">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilSearch"
android:padding="@dimen/content_padding"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:startIconDrawable="@drawable/ic_location">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etSearchLocation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_search"
android:hint="@string/Search_Place"
android:importantForAutofill="no"
android:imeOptions="actionSearch"
android:inputType="textPersonName"
android:drawableTint="?attr/colorOnBackground"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/mapFrame"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tilSearch" />
</RelativeLayout>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:id="@+id/rlMyReport"
tools:context=".fragment.MyReportsFragment"
android:layout_marginBottom="@dimen/bottom_menu_height">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvReport"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
tools:listitem="@layout/list_view_my_report" />
<TextView
android:id="@+id/tvNoData"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/No_reports_found"
android:textAlignment="center" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/extendedFbReport"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginStart="@dimen/layout_padding"
android:layout_marginTop="@dimen/layout_padding"
android:layout_marginEnd="@dimen/layout_padding"
android:layout_marginBottom="@dimen/layout_padding"
android:text="@string/Add_Report"
android:textColor="?attr/colorOnPrimary"
app:icon="@drawable/ic_add"
android:backgroundTint="?attr/colorPrimary"
app:iconSize="@dimen/textSizeLarge"
app:iconTint="?attr/colorOnPrimary"/>
</FrameLayout>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"
app:cardUseCompatPadding="true"
app:strokeColor="?attr/colorPrimary"
app:strokeWidth="2dp"
tools:ignore="MissingConstraints">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/content_padding"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal"
android:weightSum="2">
<!--Place name-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/Place_Name"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeMedium"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvPlaceName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/Place_Name"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:maxLines="5"
android:text="@string/Place_Name"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeInEditText"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/ivFavorite"
android:layout_width="@dimen/small_image_icon_width"
android:layout_height="@dimen/small_image_icon_height"
android:autofillHints="Add to Favorite"
android:contentDescription="Add to Favorite"
android:src="@drawable/ic_favorite" />
</LinearLayout>
</LinearLayout>
<!--Report description-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/Place_Description"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeInEditText"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/Place_Description"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:maxLines="5"
android:text="@string/Place_Description"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeSmall"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"
app:cardUseCompatPadding="true"
app:strokeColor="?attr/colorPrimary"
app:strokeWidth="2dp"
tools:ignore="MissingConstraints">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/content_padding"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal"
android:weightSum="2">
<!--Place name-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/Place_Name"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeMedium"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvPlaceName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/Place_Name"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:maxLines="5"
android:text="@string/Place_Name"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeInEditText" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/ivEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="Edit"
android:src="@drawable/ic_edit_location"
app:tint="?attr/colorOnBackground" />
<ImageView
android:id="@+id/ivDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/small_padding"
android:contentDescription="@string/Delete_report"
android:src="@drawable/ic_delete" />
</LinearLayout>
</LinearLayout>
<!--Report description-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/Report_Description"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeInEditText"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/Report_Description"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:maxLines="5"
android:text="@string/Place_Description"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeSmall" />
</LinearLayout>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvRating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/content_padding"
android:background="@drawable/rating"
android:backgroundTint="@color/neutralGrey"
android:gravity="center"
android:padding="@dimen/content_padding"
android:text="@string/Rating_GOOD"
android:textAppearance="?attr/textAppearanceSubtitle1"
android:textColor="?attr/colorOnPrimary" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"
app:cardUseCompatPadding="true"
app:strokeColor="?attr/colorPrimary"
app:strokeWidth="2dp"
tools:ignore="MissingConstraints">
<!--Report rating-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/content_padding"
android:weightSum="2">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.6"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/ivRating"
android:layout_width="@dimen/small_image_icon_height"
android:layout_height="@dimen/small_image_icon_height"
android:contentDescription="rating"
android:src="@drawable/ic_thumbs_up" />
<TextView
android:id="@+id/tvRating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Rating_GOOD"
android:textStyle="bold"
android:gravity="center_horizontal"
android:textColor="?attr/colorOnBackground"
/>
</LinearLayout>
<!--Report description-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/small_padding"
android:layout_weight="1.5"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/Report_Description"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeInEditText"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/Report_Description"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:text="@string/Report_Description"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeSmall"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"
app:cardUseCompatPadding="true"
app:strokeColor="?attr/colorPrimary"
app:strokeWidth="2dp"
tools:ignore="MissingConstraints">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/content_padding"
android:orientation="horizontal"
android:weightSum="2">
<!--user name-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/User"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeMedium"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:hint="@string/User"
android:importantForAutofill="no"
android:inputType="textMultiLine"
android:text="@string/User"
android:textColor="@color/neutralGrey"
android:textSize="@dimen/textSizeInEditText"
tools:ignore="HardcodedText" />
</LinearLayout>
<!--Role-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/small_padding"
android:text="@string/Role"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/textSizeMedium"
android:textStyle="bold"
app:startIconDrawable="@drawable/ic_location" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/Change_Role"
android:paddingEnd="@dimen/small_padding">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/acRole"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_dropdown"
android:inputType="none"
android:padding="@dimen/content_padding"
android:textColor="@color/neutralGrey"
android:textColorHint="@android:color/darker_gray" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp"
android:background="?attr/colorSurface"
android:orientation="vertical"
android:gravity="center">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleLarge"
android:indeterminate="true" />
<TextView
android:id="@+id/tvLoadingText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Loading..."
android:paddingTop="10dp"
android:textSize="16sp"
android:textColor="?attr/colorOnBackground"
tools:ignore="HardcodedText" />
</LinearLayout>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:textColor="?attr/colorOnPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.DayNight"
app:titleTextColor="?attr/colorOnPrimary" />

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_myReports"
android:icon="@drawable/ic_report"
android:title="@string/My_Report" />
<item
android:id="@+id/menu_map"
android:icon="@drawable/ic_map"
android:title="@string/Map" />
<item
android:id="@+id/menu_favorite"
android:icon="@drawable/ic_favorite"
android:title="@string/Favorite" />
</menu>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_profile"
android:icon="@drawable/ic_person"
android:title="@string/Profile"
app:iconTint="?attr/colorPrimaryVariant" />
<item
android:id="@+id/menu_changePassword"
android:icon="@drawable/ic_lock"
android:title="@string/Change_Password"
app:iconTint="?attr/colorPrimaryVariant" />
<item
android:id="@+id/menu_changeLanguage"
android:icon="@drawable/ic_language"
android:title="@string/Change_Language"
app:iconTint="?attr/colorPrimaryVariant" />
<item
android:id="@+id/menu_manageRoles"
android:icon="@drawable/ic_manage_roles"
android:title="@string/Manage_Roles"
app:iconTint="?attr/colorPrimaryVariant" />
<item
android:id="@+id/menu_logout"
android:icon="@drawable/ic_logout"
android:title="@string/Logout"
app:iconTint="?attr/colorError" />
</menu>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Some files were not shown because too many files have changed in this diff Show More