diff --git a/app/build.gradle b/app/build.gradle index 579fc29..0275b58 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,16 +1,17 @@ plugins { id 'com.android.application' id 'com.google.gms.google-services' + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' } android { namespace 'com.capstone.foodify.shipper' - compileSdk 33 + compileSdk 31 defaultConfig { applicationId "com.capstone.foodify.shipper" minSdk 26 - targetSdk 32 + targetSdk 30 versionCode 1 versionName "1.0" @@ -27,16 +28,22 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + buildFeatures { + viewBinding true + } } dependencies { implementation 'androidx.core:core-splashscreen:1.0.0' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0-beta01' + + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.6.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'com.google.firebase:firebase-auth:21.0.3' implementation 'com.google.firebase:firebase-storage:20.0.1' + implementation 'com.google.android.gms:play-services-maps:18.1.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' @@ -48,4 +55,6 @@ dependencies { implementation 'com.github.gabriel-TheCode:AestheticDialogs:1.3.6' implementation 'com.saadahmedev.popup-dialog:popup-dialog:1.0.2' implementation 'com.squareup.picasso:picasso:2.71828' + + implementation 'com.google.android.gms:play-services-location:21.0.1' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3808799..e0d7d6c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,10 @@ xmlns:tools="http://schemas.android.com/tools"> + + + + + android:usesCleartextTraffic="true"> + + @@ -23,7 +31,7 @@ android:exported="false" /> + android:exported="true" /> recommendFood(); -// -// @GET("products/enable?pageNo=0&pageSize=10&sortBy=createdTime&sortDir=desc") -// Call recentFood(); -// -// @GET("shops/enable?pageNo=0&pageSize=10&sortBy=id&sortDir=asc") -// Call allShops(); -// -// @GET("products/search/{name}") -// Call searchFoodByName(@Path("name") String name, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// -// @GET("products") -// Call listFood(@Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// -// @GET("categories/randoms?pageNo=0&pageSize=7") -// Call listCategory(); -// -// @GET("products/categories") -// Call listFoodByCategory(@Query("id") List id, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// -// @GET("products/categoriesAndName") -// Call listFoodByCategoriesAndName(@Query("id") List id, @Query("name") String name, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// -// @GET("products/{id}") -// Call detailFood(@Path("id") String id); -// -// @GET("shops/{id}") -// Call detailShop(@Path("id") int id); -// -// @GET("products/shops/{id}") -// Call listFoodByShopId(@Path("id") int id, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// -// @GET("districts/{districtId}/wards") -// Call> wardResponse(@Path("districtId") int district_id); -// -// @GET("districts") -// Call> districtResponse(); -// -// @GET("sliders") -// Call> listSlider(); -// -// @Headers({"accept: */*", "Content-Type: application/json"}) -// @POST("auth/signup") -// Call register(@Body User user); -// -// @POST("auth/check") -// Call checkEmailOrPhoneExist (@Body User user); -// -// @GET("products/{productId}/comments") -// Call getCommentByProductId(@Path("productId") int productId, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// -// @GET("products/{productId}/users/{userId}") -// Call getUserComment(@Path("productId") int productId, @Path("userId") int userId); -// -// @GET("shops/{id}") -// Call getShopById(@Path("id") int id); -// -// @GET("products/{productId}/user") -// Call checkUserBuyProduct(@Path("productId") int productId, @Query("userId") int userId); + @GET("shippers/user/{id}") + Call getShipper(@Path("id") int userId); } diff --git a/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java b/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java index 2f618d2..5bf94ad 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java +++ b/app/src/main/java/com/capstone/foodify/shipper/API/FoodApiToken.java @@ -3,6 +3,7 @@ package com.capstone.foodify.shipper.API; import androidx.annotation.NonNull; import com.capstone.foodify.shipper.Common; +import com.capstone.foodify.shipper.Model.Response.Orders; import com.capstone.foodify.shipper.Model.User; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -20,6 +21,7 @@ import retrofit2.http.Body; import retrofit2.http.GET; import retrofit2.http.PUT; import retrofit2.http.Path; +import retrofit2.http.Query; public interface FoodApiToken { Gson gson = new GsonBuilder().setDateFormat("HH:mm:ss dd-MM-yyyy").setLenient().create(); @@ -41,51 +43,12 @@ public interface FoodApiToken { .create(FoodApiToken.class); -// @POST("users/{userId}/loves/{productId}") -// Call addFoodToFavorite(@Path("userId") int userId, @Path("productId") int productId); -// -// @DELETE("users/{userId}/loves/{productId}") -// Call removeFoodFromFavorite(@Path("userId") int userId, @Path("productId") int productId); - @GET("users/email/{userEmail}") Call getUserFromEmail(@Path("userEmail") String userEmail); -// @GET("users/{userId}/loves") -// Call getListFavoriteFood(@Path("userId") int userId, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// @PUT("users/{userId}") Call updateUser(@Path("userId") int userId, @Body User user); -// -// @GET("users/{userId}/loves/{productId}") -// Call checkFoodIsFavorite(@Path("userId") int userId, @Path("productId") int productId); -// -// @POST("users/{userId}/addresses") -// Call createAddressUser(@Path("userId") int userId, @Body Address address); -// -// @PUT("users/{userId}/addresses/{addressId}") -// Call editAddressUser(@Path("userId") int userId, @Path("addressId") int addressId, @Body Address address); -// -// @DELETE("users/{userId}/addresses/{addressId}") -// Call deleteAddressUser(@Path("userId") int userId, @Path("addressId") int addressId); -// -// @POST("users/{userId}/orders") -// Call createOrder(@Path("userId") int userId, @Body Order order); -// -// @POST("products/{productId}/comments") -// Call createComment(@Path("productId") int productId, @Body Comment comment); -// -// @DELETE("products/{productId}/comments/{commentId}") -// Call deleteComment(@Path("productId") int productId, @Path("commentId") int commentId); -// -// @PUT("products/{productId}/comments/{commentId}") -// Call updateComment(@Path("productId") int productId, @Path("commentId") int commentId, @Body Comment comment); -// -// @GET("users/{userId}/orders/status/{status}") -// Call getOrderUser(@Path("userId") int userId, @Path("status") String status, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); -// -// @DELETE("users/{userId}/orders/{orderId}") -// Call deleteOrder(@Path("userId") int userId, @Path("orderId") int orderId); -// -// @PUT("users/{userId}/orders/{orderId}/status") -// Call updateOrderStatus(@Path("userId") int userId, @Path("orderId") int orderId, @Query("status") String status); + + @GET("shippers/{shipperId}/orders") + Call getListOrder(@Path("shipperId") int shipperId, @Query("pageNo") int pageNo, @Query("pageSize") int pageSize, @Query("sortBy") String sortBy, @Query("sortDir") String sortDir); } diff --git a/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java b/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java index 3dff0ef..daee8ba 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Activity/MainActivity.java @@ -1,19 +1,48 @@ package com.capstone.foodify.shipper.Activity; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import androidx.core.splashscreen.SplashScreen; import androidx.viewpager2.widget.ViewPager2; +import android.Manifest; import android.annotation.SuppressLint; +import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageManager; +import android.location.Location; +import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.util.Log; import android.view.MenuItem; +import android.widget.Toast; +import com.capstone.foodify.shipper.BuildConfig; import com.capstone.foodify.shipper.Common; import com.capstone.foodify.shipper.R; import com.capstone.foodify.shipper.ViewPagerAdapter; +import com.google.android.gms.common.api.ApiException; +import com.google.android.gms.common.api.ResolvableApiException; +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.location.LocationSettingsRequest; +import com.google.android.gms.location.LocationSettingsResponse; +import com.google.android.gms.location.LocationSettingsStatusCodes; +import com.google.android.gms.location.Priority; +import com.google.android.gms.location.SettingsClient; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.navigation.NavigationBarView; @@ -23,6 +52,22 @@ public class MainActivity extends AppCompatActivity { ViewPagerAdapter viewPagerAdapter; BottomNavigationView bottomNavigationView; + //Location + private static final int REQUEST_CHECK_SETTINGS = 100; + private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000; + private static final long FASTEST_UPDATE_IN_MILLISECONDS = 3000; + private static final long MAX_WAIT_TIME_IN_MILLISECONDS = 1000; + private static final int LOCATION_REQUEST_CODE = 100; + private static final String TAG = MainActivity.class.getSimpleName(); + + private FusedLocationProviderClient mFusedLocationClient; + private SettingsClient mSettingsClient; + private LocationRequest mLocationRequest; + private LocationSettingsRequest mLocationSettingsRequest; + private LocationCallback mLocationCallBack; + private Location mCurrentLocation; + private boolean mRequestingLocationUpdates = false; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -34,6 +79,46 @@ public class MainActivity extends AppCompatActivity { viewPager2 = findViewById(R.id.viewPager); bottomNavigation(); + + if (Common.CURRENT_LOCATION == null) { + mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); + mSettingsClient = LocationServices.getSettingsClient(this); + + mLocationCallBack = new LocationCallback() { + @Override + public void onLocationResult(@NonNull LocationResult locationResult) { + super.onLocationResult(locationResult); + mCurrentLocation = locationResult.getLastLocation(); + Common.CURRENT_LOCATION = mCurrentLocation; + } + }; + + mLocationRequest = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, UPDATE_INTERVAL_IN_MILLISECONDS) + .setWaitForAccurateLocation(false) + .setMinUpdateDistanceMeters(FASTEST_UPDATE_IN_MILLISECONDS) + .setMaxUpdateDelayMillis(MAX_WAIT_TIME_IN_MILLISECONDS) + .build(); + + LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); + builder.addLocationRequest(mLocationRequest); + mLocationSettingsRequest = builder.build(); + + checkLocationPermission(); + + } + + //Init location user + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + if(mCurrentLocation != null){ + Common.CURRENT_LOCATION = mCurrentLocation; + stopLocationUpdates(); + } + + } + }, 3000); } private void bottomNavigation() { @@ -73,4 +158,131 @@ public class MainActivity extends AppCompatActivity { } }); } + + //Location + private void checkLocationPermission(){ + if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){ + + requestForPermission(); + + }else { + //Get location + mRequestingLocationUpdates = true; + startLocationUpdates(); + } + } + + private void requestForPermission() { + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + if(requestCode == LOCATION_REQUEST_CODE){ + if(grantResults[0] == PackageManager.PERMISSION_GRANTED){ + Toast.makeText(this, "Đã cấp quyền thành công!", Toast.LENGTH_SHORT).show(); + } else { + showDialogPermission(); + } + } + } + + private void showDialogPermission() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + + builder.setMessage("Hãy cho ứng dụng truy cập vào vị trí của bạn để trải nghiệm tốt hơn!") + .setCancelable(false) + .setPositiveButton("Ok", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + openSettings(); + dialog.cancel(); + } + }) + .setNegativeButton("Để sau", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + AlertDialog alertDialog = builder.create(); + alertDialog.setTitle("Thông báo!"); + alertDialog.show(); + } + + private void openSettings() { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null); + intent.setData(uri); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + + private void startLocationUpdates() { + mSettingsClient.checkLocationSettings(mLocationSettingsRequest).addOnSuccessListener(this, new OnSuccessListener() { + @SuppressLint("MissingPermission") + @Override + public void onSuccess(LocationSettingsResponse locationSettingsResponse) { + mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallBack, Looper.myLooper()); + + } + }).addOnFailureListener(this, new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + int statusCode = ((ApiException) e).getStatusCode(); + switch (statusCode) { + case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: + Log.i(TAG, "Location settings are not satisfied. Attempting to upgrade location settings"); + + try { + ResolvableApiException rae = (ResolvableApiException) e; + rae.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS); + } catch (IntentSender.SendIntentException sie) { + Log.i(TAG, "PendingIntent unable to execute request"); + } + break; + case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: + String errorMessage = "Location settings are inadequate, and cannot be fixed here. Fix in Settings"; + Log.e(TAG, errorMessage); + + Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_SHORT).show(); + } + } + }); + } + + private void stopLocationUpdates() { + mFusedLocationClient.removeLocationUpdates(mLocationCallBack).addOnCompleteListener(this, task -> Log.d(TAG, "Location updates stopped!")); + } + + private boolean checkPermission() { + int permissionState = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION); + return permissionState == PackageManager.PERMISSION_GRANTED; + } + + @Override + protected void onResume() { + super.onResume(); + if (mRequestingLocationUpdates && checkPermission() && Common.CURRENT_LOCATION == null) { + startLocationUpdates(); + } + } + + @Override + protected void onPause() { + super.onPause(); + if (mRequestingLocationUpdates) { + stopLocationUpdates(); + + if(Common.CURRENT_LOCATION == null){ + if(mCurrentLocation != null) + Common.CURRENT_LOCATION = mCurrentLocation; + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java b/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java index 8a86a8e..88e6868 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Activity/OrderDetailActivity.java @@ -1,36 +1,255 @@ package com.capstone.foodify.shipper.Activity; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.PendingIntent; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; +import android.util.Log; import android.view.View; import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; +import com.capstone.foodify.shipper.API.GoogleMapApi; +import com.capstone.foodify.shipper.Adapter.OrderDetailAdapter; +import com.capstone.foodify.shipper.Common; +import com.capstone.foodify.shipper.GoogleMap.GeofenceHelper; +import com.capstone.foodify.shipper.Model.GoogleMap.GoogleMapResponse; +import com.capstone.foodify.shipper.Model.Order; import com.capstone.foodify.shipper.R; +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofencingClient; +import com.google.android.gms.location.GeofencingRequest; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.thecode.aestheticdialogs.AestheticDialog; +import com.thecode.aestheticdialogs.DialogAnimation; +import com.thecode.aestheticdialogs.DialogStyle; +import com.thecode.aestheticdialogs.DialogType; + +import java.text.DecimalFormat; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; public class OrderDetailActivity extends AppCompatActivity { - Button btn_shipping; + private static final String TAG = "OrderDetailActivity"; + private static final int REQUEST_PHONE_CALL = 1; + Order order; + Button btn_shipping, btn_call, btn_confirm_ship_completed; + TextView order_tracking_number, txt_user_name, txt_phone, txt_address, txt_distance, txt_status, txt_total, + txt_order_time; + String status = ""; + ConstraintLayout progressLayout; + RecyclerView rcv_list_order; + OrderDetailAdapter adapter; + LinearLayout layoutConfirmOrder; + private GeofencingClient geofencingClient; + private GeofenceHelper geofenceHelper; + private float GEOFENCE_RADIUS = 100; + private String GEOFENCE_ID = "SOME_GEOFENCE_ID"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_order_detail); - btn_shipping = findViewById(R.id.btn_shipping); + //Get information order + if(getIntent() != null){ + order = (Order) getIntent().getSerializableExtra("order"); + status = getIntent().getStringExtra("status"); + } + + + initComponent(); + initData(); + + //Check status shipping + if(status != null){ + btn_shipping.setVisibility(View.GONE); + layoutConfirmOrder.setVisibility(View.VISIBLE); + } + + getDistanceAndCalculateShipCost(order.getAddress()); + + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false); + rcv_list_order.setLayoutManager(linearLayoutManager); + + adapter.setData(order.getOrderDetails()); + rcv_list_order.setAdapter(adapter); btn_shipping.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_VIEW, - Uri.parse("google.navigation:q=16.0230,108.2498&mode=l")); - intent.setPackage("com.google.android.apps.maps"); - startActivity(intent); + if(Common.CURRENT_ORDER == null){ + LatLng latLng = new LatLng(order.getLat(), order.getLng()); + addGeofence(latLng, GEOFENCE_RADIUS); + + Common.CURRENT_ORDER = order; + + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("google.navigation:q="+ order.getLat() + "," + order.getLng() + "&mode=l")); + intent.setPackage("com.google.android.apps.maps"); + startActivity(intent); + } } }); + btn_call.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + order.getUser().getPhoneNumber())); + startActivity(intent); + } + }); + + //Register geofence + geofencingClient = LocationServices.getGeofencingClient(this); + geofenceHelper = new GeofenceHelper(this); + } + private void initComponent(){ + adapter = new OrderDetailAdapter(); + + btn_shipping = findViewById(R.id.btn_shipping); + btn_call = findViewById(R.id.btn_call); + btn_confirm_ship_completed = findViewById(R.id.btn_confirm_ship_complete); + + txt_address = findViewById(R.id.txt_address); + order_tracking_number = findViewById(R.id.order_tracking_number); + txt_user_name = findViewById(R.id.txt_userName); + txt_phone = findViewById(R.id.txt_phone); + txt_distance = findViewById(R.id.txt_distance); + txt_status = findViewById(R.id.txt_status); + txt_total = findViewById(R.id.txt_total); + txt_order_time = findViewById(R.id.txt_order_time); + + rcv_list_order = findViewById(R.id.list_order); + + layoutConfirmOrder = findViewById(R.id.layout1); + + progressLayout = findViewById(R.id.progress_layout); + } + + private void initData(){ + txt_user_name.setText(order.getUser().getFullName()); + txt_phone.setText(order.getUser().getPhoneNumber()); + txt_address.setText(order.getAddress()); + txt_status.setText(translateStatus(order.getStatus())); + txt_total.setText(Common.changeCurrencyUnit(order.getTotal())); + txt_order_time.setText(order.getOrderTime()); + } + + private String translateStatus(String status){ + String statusHasTranslate = null; + + if(status.equals("AWAITING")){ + statusHasTranslate = "Chờ xác nhận"; + } + + if(status.equals("CONFIRMED")){ + statusHasTranslate = "Đã xác nhận"; + } + + if(status.equals("SHIPPING")){ + statusHasTranslate = "Đang giao"; + } + + if(status.equals("COMPLETED")){ + statusHasTranslate = "Đã giao"; + } + + if(status.equals("CANCELED")){ + statusHasTranslate = "Đã huỷ"; + } + + return statusHasTranslate; + } + @SuppressLint("MissingPermission") + private void addGeofence(LatLng latLng, float radius) { + + Geofence geofence = geofenceHelper.getGeofence(GEOFENCE_ID, latLng, radius, Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_DWELL | Geofence.GEOFENCE_TRANSITION_EXIT); + GeofencingRequest geofencingRequest = geofenceHelper.getGeofencingRequest(geofence); + PendingIntent pendingIntent = geofenceHelper.getPendingIntent(); + + geofencingClient.addGeofences(geofencingRequest, pendingIntent) + .addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(Void aVoid) { + Log.d(TAG, "onSuccess: Geofence Added..."); + } + }) + .addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + String errorMessage = geofenceHelper.getErrorString(e); + Log.d(TAG, "onFailure: " + errorMessage); + } + }); + } + + private void getDistanceAndCalculateShipCost(String address) { + //Get location from address + GoogleMapApi.apiService.getGeoCode(address, Common.MAP_API).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + GoogleMapResponse jsonResponse = response.body(); + + Double lat = jsonResponse.getResults().get(0).getGeometry().getLocation().getLat(); + Double lng = jsonResponse.getResults().get(0).getGeometry().getLocation().getLng(); + + LatLng addressFrom = new LatLng(Common.CURRENT_LOCATION.getLatitude(), Common.CURRENT_LOCATION.getLongitude()); + LatLng addressTo = new LatLng(lat, lng); + + int distance = CalculationByDistance(addressFrom, addressTo); + + txt_distance.setText(distance + " km"); + progressLayout.setVisibility(View.GONE); + } + + @Override + public void onFailure(Call call, Throwable t) { + Toast.makeText(OrderDetailActivity.this, Common.ERROR_CONNECT_SERVER, Toast.LENGTH_SHORT).show(); + } + }); } + + public int CalculationByDistance(LatLng from, LatLng to) { + int Radius = 6371;// radius of earth in Km + double lat1 = from.latitude; + double lat2 = to.latitude; + double lon1 = from.longitude; + double lon2 = to.longitude; + double dLat = Math.toRadians(lat2 - lat1); + double dLon = Math.toRadians(lon2 - lon1); + double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(Math.toRadians(lat1)) + * Math.cos(Math.toRadians(lat2)) * Math.sin(dLon / 2) + * Math.sin(dLon / 2); + double c = 2 * Math.asin(Math.sqrt(a)); + double valueResult = Radius * c; + double km = valueResult / 1; + DecimalFormat newFormat = new DecimalFormat("####"); + + return Integer.valueOf(newFormat.format(km)); + } + } \ No newline at end of file diff --git a/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java b/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java index f292771..0881bb9 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Activity/SignInActivity.java @@ -14,8 +14,10 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.splashscreen.SplashScreen; +import com.capstone.foodify.shipper.API.FoodApi; import com.capstone.foodify.shipper.API.FoodApiToken; import com.capstone.foodify.shipper.Common; +import com.capstone.foodify.shipper.Model.Shipper; import com.capstone.foodify.shipper.Model.User; import com.capstone.foodify.shipper.R; import com.google.android.gms.tasks.OnCompleteListener; @@ -59,35 +61,8 @@ public class SignInActivity extends AppCompatActivity { if (user != null && Common.CURRENT_USER == null) { progressLayout.setVisibility(View.VISIBLE); - user.getIdToken(true) - .addOnCompleteListener(new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (task.isSuccessful()) { - Common.TOKEN = task.getResult().getToken(); - //Get user from database - FoodApiToken.apiService.getUserFromEmail(user.getEmail()).enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - User userData = response.body(); - if (userData != null) { - Common.CURRENT_USER = userData; - startActivity(new Intent(SignInActivity.this, MainActivity.class)); - - progressLayout.setVisibility(View.GONE); - } - } - - @Override - public void onFailure(Call call, Throwable t) { - } - }); - } else { - Toast.makeText(SignInActivity.this, "Error when taking token!", Toast.LENGTH_SHORT).show(); - } - } - }); + getInformationAndSignInUser(user); } } @@ -107,10 +82,6 @@ public class SignInActivity extends AppCompatActivity { //Init Component initComponent(); setFontUI(); - - if(Common.CURRENT_USER != null) - startActivity(new Intent(this, MainActivity.class)); - btn_sign_in.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -173,66 +144,7 @@ public class SignInActivity extends AppCompatActivity { // Sign in success, update UI with the signed-in user's information FirebaseUser user = mAuth.getCurrentUser(); - //Save user -// Paper.book().write("user", user); - - user.getIdToken(true) - .addOnCompleteListener(new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if(task.isSuccessful()){ - Common.TOKEN = task.getResult().getToken(); - - //Get user from database - FoodApiToken.apiService.getUserFromEmail(user.getEmail()).enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - if(response.code() == 200){ - - User userData = response.body(); - if(userData != null){ - - //Check user is lock or not - if(!userData.isLocked()){ - - //Check role account - if(userData.getRole().getRoleName().equals("ROLE_USER")){ - Common.CURRENT_USER = userData; - - //Dismiss progress bar - progressLayout.setVisibility(View.GONE); - - startActivity(new Intent(SignInActivity.this, MainActivity.class)); - finish(); - } else { - Toast.makeText(SignInActivity.this, "Tài khoản không hợp lệ! Vui lòng kiểm tra lại!", Toast.LENGTH_SHORT).show(); - FirebaseAuth.getInstance().signOut(); - - //Dismiss progress bar - progressLayout.setVisibility(View.GONE); - } - } else { - Toast.makeText(SignInActivity.this, "Tài khoản của bạn đã bị khoá!", Toast.LENGTH_SHORT).show(); - FirebaseAuth.getInstance().signOut(); - - //Dismiss progress bar - progressLayout.setVisibility(View.GONE); - } - } - } - } - - @Override - public void onFailure(Call call, Throwable t) { - System.out.println("ERROR: " + t); - Common.showErrorServerNotification(SignInActivity.this, "Không thể đăng nhập tài khoản! Vui lòng thử lại sau!"); - } - }); - } else { - Toast.makeText(SignInActivity.this, "Error when taking token!", Toast.LENGTH_SHORT).show(); - } - } - }); + getInformationAndSignInUser(user); } else { // If sign in fails, display a message to the user. Toast.makeText(SignInActivity.this, "Thông tin đăng nhập không đúng! Vui lòng kiểm tra và thử lại!", @@ -245,6 +157,66 @@ public class SignInActivity extends AppCompatActivity { }); } + private void getInformationAndSignInUser(FirebaseUser user) { + user.getIdToken(true) + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if(task.isSuccessful()){ + Common.TOKEN = task.getResult().getToken(); + + //Get user from database + FoodApiToken.apiService.getUserFromEmail(user.getEmail()).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if(response.code() == 200){ + + User userData = response.body(); + if(userData != null){ + + //Check user is lock or not + if(!userData.isLocked()){ + + //Check role account + if(userData.getRole().getRoleName().equals("ROLE_SHIPPER")){ + Common.CURRENT_USER = userData; + + //Get shipper id + getShipperId(userData.getId()); + + startActivity(new Intent(SignInActivity.this, MainActivity.class)); + finish(); + } else { + Toast.makeText(SignInActivity.this, "Tài khoản không hợp lệ! Vui lòng kiểm tra lại!", Toast.LENGTH_SHORT).show(); + FirebaseAuth.getInstance().signOut(); + + //Dismiss progress bar + progressLayout.setVisibility(View.GONE); + } + } else { + Toast.makeText(SignInActivity.this, "Tài khoản của bạn đã bị khoá!", Toast.LENGTH_SHORT).show(); + FirebaseAuth.getInstance().signOut(); + + //Dismiss progress bar + progressLayout.setVisibility(View.GONE); + } + } + } + } + + @Override + public void onFailure(Call call, Throwable t) { + System.out.println("ERROR: " + t); + Common.showErrorServerNotification(SignInActivity.this, "Không thể đăng nhập tài khoản! Vui lòng thử lại sau!"); + } + }); + } else { + Toast.makeText(SignInActivity.this, "Error when taking token!", Toast.LENGTH_SHORT).show(); + } + } + }); + } + private void initData(){ email = edt_email.getText().toString(); password = edt_password.getText().toString(); @@ -300,4 +272,22 @@ public class SignInActivity extends AppCompatActivity { return dataHasValidate; } + + private void getShipperId(int userId){ + FoodApi.apiService.getShipper(userId).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if(response.code() == 200){ + Common.CURRENT_SHIPPER = response.body(); + //Dismiss progress bar + progressLayout.setVisibility(View.GONE); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Toast.makeText(SignInActivity.this, Common.ERROR_CONNECT_SERVER, Toast.LENGTH_SHORT).show(); + } + }); + } } \ No newline at end of file diff --git a/app/src/main/java/com/capstone/foodify/shipper/Adapter/OrderAdapter.java b/app/src/main/java/com/capstone/foodify/shipper/Adapter/OrderAdapter.java index d824cf6..3be2259 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Adapter/OrderAdapter.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Adapter/OrderAdapter.java @@ -1,5 +1,6 @@ package com.capstone.foodify.shipper.Adapter; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.view.LayoutInflater; @@ -11,6 +12,7 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.capstone.foodify.shipper.Activity.OrderDetailActivity; +import com.capstone.foodify.shipper.Common; import com.capstone.foodify.shipper.Model.Order; import com.capstone.foodify.shipper.R; @@ -21,12 +23,14 @@ import java.util.List; public class OrderAdapter extends RecyclerView.Adapter{ public List listOrders; - - public OrderAdapter() { + private Activity activity; + public OrderAdapter(Activity activity) { + this.activity = activity; } public void setData(List listOrders){ this.listOrders = listOrders; + notifyDataSetChanged(); } @NonNull @@ -40,18 +44,24 @@ public class OrderAdapter extends RecyclerView.Adapter{ + + public List listOrder; + + public OrderDetailAdapter(){} + + public void setData(List listOrder){ + this.listOrder = listOrder; + } + + + @NonNull + @Override + public OrderDetailViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_order_detail, parent, false); + return new OrderDetailViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull OrderDetailViewHolder holder, int position) { + OrderDetail order = listOrder.get(position); + + if(order == null) + return; + + if(order.getFood().getImages().size() == 0){ + Picasso.get().load(R.drawable.default_image_food).into(holder.image_view); + } else { + Picasso.get().load(order.getFood().getImages().get(0).getImageUrl()).into(holder.image_view); + } + + holder.shop_name.setText(order.getFood().getShop().getName()); + holder.quantity.setText("Số lượng: " + order.getQuantity()); + + holder.food_name.setText(order.getFood().getName()); + + holder.shop_name.setText(order.getFood().getShop().getName()); + + //Check value discountPercent + float cost = 0; + if(order.getFood().getDiscountPercent() > 0){ + + //Calculate final cost when apply discountPercent + cost = order.getFood().getCost() - (order.getFood().getCost() * order.getFood().getDiscountPercent()/100); + + //Show discountPercent value on screen + holder.discount.setVisibility(View.VISIBLE); + holder.discount.setText("-" + order.getFood().getDiscountPercent()+ "%"); + } else { + cost = order.getFood().getCost(); + } + + holder.price.setText(Common.changeCurrencyUnit(cost)); + } + + @Override + public int getItemCount() { + if(listOrder != null) + return listOrder.size(); + return 0; + } + + public class OrderDetailViewHolder extends RecyclerView.ViewHolder{ + ImageView image_view; + TextView food_name, shop_name, price, quantity, discount; + + public OrderDetailViewHolder(@NonNull View itemView) { + super(itemView); + + //Init Component + image_view = itemView.findViewById(R.id.image_view); + food_name = itemView.findViewById(R.id.food_name_text_view); + shop_name = itemView.findViewById(R.id.shop_name_text_view); + price = itemView.findViewById(R.id.price_text_view); + quantity = itemView.findViewById(R.id.quantity_text_view); + discount = itemView.findViewById(R.id.discount); + } + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/Common.java b/app/src/main/java/com/capstone/foodify/shipper/Common.java index 618418d..78f76d8 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Common.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Common.java @@ -7,23 +7,34 @@ import android.location.Location; import androidx.annotation.NonNull; +import com.capstone.foodify.shipper.Model.Order; +import com.capstone.foodify.shipper.Model.Shipper; import com.capstone.foodify.shipper.Model.User; import com.thecode.aestheticdialogs.AestheticDialog; import com.thecode.aestheticdialogs.DialogStyle; import com.thecode.aestheticdialogs.DialogType; import com.thecode.aestheticdialogs.OnDialogClickListener; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + public class Common { public static User CURRENT_USER = null; + public static Shipper CURRENT_SHIPPER = null; + public static Order CURRENT_ORDER = null; public static String TOKEN = null; public static Location CURRENT_LOCATION = null; public static final String MAP_API = "AIzaSyAY14Ic32UP26Hg6GILznOfbBihiY5BUxw"; - public static final String IS_FORGOT_PASSWORD = "isForgotPassword"; public static final String FORMAT_DATE="dd-MM-yyyy"; public static final String VALID_EMAIL_ADDRESS_REGEX = "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$"; public static final String PHONE_CODE = "+84"; public static final String PASSWORD_PATTERN = "^(?=.*[0-9])(?=.*[A-Z])(?=.*[@#$%^&+=!_])(?=\\S+$).{4,}$"; public static final String PHONE_PATTERN = "^0[98753]{1}\\d{8}$"; + public static final String ERROR_CONNECT_SERVER = "Đã có lỗi kết nối đến hệ thống!"; + public static final List LIST_ORDER = new ArrayList<>(); + public static final String SHIPPING_COMPLETED = "1"; public static Typeface setFontBebas(AssetManager assetManager){ return Typeface.createFromAsset(assetManager, "font/bebas.ttf"); @@ -48,4 +59,10 @@ public class Common { }).show(); } + public static String changeCurrencyUnit(float price){ + Locale locale = new Locale("vi", "VN"); + NumberFormat fmt = NumberFormat.getCurrencyInstance(locale); + return fmt.format(price); + } + } diff --git a/app/src/main/java/com/capstone/foodify/shipper/Fragment/OrderFragment.java b/app/src/main/java/com/capstone/foodify/shipper/Fragment/OrderFragment.java index 0fb934c..fbaa18d 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Fragment/OrderFragment.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Fragment/OrderFragment.java @@ -2,6 +2,8 @@ package com.capstone.foodify.shipper.Fragment; import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.core.widget.NestedScrollView; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -9,23 +11,38 @@ import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; +import com.capstone.foodify.shipper.API.FoodApiToken; import com.capstone.foodify.shipper.Adapter.OrderAdapter; import com.capstone.foodify.shipper.Common; import com.capstone.foodify.shipper.Model.Order; +import com.capstone.foodify.shipper.Model.Response.Orders; import com.capstone.foodify.shipper.R; import com.squareup.picasso.Picasso; import java.util.ArrayList; import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + public class OrderFragment extends Fragment { + private static int CURRENT_PAGE = 0; + private static int PAGE_SIZE = 8; + private static String SORT_BY = "id"; + private static String SORT_DIR = "asc"; + private static boolean LAST_PAGE = false; private List listOrders = new ArrayList<>(); RecyclerView recyclerView; OrderAdapter orderAdapter; - TextView welcome_text; + TextView welcome_text, end_of_list_text_view; + NestedScrollView list_order_layout; + ProgressBar progressBar; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -36,27 +53,77 @@ public class OrderFragment extends Fragment { //Init Component recyclerView = view.findViewById(R.id.rcv_list_order); welcome_text = view.findViewById(R.id.welcome_text); + list_order_layout = view.findViewById(R.id.list_order_layout); + progressBar = view.findViewById(R.id.progress_bar); + end_of_list_text_view = view.findViewById(R.id.end_of_list_text); if(Common.CURRENT_USER != null){ welcome_text.setText("Xin chào, " + Common.CURRENT_USER.getFullName() + "!"); } - orderAdapter = new OrderAdapter(); + orderAdapter = new OrderAdapter(getActivity()); + + getListOrder(); - for(int i = 0; i < 5; i++){ - int b = (int)(Math.random()*(999999-100000+1)+100000); - listOrders.add(new Order(String.valueOf(b))); - } LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false); recyclerView.setLayoutManager(linearLayoutManager); - orderAdapter.setData(listOrders); recyclerView.setAdapter(orderAdapter); + if (list_order_layout != null) { + list_order_layout.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { + @Override + public void onScrollChange(@NonNull NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { + if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) { + //When user scroll to bottom, load more data + dataLoadMore(); + } + } + }); + } + return view; } + private void getListOrder(){ + if(Common.CURRENT_SHIPPER != null){ + FoodApiToken.apiService.getListOrder(Common.CURRENT_SHIPPER.getId(), CURRENT_PAGE++, PAGE_SIZE, SORT_BY, SORT_DIR).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if(response.code() == 200){ + listOrders.addAll(response.body().getOrders()); + orderAdapter.setData(listOrders); + + LAST_PAGE = response.body().getPage().isLast(); + + if(LAST_PAGE) + hideProgressBarAndShowEndOfListText(); + } else { + Toast.makeText(getContext(), "Đã có lỗi hệ thống! Mã lỗi: " + response.code(), Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Toast.makeText(getContext(), Common.ERROR_CONNECT_SERVER, Toast.LENGTH_SHORT).show(); + } + }); + } + } + + private void hideProgressBarAndShowEndOfListText(){ + progressBar.setVisibility(View.GONE); + end_of_list_text_view.setVisibility(View.VISIBLE); + } + + private void dataLoadMore() { + if(!LAST_PAGE){ + getListOrder(); + } else { + hideProgressBarAndShowEndOfListText(); + } + } @Override public void onResume() { super.onResume(); @@ -64,4 +131,5 @@ public class OrderFragment extends Fragment { //Update user name on textview welcome_text.setText("Xin chào, " + Common.CURRENT_USER.getFullName() + "!"); } + } \ No newline at end of file diff --git a/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java new file mode 100644 index 0000000..d691ae8 --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java @@ -0,0 +1,48 @@ +package com.capstone.foodify.shipper.GoogleMap; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.widget.Toast; + +import com.capstone.foodify.shipper.Activity.OrderDetailActivity; +import com.capstone.foodify.shipper.Common; +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofencingEvent; + +import java.util.List; + +public class GeofenceBroadcastReceiver extends BroadcastReceiver { + + private static final String TAG = "GeofenceBroadcastReceive"; + + @Override + public void onReceive(Context context, Intent intent) { + + GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); + + if (geofencingEvent.hasError()) { + Log.d(TAG, "onReceive: Error receiving geofence event..."); + return; + } + + List geofenceList = geofencingEvent.getTriggeringGeofences(); + for (Geofence geofence: geofenceList) { + Log.d(TAG, "onReceive: " + geofence.getRequestId()); + } + + int transitionType = geofencingEvent.getGeofenceTransition(); + + switch (transitionType) { + case Geofence.GEOFENCE_TRANSITION_ENTER: + Intent newIntent = new Intent(context.getApplicationContext(), OrderDetailActivity.class); + newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + newIntent.putExtra("order", Common.CURRENT_ORDER); + newIntent.putExtra("status", Common.SHIPPING_COMPLETED); + context.startActivity(newIntent); + break; + } + + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceHelper.java b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceHelper.java new file mode 100644 index 0000000..00504c9 --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceHelper.java @@ -0,0 +1,64 @@ +package com.capstone.foodify.shipper.GoogleMap; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; + +import com.google.android.gms.common.api.ApiException; +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofenceStatusCodes; +import com.google.android.gms.location.GeofencingRequest; +import com.google.android.gms.maps.model.LatLng; + +public class GeofenceHelper extends ContextWrapper { + PendingIntent pendingIntent; + public GeofenceHelper(Context base) { + super(base); + } + + public GeofencingRequest getGeofencingRequest(Geofence geofence) { + return new GeofencingRequest.Builder() + .addGeofence(geofence) + .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER) + .build(); + } + + public Geofence getGeofence(String ID, LatLng latLng, float radius, int transitionTypes) { + return new Geofence.Builder() + .setCircularRegion(latLng.latitude, latLng.longitude, radius) + .setRequestId(ID) + .setTransitionTypes(transitionTypes) + .setLoiteringDelay(5000) + .setExpirationDuration(Geofence.NEVER_EXPIRE) + .build(); + } + + public PendingIntent getPendingIntent() { + if (pendingIntent != null) { + return pendingIntent; + } + Intent intent = new Intent(this, GeofenceBroadcastReceiver.class); + pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + return pendingIntent; + } + + public String getErrorString(Exception e) { + if (e instanceof ApiException) { + ApiException apiException = (ApiException) e; + switch (apiException.getStatusCode()) { + case GeofenceStatusCodes + .GEOFENCE_NOT_AVAILABLE: + return "GEOFENCE_NOT_AVAILABLE"; + case GeofenceStatusCodes + .GEOFENCE_TOO_MANY_GEOFENCES: + return "GEOFENCE_TOO_MANY_GEOFENCES"; + case GeofenceStatusCodes + .GEOFENCE_TOO_MANY_PENDING_INTENTS: + return "GEOFENCE_TOO_MANY_PENDING_INTENTS"; + } + } + return e.getLocalizedMessage(); + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/NotificationHelper.java b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/NotificationHelper.java new file mode 100644 index 0000000..f94844f --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/GoogleMap/NotificationHelper.java @@ -0,0 +1,47 @@ +package com.capstone.foodify.shipper.GoogleMap; + +import android.annotation.SuppressLint; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.graphics.Color; +import android.os.Build; + +import androidx.annotation.RequiresApi; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import com.capstone.foodify.shipper.R; + +import java.util.Random; + +public class NotificationHelper extends ContextWrapper { + + public NotificationHelper(Context base) { + super(base); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createChannels(); + } + } + + private String CHANNEL_NAME = "High priority channel"; + private String CHANNEL_ID = "com.example.notifications" + CHANNEL_NAME; + + @RequiresApi(api = Build.VERSION_CODES.O) + private void createChannels() { + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH); + notificationChannel.enableLights(true); + notificationChannel.enableVibration(true); + notificationChannel.setDescription("this is the description of the channel."); + notificationChannel.setLightColor(Color.RED); + notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + manager.createNotificationChannel(notificationChannel); + } + + +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/Food.java b/app/src/main/java/com/capstone/foodify/shipper/Model/Food.java new file mode 100644 index 0000000..7ada228 --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/Food.java @@ -0,0 +1,116 @@ +package com.capstone.foodify.shipper.Model; + +import java.io.Serializable; +import java.util.List; + +public class Food implements Comparable, Serializable { + private String id; + private String name; + private String description; + private boolean isEnabled; + private float discountPercent; + private float cost; + private float averageRating; + private String reviewCount; + private Shop shop; + private int sold; + private List images; + public Food() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + 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 boolean isEnabled() { + return isEnabled; + } + + public void setEnabled(boolean enabled) { + isEnabled = enabled; + } + + public float getDiscountPercent() { + return discountPercent; + } + + public void setDiscountPercent(float discountPercent) { + this.discountPercent = discountPercent; + } + + public float getCost() { + return cost; + } + + public void setCost(float cost) { + this.cost = cost; + } + + public float getAverageRating() { + return averageRating; + } + + public void setAverageRating(float averageRating) { + this.averageRating = averageRating; + } + + public String getReviewCount() { + return reviewCount; + } + + public void setReviewCount(String reviewCount) { + this.reviewCount = reviewCount; + } + + public Shop getShop() { + return shop; + } + + public void setShop(Shop shop) { + this.shop = shop; + } + + public int getSold() { + return sold; + } + + public void setSold(int sold) { + this.sold = sold; + } + + public List getImages() { + return images; + } + + public void setImages(List images) { + this.images = images; + } + + @Override + public int compareTo(Food food) { + if(getName().isEmpty() || food.getName().isEmpty()){ + return 0; + } + return getName().compareTo(food.getName()); + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/Image.java b/app/src/main/java/com/capstone/foodify/shipper/Model/Image.java new file mode 100644 index 0000000..b39e970 --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/Image.java @@ -0,0 +1,33 @@ +package com.capstone.foodify.shipper.Model; + +import java.io.Serializable; + +public class Image implements Serializable { + private int id; + private String imageUrl; + private int productId; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public int getProductId() { + return productId; + } + + public void setProductId(int productId) { + this.productId = productId; + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/Order.java b/app/src/main/java/com/capstone/foodify/shipper/Model/Order.java index 61cb33a..204f7f4 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Model/Order.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/Order.java @@ -1,27 +1,23 @@ package com.capstone.foodify.shipper.Model; +import java.io.Serializable; import java.util.List; -public class Order { +public class Order implements Serializable { private int id; private String orderTrackingNumber; - private int shipperId; private String paymentMethod; private float shippingCost; + private float productCost; private String status; private String address; + private User user; + private Shipper shipper; private List orderDetails; - - public Order(String orderTrackingNumber, int shipperId, String paymentMethod, float shippingCost, String status, String address, List orderDetails) { - this.orderTrackingNumber = orderTrackingNumber; - this.shipperId = shipperId; - this.paymentMethod = paymentMethod; - this.shippingCost = shippingCost; - this.status = status; - this.address = address; - this.orderDetails = orderDetails; - } - + private String orderTime; + private float total; + private double lat; + private double lng; public int getId() { return id; } @@ -41,14 +37,6 @@ public class Order { this.orderTrackingNumber = orderTrackingNumber; } - public int getShipperId() { - return shipperId; - } - - public void setShipperId(int shipperId) { - this.shipperId = shipperId; - } - public String getPaymentMethod() { return paymentMethod; } @@ -96,4 +84,60 @@ public class Order { public void setListOrderDetail(List listOrderDetail) { this.orderDetails = listOrderDetail; } + + public float getProductCost() { + return productCost; + } + + public void setProductCost(float productCost) { + this.productCost = productCost; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Shipper getShipper() { + return shipper; + } + + public void setShipper(Shipper shipper) { + this.shipper = shipper; + } + + public String getOrderTime() { + return orderTime; + } + + public void setOrderTime(String orderTime) { + this.orderTime = orderTime; + } + + public float getTotal() { + return total; + } + + public void setTotal(float total) { + this.total = total; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public double getLng() { + return lng; + } + + public void setLng(double lng) { + this.lng = lng; + } } diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/OrderDetail.java b/app/src/main/java/com/capstone/foodify/shipper/Model/OrderDetail.java index 0284f97..f1907f4 100644 --- a/app/src/main/java/com/capstone/foodify/shipper/Model/OrderDetail.java +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/OrderDetail.java @@ -1,11 +1,15 @@ package com.capstone.foodify.shipper.Model; -public class OrderDetail { +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; + +public class OrderDetail implements Serializable { private int id; - private int orderId; - private int productId; + @SerializedName("product") + private Food food; private String quantity; - private float subTotal; + private float subtotal; public int getId() { return id; @@ -15,22 +19,6 @@ public class OrderDetail { this.id = id; } - public int getOrderId() { - return orderId; - } - - public void setOrderId(int orderId) { - this.orderId = orderId; - } - - public int getProductId() { - return productId; - } - - public void setProductId(int productId) { - this.productId = productId; - } - public String getQuantity() { return quantity; } @@ -39,11 +27,19 @@ public class OrderDetail { this.quantity = quantity; } - public float getSubTotal() { - return subTotal; + public float getSubtotal() { + return subtotal; } - public void setSubTotal(float subTotal) { - this.subTotal = subTotal; + public void setSubtotal(float subtotal) { + this.subtotal = subtotal; + } + + public Food getFood() { + return food; + } + + public void setFood(Food food) { + this.food = food; } } diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/Page.java b/app/src/main/java/com/capstone/foodify/shipper/Model/Page.java new file mode 100644 index 0000000..1ac2aaa --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/Page.java @@ -0,0 +1,49 @@ +package com.capstone.foodify.shipper.Model; + +public class Page { + private int pageNo; + private int pageSize; + private int totalElements; + private int totalPages; + private boolean last; + + public int getPageNo() { + return pageNo; + } + + public void setPageNo(int pageNo) { + this.pageNo = pageNo; + } + + public int getPageSize() { + return pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public int getTotalElements() { + return totalElements; + } + + public void setTotalElements(int totalElements) { + this.totalElements = totalElements; + } + + public int getTotalPages() { + return totalPages; + } + + public void setTotalPages(int totalPages) { + this.totalPages = totalPages; + } + + public boolean isLast() { + return last; + } + + public void setLast(boolean last) { + this.last = last; + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/Response/Orders.java b/app/src/main/java/com/capstone/foodify/shipper/Model/Response/Orders.java new file mode 100644 index 0000000..6ccd0e9 --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/Response/Orders.java @@ -0,0 +1,27 @@ +package com.capstone.foodify.shipper.Model.Response; + +import com.capstone.foodify.shipper.Model.Order; +import com.capstone.foodify.shipper.Model.Page; + +import java.util.List; + +public class Orders { + private List orders; + private Page page; + + public List getOrders() { + return orders; + } + + public void setOrders(List orders) { + this.orders = orders; + } + + public Page getPage() { + return page; + } + + public void setPage(Page page) { + this.page = page; + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/Shipper.java b/app/src/main/java/com/capstone/foodify/shipper/Model/Shipper.java new file mode 100644 index 0000000..c66af38 --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/Shipper.java @@ -0,0 +1,44 @@ +package com.capstone.foodify.shipper.Model; + +import java.io.Serializable; +import java.util.List; + +public class Shipper implements Serializable { + private int id; + private User user; + private Shop shop; + + private List orders; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Shop getShop() { + return shop; + } + + public void setShop(Shop shop) { + this.shop = shop; + } + + public List getOrders() { + return orders; + } + + public void setOrders(List orders) { + this.orders = orders; + } +} diff --git a/app/src/main/java/com/capstone/foodify/shipper/Model/Shop.java b/app/src/main/java/com/capstone/foodify/shipper/Model/Shop.java new file mode 100644 index 0000000..7accd49 --- /dev/null +++ b/app/src/main/java/com/capstone/foodify/shipper/Model/Shop.java @@ -0,0 +1,87 @@ +package com.capstone.foodify.shipper.Model; + +import java.io.Serializable; + +public class Shop implements Serializable { + private int id; + private String name; + private String description; + private String imageUrl; + private boolean isEnabled; + private boolean isStudent; + private int userId; + private double lat; + private double lng; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + 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 getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public boolean isEnabled() { + return isEnabled; + } + + public void setEnabled(boolean enabled) { + isEnabled = enabled; + } + + public boolean isStudent() { + return isStudent; + } + + public void setStudent(boolean student) { + isStudent = student; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public double getLng() { + return lng; + } + + public void setLng(double lng) { + this.lng = lng; + } +} diff --git a/app/src/main/res/drawable/baseline_local_shipping_24.xml b/app/src/main/res/drawable/baseline_local_shipping_24.xml new file mode 100644 index 0000000..14c4c7b --- /dev/null +++ b/app/src/main/res/drawable/baseline_local_shipping_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_verified_24.xml b/app/src/main/res/drawable/baseline_verified_24.xml new file mode 100644 index 0000000..faafa97 --- /dev/null +++ b/app/src/main/res/drawable/baseline_verified_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/default_image_food.png b/app/src/main/res/drawable/default_image_food.png new file mode 100644 index 0000000..8cc5216 Binary files /dev/null and b/app/src/main/res/drawable/default_image_food.png differ diff --git a/app/src/main/res/layout/activity_order_detail.xml b/app/src/main/res/layout/activity_order_detail.xml index 005c0fe..10e98aa 100644 --- a/app/src/main/res/layout/activity_order_detail.xml +++ b/app/src/main/res/layout/activity_order_detail.xml @@ -28,7 +28,7 @@ app:layout_constraintTop_toTopOf="parent" /> @@ -64,7 +64,7 @@ + + + + + + + + + @@ -301,8 +344,28 @@ + + + android:drawableLeft="@drawable/baseline_phone_24" + android:drawableTint="@color/white" + /> + android:drawableLeft="@drawable/baseline_verified_24" + android:drawableTint="@color/white"/> @@ -357,7 +424,7 @@ android:background="#80000000" android:id="@+id/progress_layout" android:clickable="true" - android:visibility="gone" + android:visibility="visible" > + android:layout_height="match_parent" + android:id="@+id/list_order_layout" + > + + + + diff --git a/app/src/main/res/layout/item_order_detail.xml b/app/src/main/res/layout/item_order_detail.xml new file mode 100644 index 0000000..831ce33 --- /dev/null +++ b/app/src/main/res/layout/item_order_detail.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0d73617..8d0d2fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,4 +2,5 @@ Foodify Shipper Hello blank fragment + MapsActivity \ No newline at end of file diff --git a/build.gradle b/build.gradle index e1d8225..954ca80 100644 --- a/build.gradle +++ b/build.gradle @@ -6,4 +6,5 @@ buildscript { plugins { id 'com.android.application' version '7.4.2' apply false id 'com.android.library' version '7.4.2' apply false + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 3e927b1..ba32d98 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true +android.enableJetifier=true # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library