From 68cd0a41eabc5501d7a619a9dc95ce2502599d3e Mon Sep 17 00:00:00 2001 From: phanhuuloi27 <73208452+Nezumi2711@users.noreply.github.com> Date: Mon, 6 Jun 2022 21:45:33 +0700 Subject: [PATCH] Add Shipping Directions Function --- FoodifyServer/app/build.gradle | 6 + .../app/src/main/AndroidManifest.xml | 23 ++ .../foodifyServer/Common/Common.java | 32 ++ .../Common/DirectionJSONParser.java | 100 ++++++ .../waterbase/foodifyServer/OrderStatus.java | 5 +- .../foodifyServer/Remote/IGeoCoordinates.java | 13 + .../foodifyServer/Remote/RetrofitClient.java | 19 + .../foodifyServer/TrackingOrder.java | 336 ++++++++++++++++++ .../app/src/main/res/drawable/box.png | Bin 0 -> 12495 bytes .../res/layout/activity_tracking_order.xml | 9 + .../app/src/main/res/values/strings.xml | 1 + FoodifyServer/build.gradle | 1 + 12 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/DirectionJSONParser.java create mode 100644 FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/IGeoCoordinates.java create mode 100644 FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/RetrofitClient.java create mode 100644 FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/TrackingOrder.java create mode 100644 FoodifyServer/app/src/main/res/drawable/box.png create mode 100644 FoodifyServer/app/src/main/res/layout/activity_tracking_order.xml diff --git a/FoodifyServer/app/build.gradle b/FoodifyServer/app/build.gradle index f62faf0..ca7d8bc 100644 --- a/FoodifyServer/app/build.gradle +++ b/FoodifyServer/app/build.gradle @@ -1,5 +1,6 @@ plugins { id 'com.android.application' + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' } android { @@ -54,6 +55,11 @@ dependencies { implementation 'com.github.jd-alexander:android-flat-button:v1.1' implementation 'com.rengwuxian.materialedittext:library:2.1.4' implementation 'com.jaredrummler:material-spinner:1.1.0' + implementation 'com.google.android.gms:play-services-maps:10.2.0' + implementation 'com.google.android.gms:play-services:10.2.0' + + implementation 'com.squareup.retrofit2:retrofit:2.3.0' + implementation 'com.squareup.retrofit2:converter-scalars:2.1.0' } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/FoodifyServer/app/src/main/AndroidManifest.xml b/FoodifyServer/app/src/main/AndroidManifest.xml index 2bb91a4..907743a 100644 --- a/FoodifyServer/app/src/main/AndroidManifest.xml +++ b/FoodifyServer/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="com.waterbase.foodifyServer"> + + + + + + + + diff --git a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/Common.java b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/Common.java index 36f0d0c..4e6ec61 100644 --- a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/Common.java +++ b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/Common.java @@ -1,14 +1,25 @@ package com.waterbase.foodifyServer.Common; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; + +import com.waterbase.foodifyServer.Model.Request; import com.waterbase.foodifyServer.Model.User; +import com.waterbase.foodifyServer.Remote.IGeoCoordinates; +import com.waterbase.foodifyServer.Remote.RetrofitClient; public class Common { public static User currentUser; + public static Request currentRequest; public static final String UPDATE = "Cập nhật"; public static final String DELETE = "Xoá"; public static final int PICK_IMAGE_REQUEST = 71; + public static final String baseUrl = "https://maps.googleapis.com"; + public static String coverCodeToStatus(String code){ if(code.equals("0")) return "Placed"; @@ -17,4 +28,25 @@ public class Common { else return "Shipped"; } + + public static IGeoCoordinates getGeoCodeService() { + return RetrofitClient.getClient(baseUrl).create(IGeoCoordinates.class); + } + + public static Bitmap scaleBitmap(Bitmap bitmap, int newWidth, int newHeight) { + Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); + + float scaleX = newWidth / (float)bitmap.getWidth(); + float scaleY = newWidth / (float)bitmap.getHeight(); + float pivotX = 0, pivotY = 0; + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(scaleX, scaleY, pivotX, pivotY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG)); + + return scaledBitmap; + } } diff --git a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/DirectionJSONParser.java b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/DirectionJSONParser.java new file mode 100644 index 0000000..c58833d --- /dev/null +++ b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Common/DirectionJSONParser.java @@ -0,0 +1,100 @@ +package com.waterbase.foodifyServer.Common; + +import com.google.android.gms.maps.model.LatLng; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DirectionJSONParser { + + /** + * Receives a JSONObject and returns a list of lists containing latitude and longitude + */ + public List>> parse(JSONObject jObject) { + + List>> routes = new ArrayList>>(); + JSONArray jRoutes = null; + JSONArray jLegs = null; + JSONArray jSteps = null; + + try { + + jRoutes = jObject.getJSONArray("routes"); + + /** Traversing all routes */ + for (int i = 0; i < jRoutes.length(); i++) { + jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs"); + List path = new ArrayList>(); + + /** Traversing all legs */ + for (int j = 0; j < jLegs.length(); j++) { + jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps"); + + /** Traversing all steps */ + for (int k = 0; k < jSteps.length(); k++) { + String polyline = ""; + polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points"); + List list = decodePoly(polyline); + + /** Traversing all points */ + for (int l = 0; l < list.size(); l++) { + HashMap hm = new HashMap(); + hm.put("lat", Double.toString(((LatLng) list.get(l)).latitude)); + hm.put("lng", Double.toString(((LatLng) list.get(l)).longitude)); + path.add(hm); + } + } + routes.add(path); + } + } + + } catch (JSONException e) { + e.printStackTrace(); + } catch (Exception e) { + } + + return routes; + } + + /** + * Method to decode polyline points + * Courtesy : http://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java + */ + private List decodePoly(String encoded) { + List poly = new ArrayList(); + int index = 0, len = encoded.length(); + int lat = 0, lng = 0; + + while (index < len) { + int b, shift = 0, result = 0; + do { + b = encoded.charAt(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lat += dlat; + + shift = 0; + result = 0; + do { + b = encoded.charAt(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lng += dlng; + + LatLng p = new LatLng((((double) lat / 1E5)), + (((double) lng / 1E5))); + poly.add(p); + } + + return poly; + } +} diff --git a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/OrderStatus.java b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/OrderStatus.java index 66cdc1a..d9b509b 100644 --- a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/OrderStatus.java +++ b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/OrderStatus.java @@ -7,6 +7,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MenuItem; @@ -68,7 +69,9 @@ public class OrderStatus extends AppCompatActivity { viewHolder.setItemClickListener(new ItemClickListener() { @Override public void onClick(View view, int position, boolean isLongClick) { - + Intent trackingOrder = new Intent(OrderStatus.this, TrackingOrder.class); + Common.currentRequest = model; + startActivity(trackingOrder); } }); } diff --git a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/IGeoCoordinates.java b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/IGeoCoordinates.java new file mode 100644 index 0000000..1ca1401 --- /dev/null +++ b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/IGeoCoordinates.java @@ -0,0 +1,13 @@ +package com.waterbase.foodifyServer.Remote; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; + +public interface IGeoCoordinates { + @GET("maps/api/geocode/json") + Call getGeoCode(@Query("address") String address); + + @GET("maps/api/directions/json") + Call getDirections(@Query("origin") String origin, @Query("destination") String destination); +} diff --git a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/RetrofitClient.java b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/RetrofitClient.java new file mode 100644 index 0000000..609650c --- /dev/null +++ b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/Remote/RetrofitClient.java @@ -0,0 +1,19 @@ +package com.waterbase.foodifyServer.Remote; + +import retrofit2.Retrofit; +import retrofit2.converter.scalars.ScalarsConverterFactory; + +public class RetrofitClient { + private static Retrofit retrofit = null; + + public static Retrofit getClient(String baseUrl) { + if(retrofit == null) { + retrofit = new Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(ScalarsConverterFactory.create()) + .build(); + } + + return retrofit; + } +} diff --git a/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/TrackingOrder.java b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/TrackingOrder.java new file mode 100644 index 0000000..e1a5028 --- /dev/null +++ b/FoodifyServer/app/src/main/java/com/waterbase/foodifyServer/TrackingOrder.java @@ -0,0 +1,336 @@ +package com.waterbase.foodifyServer; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.FragmentActivity; +import androidx.loader.content.AsyncTaskLoader; + +import android.Manifest; +import android.app.ProgressDialog; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.location.Location; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.widget.Toast; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesUtil; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.location.LocationListener; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.OnMapReadyCallback; +import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.PolylineOptions; +import com.waterbase.foodifyServer.Common.Common; +import com.waterbase.foodifyServer.Common.DirectionJSONParser; +import com.waterbase.foodifyServer.Remote.IGeoCoordinates; +import com.waterbase.foodifyServer.databinding.ActivityTrackingOrderBinding; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class TrackingOrder extends FragmentActivity implements OnMapReadyCallback, + GoogleApiClient.ConnectionCallbacks, + GoogleApiClient.OnConnectionFailedListener, + LocationListener { + + private GoogleMap mMap; + private ActivityTrackingOrderBinding binding; + + private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 1000; + private final static int LOCATION_PERMISSION_REQUEST = 1001; + + private Location mLastLocation; + + private GoogleApiClient mGoogleApiClient; + private LocationRequest mLocationRequest; + + private static int UPDATE_INTERVAL = 1000; + private static int FATEST_INTERVAL = 5000; + private static int DISPLACEMENT = 10; + + private IGeoCoordinates mService; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityTrackingOrderBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + mService = Common.getGeoCodeService(); + + if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED + && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + requestRuntimePermission(); + } else { + if(checkPlayServices()) { + buildGoogleApiClient(); + createLocationRequest(); + } + } + + // Obtain the SupportMapFragment and get notified when the map is ready to be used. + SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() + .findFragmentById(R.id.map); + mapFragment.getMapAsync(this); + } + + private void createLocationRequest() { + mLocationRequest = new LocationRequest(); + mLocationRequest.setInterval(UPDATE_INTERVAL); + mLocationRequest.setFastestInterval(FATEST_INTERVAL); + mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + mLocationRequest.setSmallestDisplacement(DISPLACEMENT); + } + private void displayLocation() { + if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED + && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + requestRuntimePermission(); + } else { + mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); + if(mLastLocation != null) { + double latitude = mLastLocation.getLatitude(); + double longitude = mLastLocation.getLongitude(); + + //Add Marker in your location and move the camera + LatLng yourLocation = new LatLng(latitude, longitude); + mMap.addMarker(new MarkerOptions().position(yourLocation).title("Vị trí của bạn")); + mMap.moveCamera(CameraUpdateFactory.newLatLng(yourLocation)); + mMap.animateCamera(CameraUpdateFactory.zoomTo(17.0f)); + + //After add Marker for your location, Add Marker for this Order and draw route + drawRoute(yourLocation, Common.currentRequest.getAddress()); + } else { + Toast.makeText(this, "Không thể lấy được vị trí của bạn", Toast.LENGTH_SHORT).show(); + } + } + } + + private void drawRoute(LatLng yourLocation, String address) { + mService.getGeoCode(address).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + try { + JSONObject jsonObject = new JSONObject(response.body()); + + String lat = ((JSONArray)jsonObject.get("results")) + .getJSONObject(0) + .getJSONObject("geometry") + .getJSONObject("location") + .get("lat").toString(); + + String lng = ((JSONArray)jsonObject.get("results")) + .getJSONObject(0) + .getJSONObject("geometry") + .getJSONObject("location") + .get("lng").toString(); + + LatLng orderLocation = new LatLng(Double.parseDouble(lat), Double.parseDouble(lng)); + + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.box); + bitmap = Common.scaleBitmap(bitmap, 70, 70); + + MarkerOptions marker = new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(bitmap)) + .title("Đơn hàng " + Common.currentRequest.getPhone()) + .position(orderLocation); + mMap.addMarker(marker); + + //draw route + mService.getDirections(yourLocation.latitude + ", " + yourLocation.longitude, + orderLocation.latitude + ", " + orderLocation.longitude) + .enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + new ParserTask().execute(response.body().toString()); + } + + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + } + + protected synchronized void buildGoogleApiClient() { + mGoogleApiClient = new GoogleApiClient.Builder(this) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .addApi(LocationServices.API).build(); + } + + private boolean checkPlayServices() { + int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); + if(resultCode != ConnectionResult.SUCCESS){ + if(GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { + GooglePlayServicesUtil.getErrorDialog(resultCode, this, PLAY_SERVICES_RESOLUTION_REQUEST).show(); + } else { + Toast.makeText(this, "This device is not support", Toast.LENGTH_SHORT).show(); + finish(); + } + return false; + } + return true; + } + + private void requestRuntimePermission() { + ActivityCompat.requestPermissions(this, new String[] + { + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION + }, LOCATION_PERMISSION_REQUEST); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + switch (requestCode) { + case LOCATION_PERMISSION_REQUEST: + if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ + if(checkPlayServices()) { + buildGoogleApiClient(); + createLocationRequest(); + + displayLocation(); + } + } + break; + } + } + + @Override + public void onMapReady(GoogleMap googleMap) { + mMap = googleMap; + } + + @Override + public void onLocationChanged(@NonNull Location location) { + mLastLocation = location; + displayLocation(); + } + + @Override + public void onConnected(@Nullable Bundle bundle) { + displayLocation(); + startLocationUpdates(); + } + + private void startLocationUpdates() { + if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED + && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + return; + } + LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this); + } + + @Override + public void onConnectionSuspended(int i) { + mGoogleApiClient.connect(); + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + + } + + @Override + protected void onResume() { + super.onResume(); + checkPlayServices(); + } + + @Override + protected void onStart() { + super.onStart(); + if(mGoogleApiClient != null) + mGoogleApiClient.connect(); + } + + private class ParserTask extends AsyncTask>>> { + ProgressDialog mDialog = new ProgressDialog(TrackingOrder.this); + + @Override + protected void onPreExecute() { + super.onPreExecute(); + mDialog.setMessage("Xin vui lòng đợi..."); + mDialog.show(); + } + + @Override + protected List>> doInBackground(String... strings) { + JSONObject jsonObject; + List>> routes = null; + try { + jsonObject = new JSONObject(strings[0]); + DirectionJSONParser parser = new DirectionJSONParser(); + + routes = parser.parse(jsonObject); + } catch (JSONException e) { + e.printStackTrace(); + } + return routes; + } + + @Override + protected void onPostExecute(List>> lists) { + mDialog.dismiss(); + + ArrayList points; + PolylineOptions lineOptions = null; + + for(int i = 0; i < lists.size(); i++){ + points = new ArrayList(); + lineOptions = new PolylineOptions(); + + List> path = lists.get(i); + + for(int j = 0; j < path.size(); j++) { + HashMap point = path.get(j); + + double lat = Double.parseDouble(point.get("lat")); + double lng = Double.parseDouble(point.get("lng")); + LatLng position = new LatLng(lat, lng); + + points.add(position); + } + + lineOptions.addAll(points); + lineOptions.width(12); + lineOptions.color(Color.RED); + lineOptions.geodesic(true); + } + mMap.addPolyline(lineOptions); + } + } +} \ No newline at end of file diff --git a/FoodifyServer/app/src/main/res/drawable/box.png b/FoodifyServer/app/src/main/res/drawable/box.png new file mode 100644 index 0000000000000000000000000000000000000000..946781aff88e2e3131ba46eb5e1af3ae061998f2 GIT binary patch literal 12495 zcmc(Fc|6q7_xGI{%Vf!9$(D?eElaW`%S0HIT_S5jlR~BJ%#1z z3L#62EkyQxo;&*fp5O2F{QEqw*VF4&&F7wT?>+aNbI-l!eb0RpBRy6oJ|+MFt3Lje z830K56A91^@V_PBFB|Ybx*J;h7HIe<5PdNme(&+ZTi*a+Z!_&LB1vj5KWyan);Z^G z?&<37<8<8x`1tt95Io#(I6HZ{$ar3Nd-O+*4*)FCKXuZ=_wnqIU#y*#)5PDAAosa} z>hsB0tdk>;48|WhPlsm_dMF~25bJ{0M<>wF%kSYT#avm=w{~%h4Sf4%r=vDEXy5)@ZpGygYrOCn5$5%7vo)r|@x-19u<*wA! z2Y=YsMaKG%&o}2u{#})D9v0^qI+V&9e~)`-xty%8<8Sb?j%+(i)$Q9Fmk0}E&SE^& z#|W|lI^HrmT*+U-W;HHkX8bS0D+~e1vHjGBX>!M==$op} zM;ieW8?%?T!>F|!t@I6oEC8#s-)M`?(BIk3^Y}&@d4%#xaOZ(|{M5`?cF z@E(1lY`Bqi(SB&`M1Yy;(jL2->3W?{nY}gs4xl;$G9@16;1viDF^Lc(U($=2+u_w0 z{QRn4GdlaeP0SoWj!_N}LyyJ`CG>s$of+?4PwtFnoA7H>D^OXjy3};4?e9x)35f!D zblHTu4p%ajOb!#Vo7?b}@iLLmG|^ZU%z4Feqlk^6A?YXpA5(vFXr~nqN$9NV+e|~I z50jzUlVt@-g7=H4vyhFkAs4b4r`oNu7S_*uNK{)P%FlO-fJ)LrBSAM~Did@D!} z*heFPZ_9xU9!td?owlt~@>o&Kvopuycgjw0S7c8I{|ukLgP1?W07my5^P^sNxlu2f zx{BxX3Q`j|o4mx>?yAXdckJ5x<+s2QfUeXw#$KsMe#PVfk>x6l44Fdwc!*?9%&BRO z>Q=}iG(#k9UWM{$sd5R5L`UT(F8JnWE1*mdMig#?9BbsonrUH95eg&cp;uSA8D=yiJTh1U|{ap}px*U=pK>6FU ze|-dCADdCXR9Q|=G)W2C?XXBe2R9Q_D9(@vG_Z@*{`=s7A1>tQyB`hgknI8fKYczx zZ9JemJI+i`uQhsz2M}+|F5bP36e>ZZz{#8rZV{$XA`d`wx@a89!i78jZ3xRU8E+Cb z!JhJh%}ChHO0N}q2SNOj-4T{-iaiB*0A8Rya1zTDikD^pW_RQTtHY5(W?=|ob52Lt z!E9Xk7e|17DZcoY7qTE20DG9cpqds5bN?N{U3l2Uku$)xoqV`Vvrs6Z?{$Gfv)HRq zddPWz0o1x3o^yB+^v?K_{Hl^bd#!KPFrKneU7jN;{a{D{JAVkWu|PJL!-D6Ex7TCb zeRiZ()7oqo*mUEI@(n9`$)*~mtgUSBMGJXp7{H&;NB z5Ge#`EESODUTUCM&C2+}Peoh&-UV$O)VnRtQ^^J8(f?R{cz{=z+Uls(vhAN^1F6)# z>C^Ku7oE|z9ESTmc|y6xF^$YDK*jmiP)OLyN{Cs_v5L#TDYAcknj_8)*d!DcAEO&( zW(xI+KoB#IeN^`jyEU#+XjjFrIQUcW?byrm7l*Gt*S8x!&_IFm@UwuW(4!GcFT#ck zCaw$i{rUDP?Z#G)@LVY~2KJCEV}9ln55ehRjaczyLBLYrj+6SM^SKez!qvAmoHJR!+9WKnAbd$k zAs8=+(eeSq=3l-P)Vz}2bee5{VbZg_Pci=z`O)Q-F%nW691QoywFJf^g;b*;vs|m# zy`|dWXKgc^OSZR6mP4tR#v;A7hk`>(GKBye^qer!EmOWC+J>LD{d>p1Zr;#eY4Dkf zi>6tWP{|8PRyjJF!6(BL)opUDI=;154Y0t~#Xm+MEyku3GQBRzk3Ak3f5aUXFH0_QtLfr z70sH@7nr?;T-i7tbQfCUVze>KRCus{KpEAj=6X{>rN*Hn`oe1EZ!YU0M)~L)Et~;4 zy#MA%iNW?N!T3*K`H4PR`!>VFOri5^^o^Vs0GW7P?P!7UI6^&TlV1IibU~McyQBhB zXoY32Q;`fBZpqX8x;Xc^(n7%J^*ytlL%Bo!k1y<~n&+EhYhOu5{c{_M23}!O$1l|M zO;u`1M%#Azic9HRQ9o@iUP6Oy*bdo)Ot8JNb4($k`2Dd)3yv zfsGV#OfUIp(GZe0BbEV&e0Yb=!~Dvhr0fbqf5MYiylP_7!H@TSjfpMJ1U*ZeCJYg7 z^uuUWgY$n!dc*Y^)yGM7aS29pbhkf?V~U*74g8#F^;R(dKhF1DewE~;jCOrSUgTTr zs`-Nms0>DWE#sp=t<>vB$TtD zy8iOA>Y1-Rdrqp{i!9GQe6`4aP06f*izzhvDDdx4yJc+eTo__N*^XB_aXdT~?|)c! zwo89zuH>HwsWyVieTyBZPHHb#==r#$!n5-T3PCfCm_U@!mbZn{-H+ohP`j zbVe7gx^Mk!QnXL;$k*?K8dny_$RJRo?5g&(v%1mig~CevZBF!eR;JLDCgG(+>Jqeb z+o>{HmJ_XPh7A=H$JlembX>Ri$Nn$y$gnSg`Rv_mz;`}dag0ZizIDdkyU>3nyAh)< zaLzL5L-zhkdL%i8DCGCvv_v;_1B%3rzQYIZi~pM;Lbp2K)#0Ex+;1ctbLdI6g~O%) zhP=#)_RKffhAR`wLpl`*UNblSH#_YBup2Odo{P@LJ#5z-ut;9lI!ThVg>$^HB!tF9 zHZbSQV4nWocgr+&&O?8@D2DNB7LKRrJ)Ym$ta)Bvk~uUVM!f&-b=8u<^$`twNn#HB z1Cw!PG<{sD&}nOYt6nekpv7zVQ?b zXb-GB0=aDV4N5WFJNzs@n1MGUbq`Wq#223sIMGe78oy4Xq3&hbO|VOUOylJR2Xvv( zO}f=pu8&Z>#UbljDz(xW|6>4l_$iLL_W&uh_0x{jy$%HkE6RZjSfp<(xXK2K{4B8E zN}%J@Dr>!7!s(_1NhOD9aqn&h7rJKUl3ps?eeGSX3Q1g8%bBKcjAnz2 z#H+z^RUNR;@rAy+z<$z(RN{AV|@pH`T`-~4ByW~?DJYj zax~-Gg0TZ~FN-;pzs4aP_o^)k%a6y1u=@O10W_)le2>iL;oW@+!2V%;cIKZ`-g&c- zEpYyf{3nO-4_B~sP=z`XJUe*?8R8*B?{&VQ3a0 zkY~9F>Jk@J*!yx9W}aKr3RVAg4m-mGgwXa5L zx_QbjWeXK0WHQPQ(9qAk(q%DcMTZaFHp;V__1x6bha&nZRjtnv!u<%YPa-GkFK9Ssy-)OCU6Z@q%&D%Ul>PeR3a)E-TbWgkC zTG{rX$g(fE_#-#{li}x;LKVl%eUpNObs17Kk1Y>!u#gk2Z5}~xU7&ofx{GwO{PDu{ zT!f9Kq_JU-=;Jm1Y~PZen&}}8>5uwiD}yO?x-Bz?bhv?K-l^9GLApew(>a5nt2vg% z(2`Pdol^9?Jm16NAm2qaUB0Sp=STd@L1lDR$OD5Hhth*jrl~q*?&Gm(!{9dHEvw2k zM3@X!q*9v6?x*C=9J+gh!)@?xKJfI@E!h0q<+zHwvwluA>E?y{L{F_RyGo zyuB&BPn%*{qVAL5SQAPeZ0b$0wp|!}Zu{eH6^EHVw>#Usfc4rL)c}h$4MHX>&%q@5 zz`Xqu-1^l;9>z3Ry&y9C!C-UqYQ$P^fkJx^nf)WrW=6$AIp^m3>eL9hl-D;?hA%CJ&Z`j*lFSGEL1Z%?@cVsWV4I7;$&*&u*ez zjrx_Dq9gUK8@1651J&E`(v;C;y{SWzJO1NbLC1jMO4`NR#Gh1fDcqT`k*ILP-w7QF zqsi@(YL9xj(>o z3!9&L&tq)~GfDIkm}<9#+Lc2-2IC_-3k(ex%G_qGs3qlB6u%3FI_1XMJ0yF+jqn&= z9eQpr`CchLf+v-%{)81cRM4@L1CHXb6NjTYLEaiVLA>|dR%aYd@{iR%(fT6pYDOA;Ej zAd!1N8fIgG2Ek`;<|fHIG?H%s1(+Fx!=)CQVgnC*HkosxA5G+SOlVaz;!rTf42Nf1 zfj@yXpIq?eBc}35tBVGABM)HZMDK%XAB^Y^0h>kY)Bpi_kaBGt=PoO=E6u8hF=f-c45MIZjP?EG*&WYyN9!;a;LZ7jd z?1ZP}1QO*aA&xm@0o|>K2Pt(Sz~y8nbEt&)VpRjUwEsWL?7Db`G@pML`CT`LDO7@G zX^9<`W%-|Vs@BNS)m52bYhSt+OMgd-?L<3*a_A^pr*`B1_s>x3PDP2qoHDP74>Yk= z@BW8a_Q0%pNCv}TLKCqY9&fvPgChEj|I6gG!tUX4yGt5^*|-pGNehh_UsbUo6VGBC zk7MdbdInqtO!0TEw7(zuz0CRq-D}HSlIUI%ad$HR|C$UYa?p;?@xt8vNN5!&u*COY zi{nOblA~Uhzd0m*o(Iw%UjUV~>1=GV-zw%K8+h;GFsDUkI}3tUiU*=5EWWAeq<%cg zKy-%!XX7dN(r9}p)c<9H^|9Qlwg`rM1(NoyZ6k^dMA?6eHGg6M{PNS_vo{hHiv}im z8Hk?$Y0vGo`gD5v@%Zt-0S^$PQcR&}E_e$#5%Oc?!~GE-7NQ8ui=lx}C)$MQ97`%o z8#zDGoQZ+$*0$IK*3w+ONHn}V6kBe#r%7a5PE|eR2x*i9@etgA%I}s+BE`G9cIDTm zaD4nM{1yuSX?%hKhlcAs z#feVoYL;Z}B8)F7)c<7+MPS(BLO=aLet+_O$=67=?c*B}iFkwUV`;~a=l*6^JHf-p zfP)4-bSjQ~^X4X(KS>T!Kc{JEycl?kuRX7FnuQn(ZJwtkjZre%YE}7r2LbNGUCecw zB-gvu5a}A-S#Zn5U?M~~?Hy=%K-0B^G$su%xok`rpNza57Ec|y({%xi{-8}FApuEg zbDD*zO*aY_Ei`3F2;v9Y#u6>ZUCKVUxNV+xT;bWq0kL4)gD7AKx0a9rp45HuLMPY9 z%PV(Ff8Ssi3qAtjuuhuF{A{oa);U#;yq&csXVoS)F2yRafbx4#^S|_WJMHhfwBl%e zl>StCID)uB3or-ZUR3J6vJ>gbc9t_mbjMBm1P_>GfjTmUo9)``qW&4)&Nn9Cm4m%m zP#_&%$%THKk>tAiIjA#l3r_3&fCP3F7&=KKm_S0G0)P6a8B$tgSf(mYRStC9(HwO@ zbLfQe)~37sp2|NU9>O9pWedkhzr>xBJ*#kDv#<(v(`ajwmjU?G_U_F%q?5Ws&-3Fs zRY8x}J_WFVN;v)#zG8f_dd>pJ&Ga3!!*MSVWE2W`KwF5zYfi>1?oaWi2gb;iqZ@e` zIR>x=dk`#&2NHsB8p%WO)ps8Xz-%I1VYHbI=3t|;5F5+(x0F0%*i$+!jF5SN08}^@ z`+4(VjF%~|7|e(W;u2h`M_xz&vkm8>o(JA=lpt|Twq48D)wINC zvo3`snCoNTz$WX%uf=vKk=t(AcMvnwWD2c}S4oyETd~=`_hChdg$0B{97N{gZ!|Rhx{XWFUrW z3-8$%VY@BtQF(wVv`s6O?VLOVD1m_Kwr(ohB|a3m#6YiA5BY6cAeefR)GgE=h!cY# zDLm|Eq~Y&Y_}eH8_fuq7d#rr~e(xA6CU`O4`GlcW1z;^#A zho$KoZFJHy4|St_VUIP*d>R|>kQ@LbqPaMHDjS}Y0mO3J??+24BJ`mQ@hvGxAr}qo zCmw(^s}jeUjib!qE)}tZHisgAaRPwwDQcJH6=WIk&fXnG9gcGL1t)Ekw{TTLm1Z>2 zFc4?O#4|I8Rvmytn!p!_Q&B>4^1nCmK;_5iZy^906lw1{hzsx^4EF!_r@vH$tstTq zj1*hHnDEu`T=dr-m~(S>Kcvu!!xPq77Ii{@G2z z%ka#dinB=))f#->@c9II>8A!09M4;7SM+dltwm{`p^cc*wwIrE_-s$Z*`lG$vv83O ze*G>l87lfz{xT8~0GN1KK7S&hxa?V^A2*$IQWkmz)^YvQxjPltey~9w2S|%^9PaZ; zmdkcl6U~C+P#`*T$>H8^cS*^jp9Mirpg6bzMWlLhkz{=uhvpH;K<0AMuw{UKDfi?B zlWDU^z&+LpSB+~l9oLqEa>be?u6OT+ydo?v8oSaT2ruoVwq)?i@%g)0qR#)0SlDWb zS%Km-kY?s7#-sgC0YB#I^Ph(6UG+w58h}w;u-e&Rg|o{S$Kyi|SA4q!Wn}$!Ed`J>*vAy=gzw~Wzi z=bTO2jOe@mLvIy6A2mG5b5y8&^Ls-MgSEFr0xS-y4(vM9|L>oP&=1c@6~*g1NV9q- zMo8s~$4k29rfyT(LgL2vR%vJfP@D1?5!<>1~%nNe*BGbhMX`csoB zx%uiI(HwwChi?Jz_mi!@NwB`CU&s^~zEtnZhS#e*l0GQf=Dm$U0DSyK7UHsIU3mia z!ks>Y;8I3&j9*QwMKUY?f+U7HU3l5E~XT%+rjr!KzKX~LwO7C`T`En3tZJGxO zh}iXQcHFh4vAMOm#4UNk(4$;MAAD<_WcpgQh3w6VIsoP)M^IVZ_30A~xrOx()xY;n zeW}OmQEdvkys5zm;PrkEVUjsL6y)-NvNlU8@JuiYwo_f(SiDsnZJj&QJ@;F2h+s-cQYMb#M$EVxtdRpjEbl{rZu$BVRJYd;!I?)(MLQb$aX;_eCy$+pkor9}C^}{jAdZVT1N@47 zT^S?UJ)}AF*>^pq!Q!cj*;Ymg3=*&(I*!7X=8w2(h)UO(+H4L{8Wf9S-t!G(Sw|!~ z1?|yn>e7wSK$)WM(1MZP9?!P<`bAy@B`8#OEuXaBcF&EX>ZWO4E|$UjqQPiB-Cm;3 z)0MBqPo~L*!;YD}^Czj6N^0ffHM54?&kj1Hp-08fAiCwsjQ#)AakX<;OwleGH?Z1cQ3 z_L77h5?dU^qUvngedjYD^OtKoF8i2a>43`jxBVrI?ay-WP+Rx!oUI?WAE@BY|1JeD z+G@)Wy_Y<5e;@DDqX%dl4@~y2_tHj+Z>iI^*;bVpoY=^_Lh{Am~2sr_GpWb zp^l(N8{DdUP+8{V_OUBFR#U0Q{e^4m3rm{~ij;AO*o_uj!9AcsxJ?k$slT&RJ*-aE zB)e${rEj@y#%c8}Xj57$HchqYfUl6BTWH-$!lhh`1M#KIVGhLxxz>a~&lNOhSJwF2 zpOLpiT#JV%v`Hl)QE$Hey&vM`0|T}vgflLdQQF7z zmoz`=Aj?AXtXS6MZt--30Zl~Ih1TKsbgu2+_7J5Ju_$|~9!biVJ%n-Z)fi8Sf@72? z_3_bLQ3^XYgyq>cvY3^q{+5&P60Xq!ufx13$EE7r^eyfWT;wDxLt*Dz=#GrINXEJD z;*b6A>w?tt%JhvtB;l!tMW7tL?phqUw7to%?bu(baFe1TJlb?(=_5Cqs?Qe`<=x|B zuzrveopWV%4h2Tlsf-OKVH!;torY4qU)|_jjSN4F<$k^6Gudi6wKme08S`sio+2mp-M)j@XN=4xCU zzR8&~pQjMe3tv*sPVH>l$HjH25pFbp@PHe0exTR0$i6fV3&5{Ukhi*!zy7}NyO&qh zc5_2jg4v62O`G+J%x2A2Lqe>cR~8OmAdFB;1$F2Eu8xgp(^jW6{Mn~Sk1}eosi-NP ze&Vp@cz`c8<(h_0tv!r3go}$w_0R0%R;W{2c`!VEm_s;5Dg<=dZ}i>WaiVoOBaqOZ zL0hrc=;eT$Z^xADx8$3yb!IHFg^-kY5Wv1=4uS1go?w;n;ol~(kSOIrF*v`jp_fOL zR&OtDg`||~5WL=4ob$+(mdK2AT{XOE%)ks9PV7MGtW|F`+jq3Ss&m@3%y^}p^{dVO z_j0Mi^7H8&=tALC6wanHBShrIIjQB*I|wU_1BorlU)_*Tjqr=+MKNf2&D)Lc+3HEh z31c?--7UGK6ZtCGs28inI5cSBf?kVX8Cug@9pIRIUm0XK%viS{4e$DNVRO&jwf&4# zUmB~vwn|(5b<=n1-F){zCv&Fl{P$&8CuJ{_3Im*)Sh}yh>c(S*xlf zZna21B+ARCt6&BCg*(%eBBxO{v9bm~`EyD8`t*>X!I10>6f6uWZFeYfLzE?n03o5i zY`o3Q7Z#H@fU5A?*E%f5LdNFYtqweV(v0DFmrXqbA!t$yPkdCck+bzj|-xtZ;B$4huGWizPA|*z*N+|t=b6tt=B?lCNf=l(1EID8XpgV5F4teLq{teiK3DIz8qZyq z@Uc2T2S%BAz-)v&rGwC?hX6QpUk~LldwUsiiGI~% zbMyere(2Kd`CXwq`>l9AEHf|fY+J<*esF+~_ht|sut{n=n|(D?>h;#_)giwpbA?xf zGy%ADMv0nqtM1Jkgo&VkSZP(|3#fk3dB60f44Qvs!r5srvt+Gf*JR^Pb)+OvS8Rak#K zzsA#w@F&JH&2A)rkgnUMb`J(0ZR=Fe6y*6pDQICjy|OA=)5kMo>+YPH%lN8-W{~+a zPkvk&^i8VEgD{Kt)Ijg1XFH_{%7d@C=Q_=OMvAUal}UD9N%`J`XfR{}4d!u)Ql}-` zw*$Qwa~viq>fR~qE%k4#pDtx8C~wcQ3-W;4MgX=Fr2bgQtls!Eu1BqUp~7lYM4`1+D}pWxhJd)X z=Jx${1^(aNpdHjEe_$SKa(QO>t;R|&W||OI(!J@4uwVzg;c{_rAEVkH4zVg9i~DTE z_HHa)*7abG8?OJiuw-YQ_A7(8%YVU1N67j~p2O=Ul4bP;mkHf) z(0rHyR|_X(TFFrLB4TRA1{R)i^@ZIyI=q+ou7ks;?)+F)OrBajW%(t@hw4<7O35^{ zSS$DNU(z=HwffGJ{V3p@!8{DXk5%;)#n9s+w9(DWEVeT_t|#anByO0L2P4A4`e`xC zRBB~=Szo2b{#~RZpmhCn`>zqfp{=PApCIv0gJ0m1T+GPweoMppoP`eEIDU}U3ip13 z)B$rLAJ6is&mq4yOl*=S5#6w!=Mv0yFBzpkWS)G1hWOK^!DS?8Z9@FoOcC(GaymoU zAXBVIYdF5!VATKvZr4c&^ka=8V7Z(j)aHXqsl_TxGq?n?FI(sYzNX_hgrLB1hI0)7 z_g+mBlA&M_(;7VrFo)P6%uT0G8}86K2v9Zul+R>lA?m}b zM0W6xy_0EV?{L4ZKhgzO!I?-Qc3Ar)qJM3dEewKr*>YgC1_yQGb^AAjeerY*p!Su? z_=m9(uA%w&` z)sR>Li4fZ@g2eNX7=wrfu)<1_1$gth)HOQ-5j4G)_W_8qYNg6v;{kh4kuU;H00((R zc~RK65F)$^;oJy$haX3Q84)Q2@j2Wv@x@#wu?L)R(x*p3iAdr)B^(i}#fg4-kpuAf4bF#1?$`w`&AAE7%pyK&*$nA0RJ$f`W!wgXNX}p|HXb77a-- z5H;ni;;-M;BzJ__LxCp5F_1QT&_zR~5fI^%oq7ZCf*lSxg9ccQuoo5r#lY&!w}7bJ zVeUh9Pa4-WB9(;kfH&HyY#C1Qf(0J|!x7y|`EfRA9TE*fNj0{8VuC9EvO zag%ZF2sseT$B91Q0M7BjVg?~tR2qhKf+ejfaM=>jCoY>}ZIKKO@1VwD^(mT$ygGtf zSh7k7mz@RQ$^fbXDi^>F49i)WwFE)pIt?+KnsIg4j%#&P{vK9Owx~>LF zs|YR;F%bBzhQP0PY9GCpAR}B$mKZz;VtlQ!O9WUW^ADJQXa$M1r8Vq@oqi@V$g#{!;Pl%(q`P*P;R%kN$!nXt80!Z}OC9Xo^L^UKX z(TE}tNW_O1aiUKs(geQW1OWTf9QwvvGQ)uU2I1BDF z#MwCs)ws%Co8Sc?KCNXagR$S0-3(4je(1MQl)cP<02uF|+_CM<_v zV|%^mAQr%21?w8kS8al+HKYIPgbyBt4FV<&0_EQ*G hN_|rl!;ACT4(V7NG!FW`RRZ82eI27yMOu!x{}-y4HEaL? literal 0 HcmV?d00001 diff --git a/FoodifyServer/app/src/main/res/layout/activity_tracking_order.xml b/FoodifyServer/app/src/main/res/layout/activity_tracking_order.xml new file mode 100644 index 0000000..2c59b6a --- /dev/null +++ b/FoodifyServer/app/src/main/res/layout/activity_tracking_order.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/FoodifyServer/app/src/main/res/values/strings.xml b/FoodifyServer/app/src/main/res/values/strings.xml index 93dc8e7..6145b01 100644 --- a/FoodifyServer/app/src/main/res/values/strings.xml +++ b/FoodifyServer/app/src/main/res/values/strings.xml @@ -12,4 +12,5 @@ Home Gallery Slideshow + TrackingOrder \ No newline at end of file diff --git a/FoodifyServer/build.gradle b/FoodifyServer/build.gradle index fc54f61..8ed7b54 100644 --- a/FoodifyServer/build.gradle +++ b/FoodifyServer/build.gradle @@ -12,6 +12,7 @@ buildscript { plugins { id 'com.android.application' version '7.2.0' apply false id 'com.android.library' version '7.2.0' apply false + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.0' apply false } allprojects {