From 378ffde31f0b195eed14cc4be5ca57f945b3efa3 Mon Sep 17 00:00:00 2001 From: Nezumi <123229563+Nez27@users.noreply.github.com> Date: Wed, 5 Apr 2023 20:55:04 +0700 Subject: [PATCH] Add information order detail --- app/build.gradle | 17 +- app/src/main/AndroidManifest.xml | 14 +- .../capstone/foodify/shipper/API/FoodApi.java | 66 +---- .../foodify/shipper/API/FoodApiToken.java | 47 +--- .../shipper/Activity/MainActivity.java | 212 ++++++++++++++++ .../shipper/Activity/OrderDetailActivity.java | 231 +++++++++++++++++- .../shipper/Activity/SignInActivity.java | 174 +++++++------ .../foodify/shipper/Adapter/OrderAdapter.java | 37 +-- .../shipper/Adapter/OrderDetailAdapter.java | 97 ++++++++ .../com/capstone/foodify/shipper/Common.java | 19 +- .../shipper/Fragment/OrderFragment.java | 82 ++++++- .../GoogleMap/GeofenceBroadcastReceiver.java | 48 ++++ .../shipper/GoogleMap/GeofenceHelper.java | 64 +++++ .../shipper/GoogleMap/NotificationHelper.java | 47 ++++ .../capstone/foodify/shipper/Model/Food.java | 116 +++++++++ .../capstone/foodify/shipper/Model/Image.java | 33 +++ .../capstone/foodify/shipper/Model/Order.java | 86 +++++-- .../foodify/shipper/Model/OrderDetail.java | 44 ++-- .../capstone/foodify/shipper/Model/Page.java | 49 ++++ .../shipper/Model/Response/Orders.java | 27 ++ .../foodify/shipper/Model/Shipper.java | 44 ++++ .../capstone/foodify/shipper/Model/Shop.java | 87 +++++++ .../drawable/baseline_local_shipping_24.xml | 5 + .../res/drawable/baseline_verified_24.xml | 5 + .../main/res/drawable/default_image_food.png | Bin 0 -> 18037 bytes .../main/res/layout/activity_order_detail.xml | 95 +++++-- app/src/main/res/layout/fragment_order.xml | 28 ++- app/src/main/res/layout/item_order_detail.xml | 124 ++++++++++ app/src/main/res/values/strings.xml | 1 + build.gradle | 1 + gradle.properties | 1 + 31 files changed, 1609 insertions(+), 292 deletions(-) create mode 100644 app/src/main/java/com/capstone/foodify/shipper/Adapter/OrderDetailAdapter.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceBroadcastReceiver.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/GoogleMap/GeofenceHelper.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/GoogleMap/NotificationHelper.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/Model/Food.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/Model/Image.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/Model/Page.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/Model/Response/Orders.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/Model/Shipper.java create mode 100644 app/src/main/java/com/capstone/foodify/shipper/Model/Shop.java create mode 100644 app/src/main/res/drawable/baseline_local_shipping_24.xml create mode 100644 app/src/main/res/drawable/baseline_verified_24.xml create mode 100644 app/src/main/res/drawable/default_image_food.png create mode 100644 app/src/main/res/layout/item_order_detail.xml 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 0000000000000000000000000000000000000000..8cc5216403b00affa93fd1bb265e3240af42db88 GIT binary patch literal 18037 zcmeFZ_dnI|A3y#)_Q)|KTNIK}k#($eBH5vgY#}-J&X$msgu*eCl@*S?vLjo@F^;{m z$>wu;y+5Ddzkk5jEw|%5pV#xcuIJhuzFcXtCKrnzyzB#UFCkHU4MO<)Wp@?YREf>n!QC~_2X?7B<=i|x zCk#5mroBpeTuL5BPjx=txraAQ4RL9x%BxJzSXs0CZ^dL&vL9WL_)l?Nynt!Q?(`0Z zqy|bN`9dzn4E!IHHr_yf!@W{MD&P5bqcEPSP2f?$wu@w4-=eIfrt-q>LRV}5!R=yY ze*q?8+MKhJkeo^Vs>ro_tt}zd$JpS%d8d~cSnUuq~m6mf_@&3Sg_RnU= zg41yPJ6n91f0Er(D{PdaJq>Q@+0G5Z`pUJxgHs%wQ%~4>*J;tC=Vwfjls(C>hNP9R zTj7f{H|jz6;xo&GAy2|{#Q?bFI8<=k&;E8}4jicZf;6*df< z6@;c;BM*WFR`#OxE7t!|L!-_Yne0#drwtZ_FFZ!Z5K|Q&L!3^Ky*hkxMDdB01e{A` zdn`K1geJuPAJ%Q-??Zp_w?4d=;>bIA^sqAAiaf_~r*(ibtupPmkTWqy>MP`L26Z;3 zM7!PB1)nF}r<<{cbAMZfM+lzh!OeRnT>t!Qk2>J6yEw#Vn|Rnb-D7y>oaQE!WD@|b8k*mju;ws z-0#5_9}9^=p*c%uWc~+in-3<=w=2{qUzufi#}oV9B1j-}$o1;Dk)rLAUWw&Av z$BBqM;P@R~BgYm2=R;&{t?ODNT}!*$4}){_cWJYjcY+6EQ`cx>k12A2`r|Cl<6-Y~mlKZ?@5o5#x?Dx! z*~*W=C8v`iG+FQ$trxC-M1jxoysG3f6e+)FQGA5${m%2)HG7zJB)?*J^(Zm)Wb$uE zM;%Mt!!BwrO`SFSNQV4XKQ=x!pUG```(BH8ZKA8e6#n(DQhv)G4ca<(snx#@4~K%m z0qs#>po7^g03Vspc7nlyk2~$?0Lb{7UmtXQv@1D5fDN&z`R)r*?qW zeG^|uDKF2B8bXD920 ztH^TBr?At}G%fY)3Dpsa(ScjfeGd{w%lVdXK<9 zWEHy`JRn0@Z!P5=BSuXWZ}x@J(Z{65YwC9hS(fbYELuNKPCJCr+vX3UQJ<5O|MoKc z8d)IE+I*8iDZgKbqxKWSUTRt?GA*lT>D$h$== zD1Q(gtzQ3i7;6bjljBS13Q~;vw;5!$0drve- z9R4UELDRfGPUzG0(67iJkVe}4u9nq&c$T_=sLN?b|9QBr6=&+xbx@k2w~|2Bp@JRDamXt)+ixWxXww6)4ei}{q}+ZWct z{+r>KJ+|gK7lu~z4)0b>c@*3PmKK$jH?#e7NC+PN$uEb`@3+oD2eByrv3oPu!r77h zk)+OoZT^rQ-mbt07`}jo9f@!V6{DBlR$?iu2RV+% zwrBS?yi2^ltlt?L9*DA%DNT&I1>T2x=V(W1u62N6sKXHNg)M@ze|7mtqwc=(=i`K+ ztN}~xs~iOB)`hCTyQQWefT3a zX;p#jM)pFNNeHyQsT1d&eySke?~`%-z#+wVmCZ4@HaoZHf+dmHuz$ zNeZp>2SR`ErM~(3sBN<&dbtZiNIl_cg`^&V@wL?C`Xjj#k32AUX*w?(b#I(Bj~5F$ zbo4Io-KD0ednYJyPFT-S(YLG0EUs))+PrnCyTls#ybeC;cbq4fv-F@2hPL)>!Oy($ z2P_hkem}LIBMrI1T7Reb#C?H>r?-^#UA25P`GI}(0hsrX)y4q)5}-BW;SMxeG73Q% zW9TF(=lH|a&(B~pGTLg zSm=P99vau6rCnh72mj>GF0s0F61{v#5Lj^l|FqG)l-*Oz`c5)7)N|8Gb_xDjv?a#z zY{WCe9bdf5jV+^xbLnTy8{qAyXKG<(HhIql#ID{ahsr0x%q3;@j#p!{yyh^wRIWK} zBr>8#{C?(6PWN6TuDD)n-ITdd1Xl43jQre-=9pbmViBE#EaMZh_gI|rPTzCFKFEn( zR$tn6=IT8s1$_qR{Yp1Gl1^D}!2kFI9g@>!0ePF(-r0Ykv*4TjjFUh8xV-lqHZ4Bn zL`>Op6Rb~rmM=pgn|Cw*Q3cmp)oPrN#|jHKW12r)g+&{AgQS8M2a%UlE7 z+Pl%OKvm0GJ3p10nmq9mK~^4vvAE5!ko`@~@7P!u%6ZuquGL^MR71;xRWIvT3$(K9 z1S`V?K~k<1-sT&6Tekj6Bc9yax4iV`5zYnnCePC_+pvdwrI&8&g?4DvqX2ewvKG+7 zY2NF?kNwMNNa)`hJMsHx0#gif?G7`8YbwQ1U*c6V;q1>2b8e8L>YWEj`dp){dON-j zX}S}32(lx!me{SA5II!P--28*W7t4LZlgW&NJXvX+$c$RK{0$5!X!CkGSClG1n^(G z1Q!$>CvNjC>0b=rj8F)YqmVpsf0Z>QoF$dm{vX#Woul0Ox<>k*FZMwP4}p-j zP6(m@Go%h0=s<}?hKODa$VFYSx=Zkr51C#*hPg zogZ1|#&?XRja}%?vl(@x|IsD90jGx~3-O8JE$?C%gz!BgL8E~FM7`9W~z;3X3N zZH`!<7zKOx;*&$H*h_1t4fDGi_#C_x9j`#*{`%;i)f?jM20ecIlku}B^I94FP+4us z%F1>1pVMunmXVwpf*D>qT4?)EPqJWG%?=`oU_$>WCtRZ~r~W}bA+8luN~LLFh#C5H z;dL&#J#Q0%R5UP!GJD%NSl)EVjb>b&ECR?UdI8uSMXxtQ z;}_6H|2xU9hs#)EcsI8#aeCzSKuvnEpZf%m%jY(+Mj^ipxu-Z=o`G6VTQ9E;H+mac zLN7gt@2KQ5LpkwH5XzyOX&5XT~{ap3p7~2HTzA{ zn2niv&Sly#ijbpG`@U^PNmAM|4OTC-<9Xpm7^QEFFp--Wy}$p{;XZ&zJT*PDfIR1A z$$W;v*WMS1v^OOXfp)!aHLS50+w%Zf|BrmGb!U1U{$RZck4g?iBd>tdZ|f|`A?m^| z^-oi#2!e7pa=FG!O9@_09RrWBBDeazO;?v}+tdD-!AL%AZ|9QhY^&q^+&T4G@jDt+ zR6Yq}Sl{Z07!$xWdApM{6zYjM?0|u}e*MgII38fOtqoS~eR&MN#>u4s1(~J^K48$n zFX<7zf&f| z4;k74i)_5*$JpqG)yr|!E+4b4R{ygWIs3Gadx+3EsnJkxysi$Ojc}Me z5!3Oh3Kg+!PcNSf`O!rY{mBlRzlW8uHZ1G6mZ`7Q?j4~|XmOpS*0IIk3go)jC4MA} z+ZQuTTlrH`sqJ?x7S8!Ymd@!(|Kn@hDJd^_|0bIW^A*IdDg&6oPPChh^8VU04shuQ7F+ZwT|YCbr~V9U&w0^G~95W=*soIFOM_zS|uXIQ;u z>%uiver>)JoA7#nzOxc=M?tYK7`L*i?Y~e3{GKs z<&mm6oO+eI*NshIg5F;#QT*?$N0QQK2v zRqs9WmI7aXpB%bt*^84a8XTmR9&x)S-~V9uZ-0M(;5x@`>ODLvJc!!F!vT_(j-i!K ziF!z|yt(;EoJHQ#(zn^@xz0-vIElBqroSDZ%A*c`%HJ>O+eL+tQaMW7kIeL9PczSa z>SE7)q#5vNFs%G(1A_7wIeD@IoLxQl9-W)|H!Gf>?%1PKQh1L~L^p^|x8;x4sZRIa z)p4AC=F1Z<-L04>&ssSO{*^_x5E1?_S+qf;%EdcFL;_)4^(f#GHUMum5kv}C%)`Dg zMxOaJF}6~4=GxvtD-~J!#ovoVk5@bde*OO#GM4gtg|zLMh2j_U%d>8$|0)%dNtY{p znrkQ_bGQ7CFruB)B=}s%*JjlPdu#bn#r`|-&+u~;M`*j(_%TBbKug28QfL$>8u4d| zfv+Np;oFm4^75{u*3H*k!}@2xwzoTXPUY9#bWr1PIu6Im!-=W7Us_!qF?CQ~5iOA2IDkjGLVS5aVLvo9-6O_XD%rM;11}$iSDNM)!m`e>iIrGe;1T zoOJEhS=c(Ai>;Hj*%MEqG-;{*Vu@IrF5?Zbt9*&)O9oSPk-PvSISwf5HCCXH3r_{9WyY#rF2wDy8%DCbhVMgEf(i=rnqZW}DbRX0V z84J0z^ObAb$+Hyd`YZ90@0KzLC#>$@4sYF=1uH0F{f?ns4QwXUHb?cU{7}_GpQ7r8-lbtPFiyzEA>=oaLnAV0n z-P_D94<`5B@xf7T+Q-WmJ%^u?IHaf3EZhX$z%(=9^xt zGE!7|i8bx{!W-|`?ndH8L@pO1E-vB8gq(VfP^`!m5S-NFReMvoW@LgOKMESz>q-60 zk6^hs>P6;_lPl@yXOL;a>w{_QL|C1($wja8Ps(2{&)w-R7!=-Jy8Y>O_uy`1clYnj z#N@;()vG#K)9S|)noG36ZcX_iR+Cy?FIxXD)Y3A2q^0xsJ9?7ZMPfxz$kTxmrOdHf z?+kI*w3TXN+mIE`=a)Or64pRn$lKn`EQ5inXMdV=cy(Z-Gxy8erCaI`2j1tHDLUGC zYBh`1Xyaz#JTYm zUJPn<-*yX^7Eo3z4vwUqspD{J3ot*|91X9#&=hYed|%x9Ic&lQ90szaN-2eGfyi z;Ou*E6`iW0Qh&n65bpO`VhQo^>4EPC)aC+&C|o$h}W|brc}tNl51TU(nKazPsOoA{;bm%wn=u@1(W zO4^`Yzvg`c{vWw5;**2dLv9giCa|rrl*2~*Ag%MlUw>aL{IMx-p31IGN z@wqmS11?6~qSQ2tYNAzoyHiR1&EMr}y|uDjbQ@ycS40u7&}Hlh>MXoqLs@E9T{Efq zKELT!EAAuUA3L7&XCc|7^s7?jzMQyc8{YP4n{YEqQU z_4!t@d%5&u1_x>MZI$>X@d{a2KA1wd)}V8qQL9_LS{k$&ZX?X~K5?CD^2ycqvov*( z6QS$d73EXUej1FBxL?QqVmMkd*~`nz(Z*5;O7W7WZY4Z2`yzIXiP7vQ1w+NjMZKx+ z4LaRWc)4OR=9-=mrk zcKP-JYmwB?=u}zO(>EC#=9ZQWYADep^)dLYbDPJG+U1M_W=+9IJpY9|LR4D%Yv72z zOLA0({w>vB(e!J@tid_bF4jM!40KHT-Dvz`Dj>q%gLYj$bq@TkOUce2?|Cq!wz{&CwKFk^aXx&(um7CwPI& zj*k|6H+U-thD%l~UM=1ttQ5o}{M|7$^!J*$Y-5S%PaiALy2C=j4tB>&D(g}CBlR3QAEOni3a(;^uqSnC; zVwznjhW%(_G80dq;@d+@CC2ef)xp2yBNG&O`{?t^OASZn=54#y>>E7`fE~@Z=fiBc zPW>Q$-R=Q_MJDNVb^Zf#LSIy7v&;M3ol{zf2(`rVsZ^xg?3PMR@ zl%F8)UuN+e96KXCloYD67wyDk3#Ag97dw2ztq4#!-~_AiG_Bu7D0e7cT*QHq?1huH zlT*k#%j8d~6XTz4CnWx!%6ipJB8ll2brV7?3oWu;+uB{F&g99fvCDdvON;7m(&$oj zb99822fI$VR5tYWX|5wo#^kn2Hf_fn=c<6*mwr6B^8A;dx}4v@B1U^2%-??B-Q7KL zcZ7!Yo=$C(+dDhcl#3u0Nk@E0kgCk)2Tv?py<^q%JP(iiE%E%x-TO?x+gBsoZURqq z)c>?@dsd;e^8}Wn?r;oXTK4+2Nn8#d2d8T0jwN)T+*5KmgZM~?+A-lIYvThn&0glJX#)FgzUdtX`}{w!a8u7dP4-(BANVWq0ALm+i_;axsH zFljW1n0AA6$~qg?rC~Z=2BoU|zeZUt6Lrx;P)8KE46MUS^qz_$&8d&5^p1M_v1__U zJxSvqimpM%RRBO4vDZT-oA&;m7UzkgmbSjI5|a-mi&>zf^gwYniS1kmnx07GM=Nf# zk7>_}MIBJ}7U#-L0|OEyRKuWya=3VnH1u?9yWFlc+|8S)C>(So_YW13pRK2d+P|`` zyLl)V(Z-mII_q5ZI6R8Hvfxg}|Lk`8I(Sppk5x}xnf=?SGty!S`I=MC6 znu0zrJUiE{-wiH%w{S+rjrEx}1eq+U9GZskd%N}VkNbL5PoqHh3Dz2sd{p_ z^Ot#w+dlHS+DuTRlAW&#b9w8{ltM23kX17jBJDN;B7K*?bL2hY7OGah3dB&Qy_#Yx z=}1qvDd%3qp{s+#kE?3&Od4!HC|V3tR;|4vSE*Vh*>n1nv*?VrAeQgP?r`g+Z+aa4 z)~oObYlc=qE(C=rlFZ-hWX2^j;8ju^x`yNl-rBRTqjIxlQmywpann1yNeNTGknK8^ zzllQNYl?kN&5wXIuoB+Qb1(hN^_^iJ`hsGo7Iq>>qC#&Opu=fM(sT7!2Alu*h!k4@yDO}O%lq6LzxXRok~l5u5TL3vC)4Brax zw$(nkDhrz~+WQs@)te7_`?|OsCbctqbpnztDkiL!-p>8jp6-iY_giv4Nd`R^kPnZ+5T{z@cpFU*|p!?0mwj3 zg0zL|MsB#zBub;8z^58dNA@QZWP(lJd+h1Ey>igwCiZDGTkyHpN?i!h|OHF zbhb3@iWgw6s7}ZIaFsUhf9EXh*4?Ai4mt2V8q)u9$Joc)+QUCp0P=Ug<=;?>&KT4+ zc)#Dl?=}2}&3e=Nr!B_sM}bgrEkn`TnBmR>rFEu}eX=QnP*W^V;C>HFpeQtB&DD{G zp>@e71L`^QgbNi5(~B04#s+BXtD74CrPM~Wf6WzU@@bU z+p9$$fF9ZmQT-fkTg5Gk_Jx+$`K@?;PEuD1A&^|~P~Ch1)>HMDHLsr8LDZK&%#i8`%#x0SevFFKg3@5eh_>zr85h!4sQFF9Rh8A6NBQ$%?8|v zGB|RE>4{NRTg1}Yy8Qn@XjRgM^m)g(1c@%xX1t8p@(>^Zkm0HC^N;Lv+b(Sdd|3KI z4yJk|;NpkPEp_*?C>$|>P-n{-O%X^LE0C6WppV}Fto=4NssN8%j{Mdd4w}MbuKUYNOBglM<+m#2zA`e;xh^%NQE?T|eJZ2AU$N zGy^)zXe38j6{AXwHp|fgydCjOk!)DT@3bi}-COkj^q0j=;yX5okeA~wbqu5M2Xq#N zYiP15;$lz0K^TYklLC=9x}pm(!lQqzMcN5If+)}1N7o5bE(2~?x>R~`^GcY9E_k3X zhk(mgm`ZI~3j{tjti1L30hZC$d1%ZM@sP&^W(^BSF+&8Q!j)Zn&eZ4e!^4#yG!Q^? z7=3sjH5jF7>x$f?_54 zPfhQC6?95Uv6oaZwwD4m&L|?!sa{wA&bAFHR*JKF`8<#??LKB4ZlYD!k0X#n$~-L+ z3zWI8kw*FJ*?U40^nccNWxU69aNud39hkASd2auehDif6&vM(MKCP;>^>=mz9%s($LraP1o?b=Ft0^1f-XTfaW63X z)0%YO-$}GflTFIQf!;{=n7`hn3rIozdhJCGjB44MO~e8b^fF*dtcU=-0ci$? zYxHYI!1*riQQXL0pfqz@ATbY5BVMs@fNyLSn-|K8BO^1H;FLrhM{ISw>3`QrKdnJj zM==bCy_RVQzb~(<(QFArWMJXRfx-*Lsg>~m6Tw0f|1yG+)0ieE%}>;m2uA=UF0^d_ zBW=?n)LV)A5RYmvsPyus#VGzIspvtWr?<8qx&-+;xXA?9E`} z*XjND$&ZsF2>m-k2$=SIE&Is(NK2sav0JM2d*~}MLb*is5WyMl@7qg*2r_~`ftJz8 z8_TJx^!~I!l?`bPoi-SJs{VU7wo-qauqPJ){T~w^t8#N*{ZFj3pH2Kk?{5)X@wDI- z8^QcyzkJXuLkEOLS!(0e*#~~d;Z#I9LZZQ#328QnKvlR(Uy(4)(MQLxuYfvuro>(s z-@fcJ&X%~i^KFC`utipZdGUaA+xI4`$Y@Yo(a)x>@2vj9rIscuhxEN^7Fg$ZF#q<% zlJs!u_Ne`bXBrfHjQ$!O4owuC>}0_Y!INH2r#_{5M5ia}de9$bB%UGk)6}XDtN=;< zQO9?{s#ggJA-UoNWtvyOAQP<9gkW!fs-yCZChfKf|7L>_$+nF=G9 z%OdI}pE4n(R`po;OSR?o6Zgl|(FLaA^ zD`9MC>#?BNR>xoish-NPY6`T)iM?(chWwfN-xN7#G>R1=$o#eEz=Zcz=Uts^=D4?4 zSg1Ynbh|oX)E-=1_zZ3Yw;sVVML0W(Sl&-OC!D2)& z4w5_6>GWj5m{GWCY{xiRf|km znZZAML&ot+^p@%79hAx&DyT|uT!D@K`&2phot-i*ID9~I_^e~T}B+W4k;pz zgslfC#s7exl`aH{M|EC{IRP0!9!XC40oegg5GS0Z8J)`$@Hy*Bfupx5|R<-v0A zOLXU@03Lt9uAKFB4x{D5MDPEva?lM*4F&a}C?XDkG5E1X|in~ZD@l%#O@^wzz~+%?6@t05?G$p*FeXuy!M+6Su7K4l-TRUp#J8#9 zqwrOX-Ur~v3^n@bR`!LyhNVLz!GwGO#Bz0^@yC%f<+)ajOv~7tjiBa;WL5flr;oj% z!|jSO^}@=9E5Jis(PG(B36Jfi*ye1)kgxluv8Cz+AhmfIMjm}Kdf6iL_lhZ;*gGX4 zU@9EhcncBs>=7K;mysIBPS?PjNaGzId;_Py8q zL)TgAIh|tHNzhS*X}?$-F=Xri zg3nb5nmYU^HRG^M81wjZ>`iI`E~dx{Hh6I5{ANGfFUwdKwFyAYV-9xzE_nN{)RsJ? zb(t&)!wXJtI^PA#xw#NIT<{aG_<=|J>Q2OCGl#T<+k`aIT^fEDv{5lIU{M|S*(ks1 zza=yYVyQRR*#Lry_L?hUKtvq{0L}PP-Rm)|tJ73Fs;R|F9I3B?d3|R_5pU&dn{8Q; zEZSaXuI?nL!0pOen~RWpn;^%+4JO2EJJAdKTydFMnu`E+;sd}vu+Z|eKZUj5WdxgP zn`cR`Dd+Y8mQVna#^NIh?*&~VVlN^0h^kA8F-Ocz3{EpNbi2VX26`P=rUCI_)CAA_ zmm3j#thSRwxO^8ThzeDlJ2azpVTK5`X9S(y2b}>WZQ^bD16zH`V&!7(;4gNh@nN}7 z?&7og6`nH*?lETznX5L}=^>AJ#S%`cE;5n30XY_9sYs?A!FowcKyn}tCJor@k))~;@dRWJ>|+XGt}jb- zH(@xILmm!19`uvi#?YCJ2SfcWpV_lMHd_UrOvN2Gee1uGKyJ@fPb#2w)o5BDW^QpO zjwOQpt2KhWU!PawarBke$QV%Rmkjdla|^U<$1~Bhb2;ROqOm>wq{$yjCBCrm!g2s% zr;G`Y|0~Ty;i$V3pmodKn9B~x`MV(Kp)O=g@N5yNebL1@97d!EoQJ?F0jZ|o143lz z!r=zZxGCasHdTk>qqnKsqUNwISWsIeLf-qWu{MEs^GwB39fKs;-SZQc1l=k-SL0|Rt6{o=a|KTvB41hp0n z3X9?WS!4A4wRPG-9#9FWj(}2&6#dy$AFdJ9b6l8oPPhN9|FA$PY7|3dNmoVxlRaMy?crGHe}>hKewI|La;+a(ejdMLFsiu;_p@z|<1cHe46Cn=V$%m|IbsX-1z#@XX<{(0^?3)-f*=-~Z%{oy)J3RdQr z7<5+Weg(esuA!W!iT(ZKLAR(hQ`lCzt>lrOL!|%xwasv28L3hC28bL}sxgv(g>rv- z1j}4Ao#r$fqZ)+Ne&cXvunMhM(y1Xia_+FifYQyH;@D;wpQsIoFC42=cwA~G)wat|V({vJN}x!aoW%iT+FOTP+_GWY$!$lBv*n(AsmA~E#_ z6+&{49%9FWyqq>p2$+ORTZf6|4acb%(?FK%a<8-cAJ514Ynvs2IwtG4xv*0`zPQ<& zlHp)dY7<$M4;>rbq69fEg$k$G+l1Y@WGd1Js8CYwZ%W*2OFj9$|HeD_-pSp|vZ=jA zYS)63j$B~{I@6$KuAlsAXZkDic|{;AD=Mz&uTLM6m!vEUV)d#+&i!6&wad`^!#}XH zo=f?ItiUq;r9ZIdtGY!)I!P8)mS#Ta!0Teb9Vc~bUfkfa3_RTv8Rb3ZMq&nACgIFT z*`6ZvkF0uxq+(XOVt5y5rAY2QX7|IU3I)a9bSx8N^$P;W%bN3Wz;4q2tvQ=H^X>C$ zU$nSI+0lmYC(9?mXf3b_-PdiasCv*eABd zG38WK*fMfsfF1U0%^F1)ttiwH!JQG~u>5Q*LRlZQp$#g7l4e^qz~-{NlCFpa1Zi(~QS`*N*z;Mz(BI=jo0 zYWKAP#x{2hF0W_~Th>TYP^>4P$?}h+f(K(Fzr7Q>#>P~cKuh9!C#D3rw8<|nv$YPR z62orE98}ASPAjH8rn6e0^8fu_QYBj>K3FM_zVkR4aihhnip0*PLDYjEW>o3qpad#q10P#HLed#MAvxyZotn{xGH zQ_ip5MD7kuGrTDYeQRq)_JNFLGZOfcNjEAI?#KvGbR=SBCzoUB$olA)nPFUk<_ZO7 z$Fy8`50S`nsoTNOvDXMK4GgNRLJ-B{Ou~09W9JjU)c61T?Q&@R`%43 ztM~VbVHUPaq}-$jZRQC8Oy>yv*+ioaG&39~L^AW|AFOYmy(3Huw-^k&=w?v%BWh&_!OGuU#m?G+ zLrgsF_o~_1MsRSRqv3%R6t!;4nztgpICVZ{6xN!t6oz|(4OrIGzHo51oVUc@wO#;p zHx{9 z_EK|KS(XQ#S#utjt4JO(V4V9_9hL@PB3lp+CjWk`ef6aDM**IYvqan1;}f&z2M4XV zDm&Bq>chtc81cWJ)wEK=TV>y<$C$(SyZ`OCs<}sarmxGnyogqKxcHm%=NBc!6`-{7 zA1{l&(=^WgSeDVyEa-0spfdQsgDCJTKb${g6B%-Qc-(9dm6G>9O+ZEXDqP~^jh9z# z{nWA=wR6^kw4f5Dud>6=Ft5WLS#o!IxvU2BB z=%!8oa7!^$lXi{No-|mV2X?KTHR8`gCO{e7*z5oCB`3Ovwrbuf{<6ZaoN%MpuhHlj z8)tuyTHlP6KI1AHb4k_xKEksaxoGB`J2samM)F7@y*b`2{t=jPcLXh5;8F8TN{aQ3 zHE~OFe{}pkO=c1YXKr-m6fi_~V2BmY*+Z0U$#2R@#q5ZAlvcR8>6|eCg3Sz4FUWfb zJ$s$onJL_>r>q5Yy;9aZXGQCZvzc^;EA#%r{N*ypkZ@V85cowyH_JKeDw!eR-04x7 z^C?(#I(}WbmdV-nRz5w&x`pvxRID&3DHIlY*c_|#hJkDGKIcFg>)SsegdC8lJsYp8 zO81Mous00;W$RKyMs1H9?xvq>@V?cxRV|%?mrK&RMv_)8W>-*9_Kw2k*V@Y=JkD4~ zCR%l4Qjm$er0FJV)=DNY`wqBK*-+5?L!Fk2Zs9lqND9!5b22^r>#xTuO}zx1c2G}f zv>5F4yGmj|bv+}F*2l`uW&SqCT;nw$8Ed$SY*Wi!^Z@w23I#Gdt;9rQqaGBr<56h< zeN@1_JHeeD;WJyXQWDnbhS>Na*iBA5P=?HW?>h;|&^ug5PejqDH>qc&9~!;~bZ6w( zyTV^iJ)!AKKDPG)BCcv?#QD3QyE%-c5{<}$yRR(4CNAIL=qIZJbJ0}2VLTvW|1*Pe z%sW-th`db=m}R?UO6Re^jXco$qVmlQyaPKq1cN?y&a>2R-G?fFFla)gTWJQAhOuB9 zg0f1FY{HY1jqjpR32v!l;)fvvI2(g9A`?(!_jEy!6or#R(u$Jk6l)~93pR1dqDK*o z&58aL`fYLV5mw3CyaejgR0F5_Hb}cbgG&~4`7o;gL%qxHV!%hRc(!#RYrQa}8U4n9 zI^JOvAZrg1abB>}99-?RaO!cR`w;}F>bDZQbQ1EMi)JkzAPtwg;>KoP53KA3u5(yW z0!sV{z@GHZx%2t3qlnArX;OQ?(T+GJ%Yv~iX0^{de+ z`G4T{`_RxNX4WHB-YJ;Ba}cd7JwrxAtTNAZQ`OnZE5M@X1HPJw`UQE~WrnWc@uT4r z3BeWR-gZ2>{CHKKi{YGl0;rvW_6SQ5;XeFLWi=lY^x*;!;}b3Jak(?Q3cj1A1_HO% zqYSqa?96)lVlF7Y6;JNb)B>N7W(i* zXf2Ig6FA~x$0=u&E_*qr6ycM8{SrvS;N@VLeJF#3GO_^(Pt*NFQ4X_=)!=f6JBla0 z2b|$UC4iu1ImlH+vuprq+b&&S)ws_)6~nB0x%NOrdM61?6ZQG;az}P+b1=jP!bnbO zN1aWr^JSeJ6RU9m4k=K?f$-h)jh2CT3${h{AcIzuS4KwqzIW$0ZJShZJjw8qWxvJp zDN|+~;raT1q|hO>2a*5xP78(eu7Z&sNheW%;RI^dKW}H} z6quKGx>Co(z=0wVGL%t+);ni+rfxzT72vE8U#m1*ci{`}Bg7Y#cVgX74N9O7Li0~^ zrN>-m@qj@^(WT*Xb369O`deC)6hk~?`B5a`1QrGLt3^<6f8p30+yS;c*T*@g9dWf= z9W8o=>v!l__@Q}(!NAH{mCNkSyd6HUL)ryNwON0;>5t5+=kv?UeRG&OjjdwngFWik zjrzqu`H1N6w*XY6)C||AJ8XQ4+9s{VN)^rt5a zsP^l4;EYeRMJd-Ms(q>9$>o0r=Es^aORZBYll0_8*(;IG*(deDfLIlgA_+>~f=O!0 zf`KEX0Pzb`OZUe5*9ssK#b0Z2A3dU;$-2G6Dof{|C2E&6GFtsP)C2fM<%QfCwEroQ ze-K7k6DLjBY5+$1H(@=iK>cbQj%@!5t)eDsHyiv;HJ~;a9}^Plh+Hm%Oh8hf2d}5 zDm!I%`q2WOKCeFUebh7#8gjd4Q!q*S_oNV#?f-=$+lt)n9ov5Y%&wI6 zdnycq?jE05uV1lG@#@(Zm|$}ao9O7^0tVpPPj%0~mZA@AK{=qWa= z4G0N^BE|3qzm}%W3WqnSxo}|!Dqafk+0??F35-me2H;gm2GmCb*g7uwf+XnH!I%EHXatrhK)6@GItn9X(nDK5gLOGq6y+P5w z^das)*Tx2xdSu_UCy8$p75o_;)m2yDZ0yaMl`K2{NOzR{7v^C8I5AZ@dypJ6*w^+{ zr|O_dQ8v4AZKRYHCgVIcmR*;nRqyAm1^G8$P;Yo{^&gOKDe7v8o*f=pZF=z7cc<_F z0_XWO6br95t)+=KgFRMw9iDf;@RVxO(_9~LGR|=A^jtH?`|#8mb5W#re7>y%^BCVC zJ}~5Z9}SIG{jln|AgOgv_3K%=sHi*f+|G!YgU-*p3&WpkVrr`26AFHQd&`0_W^(5S z-Bn#br3<8=g5rZv_;8b;DKX`fm!_PkpU(#x|r_pYcjDVtU;7_a))NTb3r4 zQ!Uo?p*KS7ewKgR&o@Nk2|1LaSYOlD9HjW=KegwP_&C3fP^(IrkByf+Z$6S{uisT; z+%%g!lVW21UO$D(%~W!uM2FP$*Q>8Z?HjUU%Ta3ypgeF|;NvG)tG6<(w)*%Z&xZJd zO}_Vw&TWQ&vb+5X`ui8yQF?y_TEf{BlltO|@<+RuPAj-3>CANDZHF4~qvKPXt%pnF z9M58t)t!C})IXNpK8j*B5P~O@ib@eeM0Wt&qGs4#h-cqBOI9QIOu$pL^ILi;#ouQi zos|&S0W_*WR(g0pyL++uLLKIRw_hK-Hy8K1SMA2h?X!S*$q(^9xl;FCFs^+c`M)hB z<+)+XH`W9tYJ5)qZ04;Nr8hS<%YnxZ)+BI9=bP!GZi|d0yMk1u z{1ulkYOe>CerxYoC{L!mv`gCURSS)_LTMGNDzVl1{H?g*cXK6nqSEJY^jAA>r7Xiu zgZTV$r>6~HUrOk7>FD*?G`zSk@x;#POVa<@?B9R?-2s*tgA!B}{NY_5^f{qO`{PZJ O13g{+T-G@yGywpN;QV6% literal 0 HcmV?d00001 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