diff --git a/.env.dev b/.env.dev deleted file mode 100644 index 6aceb95..0000000 --- a/.env.dev +++ /dev/null @@ -1,2 +0,0 @@ -API_URL="http://smtp-dev-env.eba-5jqrxjhz.eu-west-3.elasticbeanstalk.com/" -VERSION="development" diff --git a/.gitignore b/.gitignore index 8d96fd9..fd0add4 100644 --- a/.gitignore +++ b/.gitignore @@ -283,3 +283,9 @@ buck-out/ web-build/ android/java_pid13560.hprof android/java_pid22412.hprof +.idea/smtp-pi.iml + +# environment variable +.env.dev +.env.local +.env.prod diff --git a/.idea/smtp-pi.iml b/.idea/smtp-pi.iml index c0ca3af..986b696 100644 --- a/.idea/smtp-pi.iml +++ b/.idea/smtp-pi.iml @@ -18,7 +18,7 @@ - + \ No newline at end of file diff --git a/App.js b/App.js index f644b03..5c447bc 100644 --- a/App.js +++ b/App.js @@ -8,7 +8,6 @@ import { createStackNavigator } from '@react-navigation/stack'; import Login from './components/Login'; import Chart from "./components/Chart"; import MapAdmin from './components/Map/MapAdmin'; -import SetGPSLocation from "./components/Place/SetGPSLocation"; import MapTruck from './components/Map/MapTruck'; import BottomTabNavigator from './navigation/BottomTabNavigator'; import useLinking from './navigation/useLinking'; @@ -17,6 +16,7 @@ import TruckScreen from "./screens/Truck/TruckScreen"; import CraneScreen from "./screens/Crane/CraneScreen"; import { navigationRef } from './navigation/RootNavigation'; import LogoutButton from "./components/LogoutButton"; +import BottomTabTruckNavigator from "./navigation/BottomTabTruckNavigator"; const Stack = createStackNavigator(); @@ -64,7 +64,7 @@ export default function App(props) { - + diff --git a/Style.js b/Style.js index cfe0507..d6d9931 100644 --- a/Style.js +++ b/Style.js @@ -1,3 +1,8 @@ +import { Dimensions} from "react-native"; + +const { width, height } = Dimensions.get("window"); +const CARD_HEIGHT = 220; +const CARD_WIDTH = width * 0.8; export default { container:{ @@ -8,10 +13,11 @@ export default { containerForm:{ alignItems: 'center', }, - + progressBar:{ + alignItems: 'center', + justifyContent: 'center', + }, input : { - height : 40 , - padding : 10, borderColor: 'gray', borderWidth:1 , marginHorizontal : 5 @@ -48,18 +54,17 @@ export default { }, previewPlace : { + borderWidth:1, backgroundColor : '#FFFFFF', - borderBottomColor : '#202340', - marginTop : 30, - height : 37, - flex : 1, + borderColor : '#aaa5a5', + height : 50, + marginHorizontal : 15, flexDirection : 'row', - justifyContent : 'space-between', alignItems: 'center', }, rightWorksite : { color : '#FFF', - fontweight : 'bold', + fontWeight : 'bold', fontSize : 22 }, button : { @@ -101,6 +106,17 @@ export default { color: 'rgba(96,100,109, 1)', lineHeight: 24, textAlign: 'center', + justifyContent : 'center' + }, + + previewText: { + flex : 2, + paddingVertical : 50, + fontSize: 17, + color: 'rgba(96,100,109, 1)', + lineHeight: 24, + textAlign: 'center', + justifyContent : 'center' }, textinput : { alignSelf: 'stretch', @@ -113,4 +129,87 @@ export default { marginBottom: 15, }, + cardFuel: { + flexDirection : "row", + position : "absolute", + backgroundColor: "#FFF", + borderRadius : 5, + marginHorizontal: 10, + shadowColor: "#000", + shadowRadius: 5, + shadowOpacity: 0.3, + bottom: 200, + left: width*0.14, + right: 0, + height: CARD_HEIGHT*1.9, + width: CARD_WIDTH*0.85, + overflow: "hidden", + }, + + // Marker Custom + bubble : { + flexDirection : 'column', + alignSelf: "flex-start", + backgroundColor : '#fff', + borderColor: "#cccccc", + width: 150, + }, + + arrow: { + backgroundColor: "transparent", + borderColor : "transparent", + borderTopColor : "#fff", + borderWidth: 16, + alignSelf: 'center', + marginTop : -32, + }, + + arrowBorder: { + backgroundColor: "transparent", + borderColor : "transparent", + borderTopColor : "#007a87", + borderWidth: 16, + alignSelf: 'center', + marginTop : -0.5, + }, + + name : { + fontSize: 16, + marginBottom: 5, + }, + + card: { + flexDirection : "row", + position : "absolute", + backgroundColor: "#FFF", + borderRadius : 5, + marginHorizontal: 10, + shadowColor: "#000", + shadowRadius: 5, + shadowOpacity: 0.3, + bottom: 5, + left: width*0.07, + right: 0, + height: CARD_HEIGHT, + width: CARD_WIDTH, + overflow: "hidden", + }, + timePicker : { + flexDirection : "column", + width: CARD_WIDTH*0.66, + + }, + textContent: { + flex: 2, + padding: 10, + }, + cardTitle: { + fontSize: 12, + fontWeight: "bold", + }, + cardDescription: { + fontSize: 12, + paddingVertical:5, + color: "#444", + }, } diff --git a/android/app/build.gradle b/android/app/build.gradle index 79ab137..7f65467 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -142,8 +142,8 @@ android { applicationId 'com.smtp.smtp' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 13 - versionName "1.0.13" + versionCode 17 + versionName "1.0.17" resValue "string", "build_config_package", "com.smtp.smtp" } splits { @@ -201,7 +201,8 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.facebook.react:react-native:+" - implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'// From node_modules + implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0' + implementation 'junit:junit:4.12'// From node_modules addUnimodulesDependencies() implementation 'com.google.android.material:material:1.0.0' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 96a8991..afc2d1d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -40,12 +40,8 @@ android:theme="@style/AppTheme" android:usesCleartextTraffic="true" android:largeHeap="true"> - - + @@ -85,9 +81,9 @@ - + gettingLocation = this.fusedLocationClient.getLastLocation(); - while(!gettingLocation.isSuccessful()) {} - location = gettingLocation.getResult(); - - //save location in coordinates JSON Object - coordinates = new JSONObject(); - try { - coordinates.put("longitude", location.getLongitude()); - coordinates.put("latitude", location.getLatitude()); - sendCoordinates(); - Log.e(TAG, "send coordinates :" + location.getLatitude()); - Thread.sleep(10000); - } catch (JSONException | InterruptedException e) { - Log.e(TAG, e.getMessage()); - } - - */ - } - } - - // stop thread - public void stop() { - running = false; - reprendre(); - } - - // pause thread - public void pause() { - Log.d(TAG, "thread paused"); - paused = true; - } - - // resume thread - public void reprendre() { - Log.d(TAG, "thread resume"); - synchronized (pauseLock) { - paused = false; - pauseLock.notifyAll(); - } - } - - // to send coordinates - private void sendCoordinates() { - JSONObject obj = new JSONObject(); - try { - obj.put("coordinates", coordinates); - obj.put("etat", "pause"); - obj.put("ETA", 0); - } catch (JSONException e) { - Log.e(TAG, e.getMessage()); - return; - } - this.mSocket.emit("chantier/sendCoordinates", obj); - } -} diff --git a/android/app/src/main/java/com/smtp/smtp/Sortie.java b/android/app/src/main/java/com/smtp/smtp/Sortie.java new file mode 100644 index 0000000..4e0ec67 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/Sortie.java @@ -0,0 +1,220 @@ +package com.smtp.smtp; + +import android.content.Context; +import android.util.Log; + +import androidx.appcompat.app.AppCompatActivity; + +import com.android.volley.AuthFailureError; +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.VolleyError; +import com.android.volley.VolleyLog; +import com.android.volley.toolbox.HttpHeaderParser; +import com.android.volley.toolbox.StringRequest; +import com.smtp.smtp.http.RequestManager; + +import org.json.JSONObject; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class Sortie extends AppCompatActivity { + private static final String BASE_URL = "http://smtp-dev-env.eba-5jqrxjhz.eu-west-3.elasticbeanstalk.com/"; + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImJiYTg0YmM3LTlmNDMtNDAxZS04ZjAyLTQ3ZTAyZDc4NDQ2OCIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTU4NzQxODQ0MX0.zRTuqPl0UbiwJn7zZSxErvBYhkhPibEZ51S4Aqgd6LI"; + private String sortieId; + private long dateDebut; + private long dateFin; + private String chantierId; + private String typeRoute; + private String camionneurId; + private Context ctx; + private int ordre = 0; + + public Sortie(String chantierId, String camionneurId, String typeRoute, Context context){ + this.sortieId = UUID.randomUUID().toString(); + this.dateDebut = System.currentTimeMillis(); + this.chantierId = chantierId; + this.typeRoute = typeRoute; + this.camionneurId = camionneurId; + ctx = context; + sendDebutSortie(); + } + + public void addWaypoint(Double lon, Double lat){ + Map send = new HashMap<>(); + send.put("id", UUID.randomUUID().toString()); + send.put("longitude",lon); + send.put("latitude",lat); + send.put("SortieId",this.sortieId); + send.put("ordre",this.ordre); + JSONObject sortie = new JSONObject(send); + ordre++; + final String requestBody = sortie.toString(); + final String URL = BASE_URL+"sorties/point"; + Log.d("Sortie", requestBody); + + StringRequest stringRequest = new StringRequest(Request.Method.POST, URL, new Response.Listener() { + @Override + public void onResponse(String response) { + Log.d("Sortie", response); + } + }, new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.e("Sortie", error.toString()); + } + }) { + //This is for Headers If You Needed + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + + @Override + public byte[] getBody() throws AuthFailureError { + try { + return requestBody == null ? null : requestBody.getBytes("utf-8"); + } catch (UnsupportedEncodingException uee) { + VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8"); + return null; + } + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + String responseString = ""; + if (response != null) { + responseString = String.valueOf(response.statusCode); + // can get more details such as response.headers + } + return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response)); + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(stringRequest); +} + + public void sendDebutSortie(){ + Map send = new HashMap<>(); + send.put("ouvert",0); + send.put("dateDebut",this.dateDebut); + send.put("id",this.sortieId); + send.put("ChantierId",this.chantierId); + send.put("CamionneurId",this.camionneurId); + send.put("type",this.typeRoute); + JSONObject sortie = new JSONObject(send); + + final String requestBody = sortie.toString(); + final String URL = BASE_URL+"sorties"; + + Log.d("Sortie", requestBody); + + StringRequest stringRequest = new StringRequest(Request.Method.POST, URL, new Response.Listener() { + @Override + public void onResponse(String response) { + Log.d("Sortie", response); + } + }, new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.e("Sortie", error.toString()); + } + }) { + //This is for Headers If You Needed + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + + @Override + public byte[] getBody() throws AuthFailureError { + try { + return requestBody == null ? null : requestBody.getBytes("utf-8"); + } catch (UnsupportedEncodingException uee) { + VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8"); + return null; + } + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + String responseString = ""; + if (response != null) { + responseString = String.valueOf(response.statusCode); + // can get more details such as response.headers + } + return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response)); + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(stringRequest); + } + + public void sendFinSortie(){ + Map send = new HashMap<>(); + + send.put("dateFin",System.currentTimeMillis()); + JSONObject sortie = new JSONObject(send); + + final String requestBody = sortie.toString(); + final String URL = BASE_URL+"sorties/"+sortieId; + + Log.d("Sortie", requestBody); + + StringRequest stringRequest = new StringRequest(Request.Method.PATCH, URL, new Response.Listener() { + @Override + public void onResponse(String response) { + Log.d("Sortie", response); + } + }, new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.e("Sortie", error.toString()); + } + }) { + //This is for Headers If You Needed + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + + @Override + public byte[] getBody() throws AuthFailureError { + try { + return requestBody == null ? null : requestBody.getBytes("utf-8"); + } catch (UnsupportedEncodingException uee) { + VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8"); + return null; + } + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + String responseString = ""; + if (response != null) { + responseString = String.valueOf(response.statusCode); + // can get more details such as response.headers + } + return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response)); + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(stringRequest);; + } + + public String getSortieId() { + return sortieId; + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/http/CoefJSONAsyncRequest.java b/android/app/src/main/java/com/smtp/smtp/http/CoefJSONAsyncRequest.java new file mode 100644 index 0000000..adccc08 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/http/CoefJSONAsyncRequest.java @@ -0,0 +1,64 @@ +package com.smtp.smtp.http; + +import android.content.Context; + +import com.smtp.smtp.http.JSONAsyncRequest; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.function.Consumer; + +public class CoefJSONAsyncRequest extends JSONAsyncRequest { + private Double value; + private String chantierId; + private String typeRoute; + private String day; + private Consumer callback; + public CoefJSONAsyncRequest(Context ctx, String token) { + super(ctx, token); + } + + @Override + protected String getTag() { + return "CoefAsyncRequest"; + } + + @Override + protected String getURL() { + return BASE_URL + "chantiers/" + chantierId + "/route/" + typeRoute + "/coefs"; + } + + @Override + protected void onGetResponse(JSONObject response) { + try { + JSONObject route = response.getJSONObject(typeRoute); + JSONArray jours = route.getJSONArray("JourSemaines"); + for(int i=0; i requestArgs, Consumer callback){ + this.callback = callback; + this.chantierId = requestArgs.get("chantierId"); + this.typeRoute = requestArgs.get("typeRoute"); + this.day = requestArgs.get("day"); + super.get(); + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/http/JSONAsyncRequest.java b/android/app/src/main/java/com/smtp/smtp/http/JSONAsyncRequest.java new file mode 100644 index 0000000..b149460 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/http/JSONAsyncRequest.java @@ -0,0 +1,49 @@ +package com.smtp.smtp.http; + +import android.content.Context; +import android.util.Log; + +import com.android.volley.Request; +import com.android.volley.toolbox.JsonObjectRequest; + +import org.json.JSONObject; + +import java.util.HashMap; + +import java.util.Map; + +public abstract class JSONAsyncRequest { + protected Context ctx; + protected final String BASE_URL = "http://smtp-dev-env.eba-5jqrxjhz.eu-west-3.elasticbeanstalk.com/"; + protected String token; + protected JSONAsyncRequest(Context ctx, String token){ + this.ctx = ctx; + this.token = token; + } + + protected void get() { + JsonObjectRequest getRequest = new JsonObjectRequest(Request.Method.GET, getURL(), null, + response -> { + Log.d(getTag(), response.toString()); + onGetResponse(response); + }, + error -> { + Log.e(getTag(), error.getMessage()); + onError(); + } + ) { + @Override + public Map getHeaders() { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(getRequest); + } + protected abstract String getTag(); + protected abstract String getURL(); + protected abstract void onGetResponse(JSONObject response); + protected abstract void onError(); +} diff --git a/android/app/src/main/java/com/smtp/smtp/RequestManager.java b/android/app/src/main/java/com/smtp/smtp/http/RequestManager.java similarity index 97% rename from android/app/src/main/java/com/smtp/smtp/RequestManager.java rename to android/app/src/main/java/com/smtp/smtp/http/RequestManager.java index 4901265..7f60dbe 100644 --- a/android/app/src/main/java/com/smtp/smtp/RequestManager.java +++ b/android/app/src/main/java/com/smtp/smtp/http/RequestManager.java @@ -1,4 +1,4 @@ -package com.smtp.smtp; +package com.smtp.smtp.http; import android.content.Context; diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/AndroidLoggingHandler.java b/android/app/src/main/java/com/smtp/smtp/navigation/AndroidLoggingHandler.java new file mode 100644 index 0000000..47554b7 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/AndroidLoggingHandler.java @@ -0,0 +1,60 @@ +package com.smtp.smtp.navigation; + +import android.util.Log; +import java.util.logging.*; + +/** + * Make JUL work on Android. + */ +public class AndroidLoggingHandler extends Handler { + + public static void reset(Handler rootHandler) { + Logger rootLogger = LogManager.getLogManager().getLogger(""); + Handler[] handlers = rootLogger.getHandlers(); + for (Handler handler : handlers) { + rootLogger.removeHandler(handler); + } + LogManager.getLogManager().getLogger("").addHandler(rootHandler); + } + + @Override + public void close() { + } + + @Override + public void flush() { + } + + @Override + public void publish(LogRecord record) { + if (!super.isLoggable(record)) + return; + + String name = record.getLoggerName(); + int maxLength = 30; + String tag = name.length() > maxLength ? name.substring(name.length() - maxLength) : name; + + try { + int level = getAndroidLevel(record.getLevel()); + Log.println(level, tag, record.getMessage()); + if (record.getThrown() != null) { + Log.println(level, tag, Log.getStackTraceString(record.getThrown())); + } + } catch (RuntimeException e) { + Log.e("AndroidLoggingHandler", "Error logging message.", e); + } + } + + static int getAndroidLevel(Level level) { + int value = level.intValue(); + if (value >= 1000) { + return Log.ERROR; + } else if (value >= 900) { + return Log.WARN; + } else if (value >= 800) { + return Log.INFO; + } else { + return Log.DEBUG; + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/smtp/smtp/CustomUEH.java b/android/app/src/main/java/com/smtp/smtp/navigation/CustomUEH.java similarity index 93% rename from android/app/src/main/java/com/smtp/smtp/CustomUEH.java rename to android/app/src/main/java/com/smtp/smtp/navigation/CustomUEH.java index 3054a52..dc04605 100644 --- a/android/app/src/main/java/com/smtp/smtp/CustomUEH.java +++ b/android/app/src/main/java/com/smtp/smtp/navigation/CustomUEH.java @@ -1,4 +1,4 @@ -package com.smtp.smtp; +package com.smtp.smtp.navigation; import android.util.Log; diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/Cycle.java b/android/app/src/main/java/com/smtp/smtp/navigation/Cycle.java new file mode 100644 index 0000000..e5a834c --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/Cycle.java @@ -0,0 +1,178 @@ +package com.smtp.smtp.navigation; + +import android.util.Log; + +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class Cycle implements Cloneable, Comparable { + private ArrayList users; + private int myIndice; + private String myUserId; + private String myEtat; + + public Cycle(String myUserId, String myEtat) { + this.users = new ArrayList<>(); + this.myUserId = myUserId; + this.myEtat = myEtat; + } + + public void clear(){ + this.users.clear(); + } + + public int addUser(User user) { + if (sameEtat(user) && !isContainedUser(user.getUserId())) { + users.add(user); + Collections.sort(users, new EtaSorter()); + return 1; + } else { + return -1; + } + } + + public boolean isContainedUser(String userId) { + boolean res = false; + for (int i = 0; i < users.size(); i++) { + if (users.get(i).getUserId().equals(userId)) { + res = true; + } + } + return res; + } + public boolean isMyUserId(String userId) { + return userId.equals(myUserId); + } + public void updateMyIndice(String userId) { + for (int i = 0; i < users.size(); i++) { + if (isMyUserId(users.get(i).getUserId())) { + myIndice = i; + Log.d(Navigation.TAG, "changement indice : " + myIndice); + } + } + } + + public boolean sameEtat(User user) { + return user.getEtat().equals(myEtat); + } + + public void updateCycle(User user) { + if (!sameEtat(user)) { + this.deleteUser(user.getUserId()); + } else { + updateUserETA(user); + } + Collections.sort(users, new EtaSorter()); + } + + private void updateUserETA(User user) { + int uIndex = getUsers().indexOf(user); + if(uIndex == -1) { + throw new UserNotInCycleException("Cannot update user ETA that is not in cycle"); + } + getUsers().get(uIndex).setETA(user.getETA()); + } + + public User getUserAhead(){ + if(getMyIndice() == 0) { + throw new NoUserAheadException("Cannot get user ahead when I'm first"); + } + return users.get(getMyIndice() -1); + } + + public int getMyIndice() { + int res = -1; + for (int i = 0; i < users.size(); i++) { + if (isMyUserId(users.get(i).getUserId())) { + res = i; + Log.d(Navigation.TAG, "changement indice : " + myIndice); + } + } + if(res == -1 ) { + throw new RuntimeException("User not in the cycle"); + } + return res; + } + + public void deleteUser(String userId) { + int res = -1; + for (int i = 0; i < users.size(); i++) { + if (users.get(i).getUserId().equals(userId)) { + res = i; + } + } + users.remove(res); + Collections.sort(users, new EtaSorter()); + } + + @Override + protected Cycle clone() throws CloneNotSupportedException { + Cycle clone = new Cycle(myUserId, myEtat); + clone.setUsers(new ArrayList<>(getUsers())); + return clone; + } + + public Cycle cloneMe() throws CloneNotSupportedException { + return clone(); + } + + public void setUsers(ArrayList users) { + this.users = users; + } + + public List getUsers() { + return this.users; + } + + public void setMyEtat(String etat) { + this.myEtat = etat; + } + + public String getMyEtat() { + return this.myEtat; + } + + public boolean someOneIsAheadMe(){ + return getMyIndice() > 0; + } + + @Override + public int compareTo(Object o) { + Cycle c = (Cycle)o; + if(c.getUsers().containsAll(getUsers()) + && c.getUsers().size() == getUsers().size()) { + return 0; + } + return -1; + } + + @Override + public boolean equals(@Nullable Object obj) { + return compareTo(obj) == 0; + } + + class EtaSorter implements Comparator { + @Override + public int compare(User o1, User o2) { + return (int) (o1.getETA() - o2.getETA()); + } + } + + private class UserNotInCycleException extends RuntimeException { + public UserNotInCycleException(String msg) { + super(msg); + } + } + + private class NoUserAheadException extends RuntimeException { + public NoUserAheadException(String msg) { + super(msg); + } + }; + + +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/CycleManager.java b/android/app/src/main/java/com/smtp/smtp/navigation/CycleManager.java new file mode 100644 index 0000000..8c298b7 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/CycleManager.java @@ -0,0 +1,46 @@ +package com.smtp.smtp.navigation; + +public class CycleManager extends Thread { + private final String TAG = "CycleManager"; + Runnable callback; + Cycle cycle; + Cycle previousCycle; + boolean newRouteHasBeenFetched; + public CycleManager(Runnable callback, Cycle cycle){ + super("CycleManager"); + this.callback = callback; + this.cycle = cycle; + newRouteHasBeenFetched = false; + try { + this.previousCycle = cycle.cloneMe(); + } catch (Exception e) { + throw new RuntimeException("Cannot clone cycle"); + } + + } + + private boolean newCycleIsDifferentThanPrevious() { + return !previousCycle.equals(cycle); + } + + public boolean newRouteHasBeenFetched() { + return newRouteHasBeenFetched; + } + + @Override + public void run() { + while(true){ + try { + if(newCycleIsDifferentThanPrevious()){ + newRouteHasBeenFetched = true; + this.callback.run(); + previousCycle = cycle.cloneMe(); + } + Thread.sleep(5000); + newRouteHasBeenFetched = false; + } catch (InterruptedException | CloneNotSupportedException e) { + e.printStackTrace(); + } + } + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/Etape.java b/android/app/src/main/java/com/smtp/smtp/navigation/Etape.java new file mode 100644 index 0000000..5601084 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/Etape.java @@ -0,0 +1,289 @@ +package com.smtp.smtp.navigation; + +import android.content.Context; +import android.util.Log; + +import androidx.appcompat.app.AppCompatActivity; + +import com.android.volley.AuthFailureError; +import com.android.volley.Cache; +import com.android.volley.Network; +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.Response; +import com.android.volley.VolleyError; +import com.android.volley.VolleyLog; +import com.android.volley.toolbox.BasicNetwork; +import com.android.volley.toolbox.DiskBasedCache; +import com.android.volley.toolbox.HttpHeaderParser; +import com.android.volley.toolbox.HurlStack; +import com.android.volley.toolbox.JsonObjectRequest; +import com.android.volley.toolbox.StringRequest; +import com.android.volley.toolbox.Volley; +import com.google.android.material.snackbar.Snackbar; +import com.smtp.smtp.BuildConfig; +import com.smtp.smtp.http.RequestManager; + +import org.json.JSONException; +import org.json.JSONObject; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; +import java.sql.Timestamp; + +import java.util.UUID; + +public class Etape extends AppCompatActivity { + private static final String BASE_URL = BuildConfig.API_URL; + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImJiYTg0YmM3LTlmNDMtNDAxZS04ZjAyLTQ3ZTAyZDc4NDQ2OCIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTU4NzQxODQ0MX0.zRTuqPl0UbiwJn7zZSxErvBYhkhPibEZ51S4Aqgd6LI"; + private String etapeId; + private long dateDebut; + private long dateFin; + private String chantierId; + private String camionneurId; + private String type; + private String etapePrec; + private Context ctx; + private long debutPause; + private long finPause; + + public Etape(String chantierId, String camionneurId, String type, String etapePrec, Context context){ + this.etapeId = UUID.randomUUID().toString(); + this.dateDebut = System.currentTimeMillis(); + this.chantierId = chantierId; + this.camionneurId = camionneurId; + this.type = type; + this.etapePrec = etapePrec; + ctx = context; + sendDebutEtape(); + } + + public void sendDebutEtape(){ + Map send = new HashMap<>(); + + send.put("dateDebut",this.dateDebut); + send.put("id",this.etapeId); + send.put("ChantierId",this.chantierId); + send.put("CamionneurId",this.camionneurId); + send.put("type",this.type); + send.put("etapePrecId",this.etapePrec); + JSONObject etape = new JSONObject(send); + + final String requestBody = etape.toString(); + final String URL = BASE_URL+"etapes"; + + Log.d("Etape", requestBody); + + StringRequest stringRequest = new StringRequest(Request.Method.POST, URL, new Response.Listener() { + @Override + public void onResponse(String response) { + + Log.d("Etape", "res" + response); + } + }, new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.e("Etape", error.toString()); + } + }) { + //This is for Headers If You Needed + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + + @Override + public byte[] getBody() throws AuthFailureError { + try { + return requestBody == null ? null : requestBody.getBytes("utf-8"); + } catch (UnsupportedEncodingException uee) { + VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8"); + return null; + } + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + String responseString = ""; + if (response != null) { + responseString = String.valueOf(response.statusCode); + // can get more details such as response.headers + } + return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response)); + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(stringRequest); + } + + public void sendDebutPause(){ + Map send = new HashMap<>(); + this.debutPause = System.currentTimeMillis(); + send.put("debutPause",this.debutPause); + JSONObject etape = new JSONObject(send); + + final String requestBody = etape.toString(); + final String URL = BASE_URL+"etapes/"+etapeId + "/debutPause"; + + Log.d("Etape", requestBody); + + StringRequest stringRequest = new StringRequest(Request.Method.PATCH, URL, new Response.Listener() { + @Override + public void onResponse(String response) { + Log.d("Etape", response); + } + }, new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.e("Etape", error.toString()); + } + }) { + //This is for Headers If You Needed + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + + @Override + public byte[] getBody() throws AuthFailureError { + try { + return requestBody == null ? null : requestBody.getBytes("utf-8"); + } catch (UnsupportedEncodingException uee) { + VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8"); + return null; + } + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + String responseString = ""; + if (response != null) { + responseString = String.valueOf(response.statusCode); + // can get more details such as response.headers + } + return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response)); + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(stringRequest);; + } + + public void sendFinPause(){ + Map send = new HashMap<>(); + this.finPause = System.currentTimeMillis(); + send.put("finPause",this.finPause); + JSONObject etape = new JSONObject(send); + + final String requestBody = etape.toString(); + final String URL = BASE_URL+"etapes/"+etapeId +"/finPause"; + + Log.d("Etape", requestBody); + + StringRequest stringRequest = new StringRequest(Request.Method.PATCH, URL, new Response.Listener() { + @Override + public void onResponse(String response) { + Log.d("Etape", response); + } + }, new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.e("Etape", error.toString()); + } + }) { + //This is for Headers If You Needed + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + + @Override + public byte[] getBody() throws AuthFailureError { + try { + return requestBody == null ? null : requestBody.getBytes("utf-8"); + } catch (UnsupportedEncodingException uee) { + VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8"); + return null; + } + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + String responseString = ""; + if (response != null) { + responseString = String.valueOf(response.statusCode); + // can get more details such as response.headers + } + return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response)); + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(stringRequest);; + } + + public void sendFinEtape(){ + Map send = new HashMap<>(); + + send.put("dateFin",System.currentTimeMillis()); + JSONObject etape = new JSONObject(send); + + final String requestBody = etape.toString(); + final String URL = BASE_URL+"etapes/"+etapeId; + + Log.d("Etape", requestBody); + + StringRequest stringRequest = new StringRequest(Request.Method.PATCH, URL, new Response.Listener() { + @Override + public void onResponse(String response) { + Log.d("Etape", response); + } + }, new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.e("Etape", error.toString()); + } + }) { + //This is for Headers If You Needed + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + + @Override + public byte[] getBody() throws AuthFailureError { + try { + return requestBody == null ? null : requestBody.getBytes("utf-8"); + } catch (UnsupportedEncodingException uee) { + VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8"); + return null; + } + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + String responseString = ""; + if (response != null) { + responseString = String.valueOf(response.statusCode); + // can get more details such as response.headers + } + return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response)); + } + }; + RequestManager.getInstance(ctx).addToRequestQueue(stringRequest);; + } + + + + public String getEtapeId() { + return etapeId; + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/Helper.java b/android/app/src/main/java/com/smtp/smtp/navigation/Helper.java new file mode 100644 index 0000000..4c33eb8 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/Helper.java @@ -0,0 +1,29 @@ +package com.smtp.smtp.navigation; +import android.location.Location; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class Helper { + public static String getDayString(){ + Date date = new Date(); + DateFormat formatter = new SimpleDateFormat("EEEE", Locale.FRANCE); + return formatter.format(date); + } + + public static float distance(LatLng firstPosition, LatLng secondPosition) { + float[] distanceFromDestination = new float[3]; + Location.distanceBetween( + firstPosition.getLatitude(), + firstPosition.getLongitude(), + secondPosition.getLatitude(), + secondPosition.getLongitude(), + distanceFromDestination + ); + return distanceFromDestination[0]; + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/LocationRequester.java b/android/app/src/main/java/com/smtp/smtp/navigation/LocationRequester.java new file mode 100644 index 0000000..ee7e4d2 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/LocationRequester.java @@ -0,0 +1,43 @@ +package com.smtp.smtp.navigation; + +import android.content.Context; +import android.location.Location; +import android.os.Looper; + +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 java.util.concurrent.Callable; + +public class LocationRequester implements Callable { + FusedLocationProviderClient fusedLocationClient; + public LocationRequester(Context ctx) { + fusedLocationClient = LocationServices.getFusedLocationProviderClient(ctx); + + LocationRequest locationRequest = LocationRequest.create(); + // Location is requested every 0.5 seconds + locationRequest.setInterval(500); + locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + + LocationCallback locationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + if(locationResult == null){ + return; + } + //onLocationRetrieved(locationResult.getLastLocation(), this); + } + }; + + //fusedLocationClient.requestLocationUpdates(locationRequest, + //locationCallback, + //Looper.getMainLooper()); + } + @Override + public Location call() throws Exception { + return null; + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/MapMatcher.java b/android/app/src/main/java/com/smtp/smtp/navigation/MapMatcher.java new file mode 100644 index 0000000..f88a6e6 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/MapMatcher.java @@ -0,0 +1,70 @@ +package com.smtp.smtp.navigation; + +import android.content.Context; +import android.util.Log; + +import com.mapbox.api.directions.v5.DirectionsCriteria; +import com.mapbox.api.directions.v5.models.DirectionsRoute; +import com.mapbox.api.matching.v5.MapboxMapMatching; +import com.mapbox.api.matching.v5.models.MapMatchingResponse; +import com.mapbox.geojson.Point; +import com.mapbox.geojson.utils.PolylineUtils; +import com.smtp.smtp.BuildConfig; +import com.smtp.smtp.R; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; + +import retrofit2.Call; +import retrofit2.Callback; + +public class MapMatcher { + private String TAG = "MapMatcher"; + private Context ctx; + private DirectionsRoute route; + + public MapMatcher(Context ctx, DirectionsRoute route){ + this.ctx = ctx; + this.route = route; + } + + public void getMatch(Consumer success, Consumer failure) { + List pts = PolylineUtils.decode(route.geometry(), 6); + List lessThan100_Points = new ArrayList<>(); + int indice = 0; + lessThan100_Points.add(pts.get(indice)); + for (int i=1; i<99; i++){ + indice = Math.round(i*pts.size()/100); + lessThan100_Points.add(pts.get(indice)); + } + lessThan100_Points.add(pts.get(pts.size()-1)); + Log.d(TAG, "Geometry points number: " + lessThan100_Points.size()); + + MapboxMapMatching.Builder mapMatchingBuilder = MapboxMapMatching.builder() + .accessToken(ctx.getString(R.string.mapbox_access_token)) + .baseUrl(BuildConfig.API_URL) + .steps(true) + .voiceInstructions(true) + .bannerInstructions(true) + .coordinates(lessThan100_Points) + .profile(DirectionsCriteria.PROFILE_DRIVING_TRAFFIC) + .overview(DirectionsCriteria.OVERVIEW_FULL) + .language(Locale.FRENCH); + + mapMatchingBuilder.build().enqueueCall(new Callback() { + @Override + public void onResponse(Call call, retrofit2.Response response) { + Log.d(TAG, "Matching URL: " + response.toString()); + route = response.body().matchings().get(0).toDirectionRoute(); + success.accept(route); + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e(TAG, "Error getting matching: " + t.getLocalizedMessage()); + failure.accept(t); + } + }); + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/Navigation.java b/android/app/src/main/java/com/smtp/smtp/navigation/Navigation.java similarity index 70% rename from android/app/src/main/java/com/smtp/smtp/Navigation.java rename to android/app/src/main/java/com/smtp/smtp/navigation/Navigation.java index bc2805a..469bf21 100644 --- a/android/app/src/main/java/com/smtp/smtp/Navigation.java +++ b/android/app/src/main/java/com/smtp/smtp/navigation/Navigation.java @@ -1,4 +1,4 @@ -package com.smtp.smtp; +package com.smtp.smtp.navigation; import android.annotation.SuppressLint; import android.app.AlertDialog; @@ -10,9 +10,16 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.location.Location; +import android.media.Ringtone; +import android.media.RingtoneManager; import android.net.ConnectivityManager; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.Looper; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.util.Log; import android.view.View; import android.widget.Button; @@ -34,7 +41,6 @@ import com.google.android.gms.location.LocationResult; import com.google.android.gms.location.LocationServices; import com.google.android.material.snackbar.Snackbar; -import com.mapbox.api.directions.v5.models.DirectionsResponse; import com.mapbox.api.directions.v5.models.DirectionsRoute; import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.Mapbox; @@ -48,7 +54,6 @@ import com.mapbox.services.android.navigation.ui.v5.OnNavigationReadyCallback; import com.mapbox.services.android.navigation.ui.v5.listeners.NavigationListener; import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigationOptions; -import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute; import com.mapbox.services.android.navigation.v5.navigation.camera.Camera; import com.mapbox.services.android.navigation.v5.navigation.camera.RouteInformation; import com.mapbox.services.android.navigation.v5.offroute.OffRoute; @@ -56,6 +61,11 @@ import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress; import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgressState; import com.mapbox.services.android.navigation.v5.utils.RouteUtils; +import com.smtp.smtp.BuildConfig; +import com.smtp.smtp.R; +import com.smtp.smtp.Sortie; +import com.smtp.smtp.http.CoefJSONAsyncRequest; +import com.smtp.smtp.http.RequestManager; import org.json.JSONArray; import org.json.JSONException; @@ -64,21 +74,21 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import java.util.logging.Level; import io.socket.client.IO; +import io.socket.client.Manager; import io.socket.client.Socket; import io.socket.emitter.Emitter; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; @SuppressLint("MissingPermission") public class Navigation extends AppCompatActivity implements NavigationListener, OnNavigationReadyCallback, ProgressChangeListener{ + private static final int ONE_HUNDRED_MILLISECONDS = 100; private NavigationView navigationView; private TextView timeDiffTextView; private DirectionsRoute route; @@ -86,30 +96,26 @@ public class Navigation extends AppCompatActivity implements NavigationListener, private final int INITIAL_ZOOM = 18; private final double INITIAL_TILT = 30; private final int DISTANCE_TOLERANCE = 5000; - private static final String TAG = "Navigation"; + static final String TAG = "Navigation"; private boolean isOffRoute = false; private String preOffRoute = ""; - private OffRoute neverOffRouteEngine = new OffRoute() { - @Override - public boolean isUserOffRoute(Location location, RouteProgress routeProgress, MapboxNavigationOptions options) { - // User will never be off-route - - return false; - } - }; - - private Point ORIGIN; - private Point DESTINATION; - + private Boolean sendingOffRoute = false; + private OffRoute neverOffRouteEngine; + private Point CHARGEMENT; + private Point DECHARGEMENT; + private Point destination; private String userId; private String chantierId; private String typeRoute; private String token; private JSONObject coordinates; private double remainingTime; + private Double remainingTimeCoef; private double timeDiffTruckAhead = Double.POSITIVE_INFINITY; - private String myEtat; + String myEtat; private Etape etape = null; + private Sortie sortie = null; + private Pause pause = null; private String etapeIdPrecedente = null; private int rayonChargement; private int rayonDéchargement; @@ -121,21 +127,25 @@ public boolean isUserOffRoute(Location location, RouteProgress routeProgress, Ma private List roadPoint = new ArrayList<>(); private Location location; private int remainingWaypoints = -1; - private int timeToSend = 3; private int delay = timeToSend; + // + private Cycle cycle; + // for paused button private String previousEtat; private Button buttonPause; private Button buttonReprendre; private boolean onPause = false; private NetworkStateReceiver networkStateReceiver = new NetworkStateReceiver(); + private List initialWaypoints; + private CycleManager cycleManager; // Connection to the socket server { try { - Log.i(TAG, "Instanciating a new socket"); + Log.d(TAG, "Instanciating a new socket"); mSocket = IO.socket(BuildConfig.API_URL); } catch (URISyntaxException e) { throw new RuntimeException(e); @@ -146,122 +156,18 @@ public boolean isUserOffRoute(Location location, RouteProgress routeProgress, Ma private CustomUEH UEH = new CustomUEH(new Runnable() { @Override public void run() { + try { + cycleManager.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } disconnectFromChantier(); } }); - public boolean isMyUserId(String userId) { - return userId.equals(this.userId); - } - - class User { - public String userId; - public Double ETA; - public String etat; - - public User(String userId, Double ETA, String etat) { - this.userId = userId; - this.ETA = ETA; - this.etat = etat; - } - public Double getETA() { - return ETA; - } - public String getUserId() { - return userId; - } - public String getEtat() { - return etat; - } - - - @Override - public String toString() { - return "User{ moi ?" + isMyUserId(this.userId) + ", ETA=" + ETA + ", etat='" + etat + '}'; - } - } - - class EtaSorter implements Comparator { - @Override - public int compare(User o1, User o2) { - return (int) (o1.getETA() - o2.getETA()); - } - } - - public class ListUser { - public ArrayList list; - - public ListUser() { - this.list = new ArrayList<>(); - } - - public boolean isAddable(User user) { - return user.getEtat().equals(myEtat); - } - - public int addList(User user) { - if (isAddable(user) && !isContainedUser(user.getUserId())) { - list.add(user); - Collections.sort(list, new EtaSorter()); - return 1; - } else { - return -1; - } - } - - public boolean isContainedUser(String userId) { - boolean res = false; - for (int i = 0; i < list.size(); i++) { - if (list.get(i).getUserId().equals(userId)) { - res = true; - } - } - return res; - } - - public void updateMyIndice(String userId) { - for (int i = 0; i < list.size(); i++) { - if (isMyUserId(list.get(i).getUserId())) { - myIndice = i; - Log.d(TAG, "changement indice : " + myIndice); - } - } - } - - public boolean sameEtat(User user) { - return user.getEtat().equals(myEtat); - } - - public void updateList(User user) { - if (!sameEtat(user)) { - this.deleteUser(user.getUserId()); - } else { - for (int i = 0; i < list.size(); i++) { - if (list.get(i).getUserId().equals(user.getUserId())) { - list.get(i).ETA = user.getETA(); - } - } - } - Collections.sort(list, new EtaSorter()); - } - - public void deleteUser(String userId) { - int res = -1; - for (int i = 0; i < list.size(); i++) { - if (list.get(i).getUserId().equals(userId)) { - res = i; - } - } - list.remove(res); - Collections.sort(list, new EtaSorter()); - } - } - - private ListUser myList = new ListUser(); - private int myIndice; @SuppressLint("MissingPermission") @Override @@ -269,7 +175,11 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "OnCreate"); ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); - Log.d(TAG, "LargeMemoryClass : " + am.getLargeMemoryClass()); + + AndroidLoggingHandler.reset(new AndroidLoggingHandler()); + java.util.logging.Logger.getLogger(Socket.class.getName()).setLevel(Level.FINEST); + java.util.logging.Logger.getLogger(io.socket.engineio.client.Socket.class.getName()).setLevel(Level.FINEST); + java.util.logging.Logger.getLogger(Manager.class.getName()).setLevel(Level.FINEST); Thread.setDefaultUncaughtExceptionHandler(UEH); @@ -277,15 +187,13 @@ protected void onCreate(Bundle savedInstanceState) { double[] origin = i.getDoubleArrayExtra("origin"); double[] destination = i.getDoubleArrayExtra("destination"); - ORIGIN = Point.fromLngLat(origin[0], origin[1]); - DESTINATION = Point.fromLngLat(destination[0], destination[1]); + CHARGEMENT = Point.fromLngLat(origin[0], origin[1]); + DECHARGEMENT = Point.fromLngLat(destination[0], destination[1]); userId = i.getStringExtra("userId"); chantierId = i.getStringExtra("chantierId"); - typeRoute = i.getStringExtra("typeRoute"); token = i.getStringExtra("token"); myEtat = i.getStringExtra("myEtat"); previousEtat = myEtat; - myList.addList(new User(userId, Double.POSITIVE_INFINITY, myEtat)); Mapbox.getInstance(this, getString(R.string.mapbox_access_token)); @@ -297,11 +205,14 @@ protected void onCreate(Bundle savedInstanceState) { navigationView.onCreate(savedInstanceState); IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); - //filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(networkStateReceiver, filter); registerReceiver(broadcastReceiver, new IntentFilter("NO_INTERNET")); retrieveLocation(); + + cycle = new Cycle(userId, myEtat); + cycle.addUser(new User(userId, Double.POSITIVE_INFINITY, myEtat)); + cycleManager = new CycleManager(this::fetchRoute, cycle); } private void retrieveLocation() { @@ -310,6 +221,7 @@ private void retrieveLocation() { LocationRequest locationRequest = LocationRequest.create(); // Location is requested every 0.5 seconds locationRequest.setInterval(500); + locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); LocationCallback locationCallback = new LocationCallback() { @Override @@ -338,6 +250,7 @@ private void onLocationRetrieved(Location l, LocationCallback locationCallback) mSocket.on("chantier/user/sentCoordinates", onUserSentCoordinates); mSocket.on("chantier/connect/success", onConnectToChantierSuccess); mSocket.on("chantier/user/disconnected", onUserDisconnected); + mSocket.on("chantier/detournement",onDetournement); mSocket.connect(); connectToChantier(); @@ -358,6 +271,11 @@ protected void onDestroy() { unregisterReceiver(broadcastReceiver); unregisterReceiver(networkStateReceiver); + mSocket.off("chantier/user/sentCoordinates", onUserSentCoordinates); + mSocket.off("chantier/connect/success", onConnectToChantierSuccess); + mSocket.off("chantier/user/disconnected", onUserDisconnected); + mSocket.off("chantier/detournement", onUserDisconnected); + navigationView.onDestroy(); } @@ -405,12 +323,18 @@ protected void onSaveInstanceState(@NonNull Bundle outState) { public void onCancelNavigation() { Log.d(TAG, "OnCancelNavigation"); disconnectFromChantier(); + try { + cycleManager.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } this.finish(); } @Override public void onNavigationFinished() { Log.d(TAG, "OnNavigationFinished"); + } @Override @@ -421,7 +345,7 @@ public void onNavigationRunning() { @Override public void onNavigationReady(boolean isRunning) { Log.d(TAG, "OnNavigationReady"); - + navigationView.retrieveNavigationMapboxMap().retrieveMap().getMarkers().clear(); fetchRayon(); fetchRoute(); modifyTimeDiffTruckAheadIfNecessary(); @@ -431,15 +355,21 @@ public void onNavigationReady(boolean isRunning) { Icon icon2 = iconFactory.fromResource(R.drawable.icon_dechargement); navigationView.retrieveNavigationMapboxMap().retrieveMap().addMarker(new MarkerOptions().title("Chargement") - .position(new LatLng(ORIGIN.latitude(), ORIGIN.longitude())) + .position(new LatLng(CHARGEMENT.latitude(), CHARGEMENT.longitude())) .icon(icon) ); navigationView.retrieveNavigationMapboxMap().retrieveMap().addMarker(new MarkerOptions().title("Déchargement") - .position(new LatLng(DESTINATION.latitude(), DESTINATION.longitude())) + .position(new LatLng(DECHARGEMENT.latitude(), DECHARGEMENT.longitude())) .icon(icon2) ); + cycleManager.start(); + + } + + public void print(){ + Log.d("CycleManager", "Hello"); } @@ -447,26 +377,10 @@ public void onNavigationReady(boolean isRunning) { BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - navigationView.stopNavigation(); - showAlertDialog(); + showAlertDetournementWithAutoDismiss("Pas de connexion internet","Pas de connexion internet","Fermer",10000); } }; - // Alert to show when internet is disabled - private void showAlertDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Erreur Réseaux"); - builder.setMessage("Pas de connexion internet") - .setCancelable(false); - builder.setPositiveButton("Quitter", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - finish(); - } - }); - AlertDialog alert = builder.create(); - alert.show(); - } - // listener for paused and reprendre buttons private void addListenerOnButton() { @@ -480,11 +394,15 @@ public void onClick(View arg0) { buttonPause.setEnabled(false); buttonReprendre.setVisibility(View.VISIBLE); buttonReprendre.setEnabled(true); + if(isOffRoute){ previousEtat = preOffRoute; }else{ previousEtat = myEtat; } + if(etape != null){ + pause = new Pause(etape.getEtapeId(),getApplicationContext()); + } myEtat = "pause"; onPause = true; } @@ -501,7 +419,17 @@ public void onClick(View view) { buttonPause.setEnabled(true); buttonPause.setVisibility(View.VISIBLE); timeDiffTextView.setVisibility(View.VISIBLE); - myEtat = previousEtat; + if(etape != null && pause != null){ + pause.sendFinPause(); + } + if(isOffRoute){ + myEtat = "offRoute"; + preOffRoute = previousEtat; + }else{ + myEtat = previousEtat; + preOffRoute = ""; + } + } } }); @@ -518,7 +446,6 @@ private void fetchRayon() { } catch (JSONException e) { e.printStackTrace(); } - Log.d(TAG, response.toString()); }, new com.android.volley.Response.ErrorListener() { @Override @@ -540,45 +467,23 @@ public Map getHeaders() throws AuthFailureError { private float getDistanceFromDestination(Location location) { float[] distanceFromDestination = new float[3]; - Point destination = null; + Log.d("offT", " myEtat "+ myEtat); - Log.d("offT", " isOfRoute "+ isOffRoute); + Log.d("offT", " isOffRoute "+ isOffRoute); Log.d("offT", " preOffRoute "+ preOffRoute); + if(isOffRoute){ - if (myEtat.equals("chargé") || preOffRoute.equals("chargé")) { - destination = DESTINATION; - } if(preOffRoute.equals("enDéchargement") || myEtat.equals("enDéchargement")){ myEtat = "enDéchargement"; preOffRoute = ""; isOffRoute = false; - destination = DESTINATION; - } - if (myEtat.equals("déchargé") || preOffRoute.equals("déchargé") ) { - destination = ORIGIN; } if(preOffRoute.equals("enChargement") || myEtat.equals("enChargement")){ myEtat = "enChargement"; preOffRoute = ""; isOffRoute = false; - destination = DESTINATION; - } - }else if(myEtat.equals("pause")) { - if(previousEtat.equals("chargé") || previousEtat.equals("enDéchargement")){ - destination = DESTINATION; - }else if (previousEtat.equals("déchargé") || previousEtat.equals("enChargement")) { - destination = ORIGIN; - } - }else{ - if (myEtat.equals("chargé") || myEtat.equals("enDéchargement")) { - destination = DESTINATION; - }else if (myEtat.equals("déchargé") || myEtat.equals("enChargement")) { - destination = ORIGIN; } } - if (destination == null) { - throw new Error("getDistanceFromDestination: destination cannot be null"); - } Location.distanceBetween( location.getLatitude(), location.getLongitude(), @@ -589,16 +494,11 @@ private float getDistanceFromDestination(Location location) { return distanceFromDestination[0]; } - public void handleOffRoute(RouteProgress routeProgress){ - Log.d("offR", " preOffRoute : " + preOffRoute); - Log.d("offR", " isOffRoute : " + isOffRoute); - Log.d("offR", " routeProgress? : " + (routeProgress.currentState() != null)); - Log.d("offR", " previousEtat : " + previousEtat); - + public void handleOffRoute(Location location, RouteProgress routeProgress){ if(myEtat.equals("enChargement") || myEtat.equals("enDéchargement") || myEtat.equals("pause") ){ preOffRoute = ""; isOffRoute = false; - }else{ + } else { // je suis sur la route et isOffRoute = true => je change isOfRoute en false if(routeProgress.currentState() != null && isOffRoute) { myEtat = preOffRoute; @@ -606,7 +506,7 @@ public void handleOffRoute(RouteProgress routeProgress){ isOffRoute = false; Log.d("offR", " sortie de route " + isOffRoute); }else{ - // je ne suis pas sur la route + // je ne suis plus sur la route et isOffRoute = false if(routeProgress.currentState() == null && !isOffRoute){ preOffRoute = myEtat; myEtat = "offRoute"; @@ -617,22 +517,49 @@ public void handleOffRoute(RouteProgress routeProgress){ } } } + sendOffRoute(location); + } + + public void sendOffRoute(Location location){ + // on commence un offRoute + if(!sendingOffRoute && isOffRoute){ + Log.d("Sortie", "débutSortie"); + sendingOffRoute = true; + sortie = new Sortie(chantierId,userId,typeRoute,getApplicationContext()); + sortie.addWaypoint(location.getLatitude(),location.getLongitude()); + } + // on continue + if(sendingOffRoute && isOffRoute){ + Log.d("Sortie", "ajoutPointSortie"); + // on conserve la meme Sortie ( ajout d'un Point) + sortie.addWaypoint(location.getLongitude(),location.getLatitude()); + } + // on arrete + if(sendingOffRoute && !isOffRoute){ + sendingOffRoute = false; + sortie.sendFinSortie(); + Log.d("Sortie", "finSortie"); + // on termine la Sortie ( dateFin) + } + } @Override public void onProgressChange(Location location, RouteProgress routeProgress) { - handleOffRoute(routeProgress); + handleOffRoute(location,routeProgress); boolean didEtatChanged; float distanceFromDestination = getDistanceFromDestination(location); this.location = location; - this.remainingTime = routeProgress.durationRemaining() * 1.25; + this.remainingTime = Math.max(0, routeProgress.durationRemaining() * this.remainingTimeCoef); didEtatChanged = changeMyEtatIfNecessary(distanceFromDestination); if (rerouteUserIfNecessary(didEtatChanged)) { return; } + modifyTimeDiffTruckAheadIfNecessary(); + Log.d(TAG, "Number of remaining waypoints: " + routeProgress.remainingWaypoints()); //Register remaining waypoints in SharedPreferences if(remainingWaypoints != routeProgress.remainingWaypoints()) { @@ -654,7 +581,7 @@ public void onProgressChange(Location location, RouteProgress routeProgress) { Log.d(TAG, Integer.toString(remainingWaypoints)); for (Point p: remaininPoints ) { - Log.d(TAG, p.toString()); + Log.d(TAG, "Remaining waypoints: " + p.toString()); } coordinates = new JSONObject(); @@ -670,7 +597,7 @@ public void onProgressChange(Location location, RouteProgress routeProgress) { if(timeToSend()){ sendCoordinates(); } - myList.updateList(new User(userId, remainingTime, myEtat)); + cycle.updateCycle(new User(userId, remainingTime, myEtat)); } } @@ -687,7 +614,7 @@ private boolean timeToSend() { public void prepareRoute(JSONArray array) throws JSONException { // Ajoute chaque donnée à la liste de waypoint - List initialWaypoints = new ArrayList<>(); + initialWaypoints = new ArrayList<>(); for (int i = 0; i < array.length(); i++) { JSONObject waypoint = array.getJSONObject(i); initialWaypoints.add( @@ -704,13 +631,13 @@ public void prepareRoute(JSONArray array) throws JSONException { initialWaypoints, location, DISTANCE_TOLERANCE, - getRemainingWaypointsFromSharedPreferences(initialWaypoints) + getRemainingWaypointsFromSharedPreferences() ); initialWaypoints = filter.cleanWaypoints(); for (Waypoint wp: - initialWaypoints) { + initialWaypoints) { Log.d(TAG, "Cleaned waypoints: " + wp.toString()); } @@ -719,6 +646,7 @@ public void prepareRoute(JSONArray array) throws JSONException { for (Waypoint waypoint : initialWaypoints) { points.add(waypoint.getPoint()); } + points.add(destination); roadPoint = points; } @@ -730,18 +658,18 @@ private void registerRemainingWaypointInSharedPreferences() { editor.apply(); } - public List getRemainingWaypointsFromSharedPreferences(List initialWaypointList){ + public List getRemainingWaypointsFromSharedPreferences(){ Log.d(TAG, "Getting remaining points in " + chantierId + typeRoute + "remainingWaypoints"); SharedPreferences sharedPref = getPreferences(MODE_PRIVATE); int nbRemainingWp = sharedPref.getInt(chantierId + typeRoute + "remainingWaypoints", -1); Log.d(TAG, "nbRemainingWaypoints:" + nbRemainingWp); if (nbRemainingWp == -1) { - return initialWaypointList; - } else if (nbRemainingWp > initialWaypointList.size()) { + return initialWaypoints; + } else if (nbRemainingWp > initialWaypoints.size()) { removeRemainingWaypointsFromSharedPreferences(); - return initialWaypointList; + return initialWaypoints; } - List res = new ArrayList<>(initialWaypointList.subList(initialWaypointList.size()-nbRemainingWp, initialWaypointList.size())); + List res = new ArrayList<>(initialWaypoints.subList(initialWaypoints.size()-nbRemainingWp, initialWaypoints.size())); Collections.sort(res); return res; } @@ -757,51 +685,46 @@ private void removeRemainingWaypointsFromSharedPreferences() { private void fetchRoute() { if (myEtat.equals("chargé") || myEtat.equals("enDéchargement")) { typeRoute = "aller"; + destination = DECHARGEMENT; } if (myEtat.equals("déchargé") || myEtat.equals("enChargement")) { typeRoute = "retour"; + destination = CHARGEMENT; } - initWaypoints(); + + fetchCoef(() -> initWaypoints()); } - private void buildRoute() { + private void fetchCoef(Runnable then) { + CoefJSONAsyncRequest coefAsyncRequest = new CoefJSONAsyncRequest(getApplicationContext(), token); + HashMap coefRequestArgs = new HashMap<>(); + coefRequestArgs.put("chantierId", this.chantierId); + coefRequestArgs.put("typeRoute", this.typeRoute); + coefRequestArgs.put("day", Helper.getDayString()); + coefAsyncRequest.getByChantierAndTypeRouteAndDay(coefRequestArgs, fetchedCoef -> { + this.remainingTimeCoef = fetchedCoef; + then.run(); + }); + } - NavigationRoute.Builder builder = NavigationRoute.builder(this) - .accessToken("pk." + getString(R.string.gh_key)) - .baseUrl(getString(R.string.base_url)) - //.baseUrl("https://router.project-osrm.org/route/v1/") - .user("gh") - .origin(roadPoint.get(0)) - .destination(roadPoint.get(roadPoint.size() - 1)) - .profile("car"); - // add waypoints without first and last point - if (roadPoint.size() > 2) { - for (int i = 1; i < roadPoint.size() - 1; i++) { - builder.addWaypoint(roadPoint.get(i)); - } - } - NavigationRoute navRoute = builder.build(); + private void buildRoute() { + Consumer matchGetSuccess = (route) -> { + Navigation.this.route = route; + launchNavigation(); + }; - navRoute.getRoute( - new Callback() { - @Override - public void onResponse(Call call, Response response) { - Log.d(TAG, call.request().url().toString()); - Log.d(TAG, response.message()); - if (validRouteResponse(response)) { - route = response.body().routes().get(0); - launchNavigation(); - } else { - Snackbar.make(navigationView, "Erreur au calcul de la route", Snackbar.LENGTH_LONG).show(); - } - } + Consumer routeGetFailure = (t) -> { + Snackbar.make(navigationView, "Error getting the route", Snackbar.LENGTH_LONG).show(); + Log.e(TAG, t.getLocalizedMessage()); + }; + Consumer routeGetSuccess = (route) -> { + MapMatcher mapMatcher = new MapMatcher(getApplicationContext(), route); + mapMatcher.getMatch( matchGetSuccess, routeGetFailure); + }; - @Override - public void onFailure(Call call, Throwable throwable) { - Snackbar.make(navigationView, "Erreur au calcul de la route", Snackbar.LENGTH_LONG).show(); - } - }); + RouteGetter routeGetter = new RouteGetter(getApplicationContext(), roadPoint); + routeGetter.getRoute(routeGetSuccess, routeGetFailure); } private void launchNavigation() { @@ -829,13 +752,20 @@ public List overview(RouteInformation routeInformation) { } }; + navigationView.startNavigation(navViewBuilderOptions.build()); navigationView.retrieveMapboxNavigation().setCameraEngine(camera); - navigationView.retrieveMapboxNavigation().setOffRouteEngine(neverOffRouteEngine); + setOffRouteEngine(); } - private boolean validRouteResponse(Response response) { - return response.body() != null && !response.body().routes().isEmpty(); + private void setOffRouteEngine() { + neverOffRouteEngine = new OffRoute() { + @Override + public boolean isUserOffRoute(Location location, RouteProgress routeProgress, MapboxNavigationOptions options) { + return false; + } + }; + navigationView.retrieveMapboxNavigation().setOffRouteEngine(neverOffRouteEngine); } public void initWaypoints() { @@ -851,12 +781,11 @@ public void initWaypoints() { } catch (JSONException e) { e.printStackTrace(); } - Log.d(TAG, response.toString()); }, new com.android.volley.Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { - Log.d(TAG, error.toString() + error.networkResponse); + Log.e(TAG, error.toString() + " " + error.networkResponse.toString()); } } ) { @@ -885,8 +814,6 @@ private void changeEtape(){ } private boolean changeMyEtatIfNecessary(double distanceRemaining) { - boolean etatChanged = false; - String previousEtat = myEtat; int rayonChangementEtat = 0; if (typeRoute.equals("aller")) { rayonChangementEtat = rayonDéchargement; @@ -898,57 +825,55 @@ private boolean changeMyEtatIfNecessary(double distanceRemaining) { if (myEtat.equals("chargé") || preOffRoute.equals("chargé")) { myEtat = "enDéchargement"; changeEtape(); - etatChanged = true; return true; } else if (myEtat.equals("déchargé") || preOffRoute.equals("déchargé")) { myEtat = "enChargement"; changeEtape(); - etatChanged = true; return true; } } else { if (myEtat.equals("enChargement")) { myEtat = "chargé"; changeEtape(); - etatChanged = true; return true; } else if (myEtat.equals("enDéchargement")) { myEtat = "déchargé"; changeEtape(); - etatChanged = true; return true; } } - if (etatChanged) { - Log.d(TAG, "Etat changed: from " + previousEtat + " to " + myEtat); - } return false; } private boolean rerouteUserIfNecessary(boolean etatChanged) { boolean userRerouted = false; - if (etatChanged) { - if (myEtat.equals("chargé") || myEtat.equals("déchargé")) { - roadPoint.clear(); - navigationView.stopNavigation(); - navigationView.retrieveNavigationMapboxMap().clearMarkers(); - Log.d(TAG, "User rerouted"); - userRerouted = true; - } - } + if(!etatChanged) return userRerouted; + if(!userIsInReroutableState()) return userRerouted; + + roadPoint.clear(); + navigationView.stopNavigation(); + navigationView.retrieveNavigationMapboxMap().clearMarkers(); + Log.d(TAG, "User rerouted"); + userRerouted = true; + + if (userRerouted) { fetchRoute(); } return userRerouted; } + private boolean userIsInReroutableState() { + return myEtat.equals("chargé") || myEtat.equals("déchargé"); + } + private void modifyTimeDiffTruckAheadIfNecessary() { if (myEtat.equals("enChargement")) { timeDiffTextView.setText("Vous êtes en cours de chargement \nQuittez la zone une fois chargé"); } else if (myEtat.equals("enDéchargement")) { timeDiffTextView.setText("Vous êtes en cours de déchargement \nQuittez la zone une fois déchargé"); - } else if (myIndice > 0 && myList.list.size() > 1) { - User userAhead = myList.list.get(myIndice - 1); + } else if (cycle.someOneIsAheadMe()) { + User userAhead = cycle.getUserAhead(); timeDiffTruckAhead = Math.abs(remainingTime - userAhead.getETA()); int minutes = (int) Math.floor(timeDiffTruckAhead / 60); int secondes = (int) Math.floor(timeDiffTruckAhead % 60); @@ -1006,12 +931,12 @@ private void connectToChantier() { senderEtat = data.getString("etat"); senderId = data.getString("userId"); User sender = new User(senderId, senderETA, senderEtat); - if (myList.isContainedUser(senderId)) { - myList.updateList(sender); + if (cycle.isContainedUser(senderId)) { + cycle.updateCycle(sender); } else { - myList.addList(sender); + cycle.addUser(sender); } - myList.updateMyIndice(Navigation.this.userId); + cycle.updateMyIndice(Navigation.this.userId); modifyTimeDiffTruckAheadIfNecessary(); } catch (JSONException e) { Log.e(TAG, e.getMessage()); @@ -1030,12 +955,65 @@ private void connectToChantier() { String senderId; try { senderId = data.getString("userId"); - if (myList.isContainedUser(senderId)) { - myList.deleteUser(senderId); + if (cycle.isContainedUser(senderId)) { + cycle.deleteUser(senderId); } else { Log.d(TAG, " impossible to delete : user not in the list "); } + } catch (JSONException e) { + Log.e(TAG, e.getMessage()); + return; + } + modifyTimeDiffTruckAheadIfNecessary(); + }); + + private void rerouting(JSONObject data) throws JSONException { + navigationView.stopNavigation(); + connectedToChantier = false; + mSocket.emit("chantier/disconnect"); + chantierId = data.getString("chantierId"); + Double originLong = data.getDouble("originLong"); + Double originLat = data.getDouble("originLat"); + Double destinationLong = data.getDouble("destinationLong"); + Double destinationLat = data.getDouble("destinationLat"); + CHARGEMENT = Point.fromLngLat(originLong,originLat); + DECHARGEMENT = Point.fromLngLat(destinationLong,destinationLat); + cycle.clear(); + cycle.addUser(new User(userId, Double.POSITIVE_INFINITY, myEtat)); + connectToChantier(); + navigationView.retrieveNavigationMapboxMap().clearMarkers(); + navigationView.retrieveNavigationMapboxMap().retrieveMap().getMarkers().clear(); + fetchRayon(); + fetchRoute(); + modifyTimeDiffTruckAheadIfNecessary(); + + IconFactory iconFactory = IconFactory.getInstance(getApplicationContext()); + Icon icon = iconFactory.fromResource(R.drawable.icon_chargement); + Icon icon2 = iconFactory.fromResource(R.drawable.icon_dechargement); + + navigationView.retrieveNavigationMapboxMap().retrieveMap().addMarker(new MarkerOptions().title("Chargement") + .position(new LatLng(CHARGEMENT.latitude(), CHARGEMENT.longitude())) + .icon(icon) + ); + + navigationView.retrieveNavigationMapboxMap().retrieveMap().addMarker(new MarkerOptions().title("Déchargement") + .position(new LatLng(DECHARGEMENT.latitude(), DECHARGEMENT.longitude())) + .icon(icon2) + ); + } + private Emitter.Listener onDetournement = args -> runOnUiThread(() -> { + Log.e("Detournement", "onDetournement"); + JSONObject data = (JSONObject) args[0]; + Log.e("Detournement", "data "+ data.toString()); + String userIdToMove; + try { + userIdToMove = data.getString("userId"); + if (userIdToMove.equals(userId)) { + rerouting(data); + vibrateAndNotify(); + showAlertDetournementWithAutoDismiss("Détournement !","Vous avez été détourné vers un autre chantier","Fermer",5000); + } } catch (JSONException e) { Log.e(TAG, e.getMessage()); return; @@ -1043,4 +1021,46 @@ private void connectToChantier() { modifyTimeDiffTruckAheadIfNecessary(); }); + @SuppressLint("MissingPermission") + private void vibrateAndNotify() { + Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); + if (vibrator == null) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vibrator.vibrate(VibrationEffect.createOneShot(ONE_HUNDRED_MILLISECONDS, VibrationEffect.DEFAULT_AMPLITUDE)); + } else { + vibrator.vibrate(ONE_HUNDRED_MILLISECONDS); + } + try { + Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification); + r.play(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void showAlertDetournementWithAutoDismiss(String title, String message, String messageButton, int delayTime) { + AlertDialog.Builder builder = new AlertDialog.Builder(Navigation.this); + builder.setTitle(title) + .setMessage(message) + .setCancelable(false).setCancelable(false) + .setPositiveButton(messageButton, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + //this for skip dialog + dialog.cancel(); + } + }); + final AlertDialog alertDialog = builder.create(); + alertDialog.show(); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + if (alertDialog.isShowing()){ + alertDialog.dismiss(); + } + } + }, delayTime); //change 5000 with a specific time you want + } } diff --git a/android/app/src/main/java/com/smtp/smtp/NetworkStateReceiver.java b/android/app/src/main/java/com/smtp/smtp/navigation/NetworkStateReceiver.java similarity index 96% rename from android/app/src/main/java/com/smtp/smtp/NetworkStateReceiver.java rename to android/app/src/main/java/com/smtp/smtp/navigation/NetworkStateReceiver.java index b4cfa77..71dc0b0 100644 --- a/android/app/src/main/java/com/smtp/smtp/NetworkStateReceiver.java +++ b/android/app/src/main/java/com/smtp/smtp/navigation/NetworkStateReceiver.java @@ -1,4 +1,4 @@ -package com.smtp.smtp; +package com.smtp.smtp.navigation; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/android/app/src/main/java/com/smtp/smtp/Etape.java b/android/app/src/main/java/com/smtp/smtp/navigation/Pause.java similarity index 69% rename from android/app/src/main/java/com/smtp/smtp/Etape.java rename to android/app/src/main/java/com/smtp/smtp/navigation/Pause.java index a28e4f0..bba982c 100644 --- a/android/app/src/main/java/com/smtp/smtp/Etape.java +++ b/android/app/src/main/java/com/smtp/smtp/navigation/Pause.java @@ -1,83 +1,66 @@ -package com.smtp.smtp; +package com.smtp.smtp.navigation; import android.content.Context; import android.util.Log; -import androidx.appcompat.app.AppCompatActivity; - import com.android.volley.AuthFailureError; -import com.android.volley.Cache; -import com.android.volley.Network; import com.android.volley.NetworkResponse; import com.android.volley.Request; -import com.android.volley.RequestQueue; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.VolleyLog; -import com.android.volley.toolbox.BasicNetwork; -import com.android.volley.toolbox.DiskBasedCache; import com.android.volley.toolbox.HttpHeaderParser; -import com.android.volley.toolbox.HurlStack; import com.android.volley.toolbox.StringRequest; -import com.android.volley.toolbox.Volley; -import com.google.android.material.snackbar.Snackbar; +import com.smtp.smtp.BuildConfig; +import com.smtp.smtp.http.RequestManager; import org.json.JSONObject; + import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; -import java.sql.Timestamp; - import java.util.UUID; -public class Etape extends AppCompatActivity { - private static final String BASE_URL = "http://smtp-dev-env.eba-5jqrxjhz.eu-west-3.elasticbeanstalk.com/"; +public class Pause { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImJiYTg0YmM3LTlmNDMtNDAxZS04ZjAyLTQ3ZTAyZDc4NDQ2OCIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTU4NzQxODQ0MX0.zRTuqPl0UbiwJn7zZSxErvBYhkhPibEZ51S4Aqgd6LI"; - private String etapeId; + + private String pauseId; private long dateDebut; private long dateFin; - private String chantierId; - private String camionneurId; - private String type; - private String etapePrec; private Context ctx; + private String etapeId; - public Etape(String chantierId, String camionneurId, String type, String etapePrec, Context context){ - this.etapeId = UUID.randomUUID().toString(); + public Pause(String etapeId, Context ctx){ + this.pauseId = UUID.randomUUID().toString(); this.dateDebut = System.currentTimeMillis(); - this.chantierId = chantierId; - this.camionneurId = camionneurId; - this.type = type; - this.etapePrec = etapePrec; - ctx = context; - sendDebutEtape(); + this.etapeId = etapeId; + this.ctx = ctx; + this.sendDebutPause(); } - public void sendDebutEtape(){ + public void sendDebutPause(){ Map send = new HashMap<>(); send.put("dateDebut",this.dateDebut); - send.put("id",this.etapeId); - send.put("ChantierId",this.chantierId); - send.put("CamionneurId",this.camionneurId); - send.put("type",this.type); - send.put("etapePrecId",this.etapePrec); - JSONObject etape = new JSONObject(send); + send.put("id",this.pauseId); + send.put("EtapeId",this.etapeId); + JSONObject pause = new JSONObject(send); - final String requestBody = etape.toString(); - final String URL = BASE_URL+"etapes"; + final String requestBody = pause.toString(); + final String URL = BuildConfig.API_URL +"pauses"; - Log.d("Etape", requestBody); + Log.d("Pause", requestBody); StringRequest stringRequest = new StringRequest(Request.Method.POST, URL, new Response.Listener() { @Override public void onResponse(String response) { - Log.d("Etape", response); + + Log.d("Pause", "res" + response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { - Log.e("Etape", error.toString()); + Log.e("Pause", error.toString()); } }) { //This is for Headers If You Needed @@ -112,26 +95,26 @@ protected Response parseNetworkResponse(NetworkResponse response) { RequestManager.getInstance(ctx).addToRequestQueue(stringRequest); } - public void sendFinEtape(){ + public void sendFinPause(){ Map send = new HashMap<>(); + this.dateFin = System.currentTimeMillis(); + send.put("dateFin",this.dateFin); + JSONObject pause = new JSONObject(send); - send.put("dateFin",System.currentTimeMillis()); - JSONObject etape = new JSONObject(send); - - final String requestBody = etape.toString(); - final String URL = BASE_URL+"etapes/"+etapeId; + final String requestBody = pause.toString(); + final String URL = BuildConfig.API_URL +"pauses/"+ this.pauseId; - Log.d("Etape", requestBody); + Log.d("Pause", requestBody); StringRequest stringRequest = new StringRequest(Request.Method.PATCH, URL, new Response.Listener() { @Override public void onResponse(String response) { - Log.d("Etape", response); + Log.d("Pause", response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { - Log.e("Etape", error.toString()); + Log.e("Pause", error.toString()); } }) { //This is for Headers If You Needed @@ -165,8 +148,4 @@ protected Response parseNetworkResponse(NetworkResponse response) { }; RequestManager.getInstance(ctx).addToRequestQueue(stringRequest);; } - - public String getEtapeId() { - return etapeId; - } } diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/RouteGetter.java b/android/app/src/main/java/com/smtp/smtp/navigation/RouteGetter.java new file mode 100644 index 0000000..f78438b --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/RouteGetter.java @@ -0,0 +1,86 @@ +package com.smtp.smtp.navigation; + +import android.content.Context; +import android.util.Log; + +import com.mapbox.api.directions.v5.models.DirectionsResponse; +import com.mapbox.api.directions.v5.models.DirectionsRoute; +import com.mapbox.api.matching.v5.MapboxMapMatching; +import com.mapbox.geojson.Point; +import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute; +import com.smtp.smtp.BuildConfig; +import com.smtp.smtp.R; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Consumer; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class RouteGetter { + private String TAG = "RouteGetter"; + private Context ctx; + private List roadPoints; + + public RouteGetter(Context ctx, List roadPoints){ + this.ctx = ctx; + this.roadPoints = roadPoints; + } + + public void getRoute(Consumer success, Consumer failure) { + NavigationRoute navRoute = getNavigationRoute(); + + navRoute.getRoute( + new Callback() { + @Override + public void onResponse(Call call, Response response) { + Log.d(TAG, "RouteGetting completed"); + if (validRouteResponse(response)) { + DirectionsRoute route = response.body().routes().get(0); + success.accept(route); + } else { + failure.accept(new Throwable("Route response is invalid!")); + } + } + @Override + public void onFailure(Call call, Throwable throwable) { + failure.accept(throwable); + } + }); + } + + @NotNull + private NavigationRoute getNavigationRoute() { + Log.d(TAG, "API_URL: " + BuildConfig.API_URL); + NavigationRoute.Builder builder = getBuilder(); + addWaypointToRouteBuilder(builder); + return builder.build(); + } + + @NotNull + private NavigationRoute.Builder getBuilder() { + return NavigationRoute.builder(ctx) + .accessToken("pk." + ctx.getString(R.string.gh_key)) + .baseUrl(BuildConfig.API_URL) + .user("gh") + .origin(roadPoints.get(0)) + .destination(roadPoints.get(roadPoints.size() - 1)) + .continueStraight(false) + .profile("car"); + } + + private void addWaypointToRouteBuilder(NavigationRoute.Builder builder) { + if (roadPoints.size() > 2) { + for (int i = 1; i < roadPoints.size() - 1; i++) { + builder.addWaypoint(roadPoints.get(i)); + } + } + } + + private boolean validRouteResponse(Response response) { + return response.body() != null && !response.body().routes().isEmpty(); + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/User.java b/android/app/src/main/java/com/smtp/smtp/navigation/User.java new file mode 100644 index 0000000..0e29b60 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/User.java @@ -0,0 +1,47 @@ +package com.smtp.smtp.navigation; + +import androidx.annotation.Nullable; + +public class User implements Comparable { + public String userId; + public Double ETA; + public String etat; + + public User(String userId, Double ETA, String etat) { + this.userId = userId; + this.ETA = ETA; + this.etat = etat; + } + + public Double getETA() { + return ETA; + } + + public String getUserId() { + return userId; + } + + public String getEtat() { + return etat; + } + + @Override + public int compareTo(Object o) { + User u = (User)o; + if (u.getUserId().equals(userId)) return 0; + return -1; + } + + @Override + public boolean equals(@Nullable Object obj) { + return compareTo(obj) == 0; + } + + public void setETA(Double eta) { + this.ETA = eta; + } + + public void setState(String state) { + etat = state; + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/Waypoint.java b/android/app/src/main/java/com/smtp/smtp/navigation/Waypoint.java similarity index 83% rename from android/app/src/main/java/com/smtp/smtp/Waypoint.java rename to android/app/src/main/java/com/smtp/smtp/navigation/Waypoint.java index 52ad883..f05a4fe 100644 --- a/android/app/src/main/java/com/smtp/smtp/Waypoint.java +++ b/android/app/src/main/java/com/smtp/smtp/navigation/Waypoint.java @@ -1,19 +1,19 @@ -package com.smtp.smtp; +package com.smtp.smtp.navigation; import com.mapbox.geojson.Point; import androidx.annotation.NonNull; import androidx.annotation.Nullable; public class Waypoint implements Comparable { - double longitude; - double latitude; - int ordre; - Waypoint(double longitude, double latitude, int ordre){ + public double longitude; + public double latitude; + public int ordre; + public Waypoint(double longitude, double latitude, int ordre){ this.longitude = longitude; this.latitude = latitude; this.ordre = ordre; } - Waypoint(double longitude, double latitude){ + public Waypoint(double longitude, double latitude){ this.longitude = longitude; this.latitude = latitude; ordre = -1; diff --git a/android/app/src/main/java/com/smtp/smtp/WaypointFilter.java b/android/app/src/main/java/com/smtp/smtp/navigation/WaypointFilter.java similarity index 99% rename from android/app/src/main/java/com/smtp/smtp/WaypointFilter.java rename to android/app/src/main/java/com/smtp/smtp/navigation/WaypointFilter.java index de3221a..f008efa 100644 --- a/android/app/src/main/java/com/smtp/smtp/WaypointFilter.java +++ b/android/app/src/main/java/com/smtp/smtp/navigation/WaypointFilter.java @@ -1,4 +1,4 @@ -package com.smtp.smtp; +package com.smtp.smtp.navigation; import android.location.Location; import android.util.Log; diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/test/TestCycle.java b/android/app/src/main/java/com/smtp/smtp/navigation/test/TestCycle.java new file mode 100644 index 0000000..2e700fe --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/test/TestCycle.java @@ -0,0 +1,119 @@ +package com.smtp.smtp.navigation.test; + +import com.smtp.smtp.navigation.Cycle; +import com.smtp.smtp.navigation.User; + +import org.junit.Assert; +import org.junit.Test; + + +public class TestCycle { + + @Test + public void cloneStateIsDifferentAfterModification() throws CloneNotSupportedException { + Cycle c1 = new Cycle("1","chargé"); + Cycle c2 = c1.cloneMe(); + c2.setMyEtat("déchargé"); + Assert.assertNotEquals(c1.getMyEtat(), c2.getMyEtat()); + } + + @Test + public void cycleCloneListIsDifferentAfterModification() throws CloneNotSupportedException { + Cycle c1 = new Cycle("1","chargé"); + User u1 = new User("1", 1.0, "chargé"); + User u2 = new User("2", 2.0, "chargé"); + c1.addUser(u1); + c1.addUser(u2); + Cycle c2 = c1.cloneMe(); + c1.deleteUser(u1.getUserId()); + Assert.assertTrue( "Cycle clone must keep his element when it is deleted from original", + c2.getUsers().contains(u1) && c2.getUsers().contains(u2)); + Assert.assertNotEquals("Cycle clone size must be different than original after modification", + c1.getUsers().size(), c2.getUsers().size()); + Assert.assertNotEquals("Cycle clone must be different than original after modification", + c1.getUsers(), c2.getUsers()); + } + + @Test + public void cycleOrderIsDifferentAfterUpdateWithSmallerETA() { + Cycle c1 = new Cycle("1","chargé"); + User u1 = new User("1", 2.0, "chargé"); + User u2 = new User("2", 3.0, "chargé"); + c1.addUser(u1); + c1.addUser(u2); + u2.setETA(1.0); + c1.updateCycle(u2); + Assert.assertEquals("User with smaller ETA should be first", + c1.getUsers().get(0), u2 ); + Assert.assertEquals("User with bigger ETA should be second", + c1.getUsers().get(1), u1 ); + } + + @Test + public void userIsNotInTheCycleAfterHisStateChanged() { + Cycle c1 = new Cycle("1","chargé"); + User u1 = new User("1", 2.0, "chargé"); + User u2 = new User("2", 3.0, "chargé"); + c1.addUser(u1); + c1.addUser(u2); + u2.setState("déchargé"); + c1.updateCycle(u2); + + Assert.assertEquals("", c1.getUsers().size(), 1); + } + + @Test + public void cycleAreNotEqualsAfterOneUserLeft() throws CloneNotSupportedException { + Cycle c1 = new Cycle("1","chargé"); + User u1 = new User("1", 2.0, "chargé"); + User u2 = new User("2", 3.0, "chargé"); + c1.addUser(u1); + c1.addUser(u2); + Cycle c2 = c1.cloneMe(); + c1.deleteUser("1"); + + Assert.assertTrue("Two cycle must not be equals if one user left", + !c1.equals(c2)); + } + + @Test + public void cycleAreEqualsAfterClone() throws CloneNotSupportedException { + Cycle c1 = new Cycle("1","chargé"); + User u1 = new User("1", 2.0, "chargé"); + User u2 = new User("2", 3.0, "chargé"); + c1.addUser(u1); + c1.addUser(u2); + Cycle c2 = c1.cloneMe(); + + Assert.assertTrue("Two cycle must be equals after clone", + c1.equals(c2)); + } + + @Test + public void cycleListAreEqualsAfterClone() throws CloneNotSupportedException { + Cycle c1 = new Cycle("1","chargé"); + User u1 = new User("1", 2.0, "chargé"); + User u2 = new User("2", 3.0, "chargé"); + c1.addUser(u1); + c1.addUser(u2); + Cycle c2 = c1.cloneMe(); + + Assert.assertTrue("Two cycle list must be equals after clone", + c1.getUsers().equals(c2.getUsers())); + } + + @Test + public void cycleAreEqualsEvenIfUserOrderIsDifferent() throws CloneNotSupportedException { + Cycle c1 = new Cycle("1","chargé"); + User u1 = new User("1", 2.0, "chargé"); + User u2 = new User("2", 3.0, "chargé"); + c1.addUser(u1); + c1.addUser(u2); + Cycle c2 = c1.cloneMe(); + u2.setETA(1.0); + c1.updateCycle(u2); + + Assert.assertTrue("Two cycle must be equals even if order is different", + c1.equals(c2)); + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/test/TestCycleManager.java b/android/app/src/main/java/com/smtp/smtp/navigation/test/TestCycleManager.java new file mode 100644 index 0000000..91898c7 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/test/TestCycleManager.java @@ -0,0 +1,32 @@ +package com.smtp.smtp.navigation.test; + +import com.smtp.smtp.navigation.Cycle; +import com.smtp.smtp.navigation.CycleManager; +import com.smtp.smtp.navigation.User; + +import org.junit.Assert; +import org.junit.Test; + +public class TestCycleManager { + @Test + public void newRouteIsFetchedWhenCycleChange() { + Cycle c = new Cycle("1", "chargé"); + User u1 = new User("1", 2.0, "chargé"); + User u2 = new User("2", 3.0, "chargé"); + c.addUser(u1); + c.addUser(u2); + CycleManager cycleManager = new CycleManager(() -> {}, c); + u2.setState("déchargé"); + c.updateCycle(u2); + + cycleManager.start(); + try { + Thread.sleep(1000); + Assert.assertTrue(cycleManager.newRouteHasBeenFetched()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/navigation/test/TestUser.java b/android/app/src/main/java/com/smtp/smtp/navigation/test/TestUser.java new file mode 100644 index 0000000..d8ef068 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/navigation/test/TestUser.java @@ -0,0 +1,22 @@ +package com.smtp.smtp.navigation.test; + +import com.smtp.smtp.navigation.User; + +import org.junit.Assert; +import org.junit.Test; + +public class TestUser { + @Test + public void userWithSameIdAreEquals(){ + User u1 = new User("1", 1.0, "chargé"); + User u2 = new User("1", 2.0, "déchargé"); + Assert.assertTrue(u1.equals(u2)); + } + + @Test + public void userWithDifferentIdAreNotEquals(){ + User u1 = new User("1", 1.0, "chargé"); + User u2 = new User("2", 2.0, "déchargé"); + Assert.assertFalse(u1.equals(u2)); + } +} diff --git a/android/app/src/main/java/com/smtp/smtp/road/MapboxRing.java b/android/app/src/main/java/com/smtp/smtp/road/MapboxRing.java new file mode 100644 index 0000000..6cd7182 --- /dev/null +++ b/android/app/src/main/java/com/smtp/smtp/road/MapboxRing.java @@ -0,0 +1,65 @@ +package com.smtp.smtp.road; + +import android.graphics.Color; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.mapbox.geojson.LineString; +import com.mapbox.geojson.Point; +import com.mapbox.geojson.Polygon; +import com.mapbox.mapboxsdk.maps.Style; +import com.mapbox.mapboxsdk.style.layers.FillLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.turf.TurfMeta; +import com.mapbox.turf.TurfTransformation; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor; +import static com.mapbox.turf.TurfConstants.UNIT_METERS; + +public class MapboxRing { + private Point center; + private Integer radius; + private String sourceId; + private String layerId; + private GeoJsonSource source; + private FillLayer layer; + private int color; + private int meterDifferenceBetweenCircles; + + MapboxRing(Point center, Integer radius, String sourceId, String layerId, Integer meterDifferenceBetweenCircles) { + this.center = center; + this.radius = radius; + this.sourceId = sourceId; + this.layerId = layerId; + this.color = Color.argb(125, 255, 0, 0); + this.source = new GeoJsonSource(sourceId); + this.layer = new FillLayer(layerId, this.sourceId).withProperties(fillColor(color)); + this.meterDifferenceBetweenCircles = meterDifferenceBetweenCircles; + + computeSource(); + } + + private void computeSource(){ + Polygon outerCirclePolygon = getTurfPolygonCircle(radius + this.meterDifferenceBetweenCircles, center); + Polygon innerCirclePolygon = getTurfPolygonCircle( + radius, center); + if (source != null) { + source.setGeoJson(Polygon.fromOuterInner( + LineString.fromLngLats(TurfMeta.coordAll(outerCirclePolygon, false)), + LineString.fromLngLats(TurfMeta.coordAll(innerCirclePolygon, false)) + )); + } + } + + public void addToStyle(Style style){ + style.addSource(this.source); + style.addLayer(this.layer); + } + + static Polygon getTurfPolygonCircle(@NonNull double radius, @NonNull Point centerPoint) { + return TurfTransformation.circle(centerPoint, radius, 360, UNIT_METERS); + } + + +} diff --git a/android/app/src/main/java/com/smtp/smtp/NavigationLauncherActivity.java b/android/app/src/main/java/com/smtp/smtp/road/RouteEditor.java similarity index 65% rename from android/app/src/main/java/com/smtp/smtp/NavigationLauncherActivity.java rename to android/app/src/main/java/com/smtp/smtp/road/RouteEditor.java index 205a487..d1b8be9 100644 --- a/android/app/src/main/java/com/smtp/smtp/NavigationLauncherActivity.java +++ b/android/app/src/main/java/com/smtp/smtp/road/RouteEditor.java @@ -1,4 +1,4 @@ -package com.smtp.smtp; +package com.smtp.smtp.road; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; @@ -6,6 +6,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; +import android.graphics.Color; import android.location.Location; import android.os.Build; import android.os.Bundle; @@ -30,11 +31,16 @@ import com.android.volley.toolbox.StringRequest; import com.android.volley.toolbox.Volley; import com.google.android.material.snackbar.Snackbar; +import com.mapbox.api.directions.v5.DirectionsCriteria; +import com.mapbox.api.directions.v5.DirectionsService; import com.mapbox.api.directions.v5.models.DirectionsResponse; import com.mapbox.api.directions.v5.models.DirectionsRoute; +import com.mapbox.api.matching.v5.MapboxMapMatching; +import com.mapbox.api.matching.v5.models.MapMatchingResponse; import com.mapbox.core.constants.Constants; import com.mapbox.geojson.LineString; import com.mapbox.geojson.Point; +import com.mapbox.geojson.utils.PolylineUtils; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.IconFactory; @@ -48,65 +54,67 @@ import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; - import com.mapbox.mapboxsdk.maps.Style; -import com.mapbox.mapboxsdk.style.light.Position; import com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute; import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute; - - - +import com.smtp.smtp.BuildConfig; +import com.smtp.smtp.R; +import com.smtp.smtp.http.RequestManager; +import com.smtp.smtp.navigation.MapMatcher; +import com.smtp.smtp.navigation.RouteGetter; +import com.smtp.smtp.navigation.Waypoint; + +import org.jetbrains.annotations.NotNull; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.unimodules.core.interfaces.Consumer; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import retrofit2.Call; import retrofit2.Callback; -public class NavigationLauncherActivity extends AppCompatActivity implements OnMapReadyCallback, +public class RouteEditor extends AppCompatActivity implements OnMapReadyCallback, MapboxMap.OnMapLongClickListener, MapboxMap.OnMarkerClickListener { - private static final int ONE_HUNDRED_MILLISECONDS = 100; - - private Point ORIGIN; - private Point DESTINATION; - private static final String TAG = "Navigation"; + private Point CHARGEMENT; + private Point DECHARGEMENT; + private static final String TAG = "EditRoad"; private String nameChantier; private String typeRoute; private String chantierId; private int rayonChargement; + private int distanceSecuriteMarkerRayon = 25; private int rayonDéchargement; - private String token; + private String token; private static final int CAMERA_ANIMATION_DURATION = 1000; - private static final int DEFAULT_CAMERA_ZOOM = 16; - private static final String BASE_URL = "http://smtp-dev-env.eba-5jqrxjhz.eu-west-3.elasticbeanstalk.com/"; + private static final String BASE_URL = BuildConfig.API_URL; private NavigationMapRoute mapRoute; private MapboxMap mapboxMap; - private final int[] padding = new int[]{50, 50, 50, 50}; - MapView mapView; ProgressBar loading; private TextView routeInfo; private DirectionsRoute route; + private boolean firstFetch; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Mapbox.getInstance(this.getApplicationContext(), getString(R.string.mapbox_access_token)); setContentView(R.layout.activity_navigation_launcher); - + Intent i = getIntent(); chantierId = i.getStringExtra("chantierId"); typeRoute = i.getStringExtra("typeRoute"); @@ -116,29 +124,102 @@ protected void onCreate(Bundle savedInstanceState) { double[] origin = i.getDoubleArrayExtra("origin"); double[] destination = i.getDoubleArrayExtra("destination"); - ORIGIN = Point.fromLngLat(origin[0], origin[1]); - DESTINATION = Point.fromLngLat(destination[0], destination[1]); + CHARGEMENT = Point.fromLngLat(origin[0], origin[1]); + DECHARGEMENT = Point.fromLngLat(destination[0], destination[1]); routeInfo = findViewById(R.id.route_info); - routeInfo.setText(nameChantier+ "\n" + "Route : " + typeRoute.substring(0, 1).toUpperCase() + typeRoute.substring(1)); + routeInfo.setText(nameChantier + "\n" + "Route : " + typeRoute.substring(0, 1).toUpperCase() + typeRoute.substring(1)); mapView = findViewById(R.id.mapView); mapView.onCreate(savedInstanceState); mapView.getMapAsync(this); + firstFetch = true; loading = findViewById(R.id.loading); } - public JSONObject prepareRouteForSending(){ + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + public void initWaypoints() { + final String URL = BASE_URL + "chantiers/" + chantierId + "/route/" + typeRoute; + RequestQueue requestQueue = Volley.newRequestQueue(this); + JsonArrayRequest getRequest = new JsonArrayRequest(Request.Method.GET, URL, null, + response -> { + try { + initRoute(response); + } catch (JSONException e) { + e.printStackTrace(); + } + }, + new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Log.d(TAG, error.toString()); + } + } + ) { + @Override + public Map getHeaders() throws AuthFailureError { + Map params = new HashMap(); + params.put("Content-Type", "application/json; charset=UTF-8"); + params.put("Authorization", "Bearer " + token); + return params; + } + }; + + requestQueue.add(getRequest); + } + + public JSONObject prepareRouteForSending() { JSONArray res = new JSONArray(); int order = 0; for (Marker marker : mapboxMap.getMarkers()) { JSONObject json = new JSONObject(); try { - if(!isOriginOrDestination(marker)){ - json.put("latitude",marker.getPosition().getLatitude()); - json.put("longitude",marker.getPosition().getLongitude()); + if (!isOriginOrDestination(marker)) { + json.put("latitude", marker.getPosition().getLatitude()); + json.put("longitude", marker.getPosition().getLongitude()); json.put("ordre", order); res.put(json); } @@ -148,7 +229,7 @@ public JSONObject prepareRouteForSending(){ order++; } try { - return new JSONObject().put("waypoints",res); + return new JSONObject().put("waypoints", res); } catch (JSONException e) { e.printStackTrace(); } @@ -158,7 +239,7 @@ public JSONObject prepareRouteForSending(){ public void initRoute(JSONArray array) throws JSONException { // Ajoute chaque donnée à la liste de waypoint triable List waypoints = new ArrayList<>(); - for (int i=0; i< array.length(); i++){ + for (int i = 0; i < array.length(); i++) { JSONObject waypoint = array.getJSONObject(i); waypoints.add( new Waypoint( @@ -170,103 +251,66 @@ public void initRoute(JSONArray array) throws JSONException { } Collections.sort(waypoints); - // Ajoute les marker à la map int i = 0; - for (Waypoint w: waypoints ) { - i+=1; + for (Waypoint w : waypoints) { + i += 1; LatLng point = new LatLng(w.latitude, w.longitude); MarkerOptions markerOptions = new MarkerOptions() .position(point) .icon(getMyIcon(i)); mapboxMap.addMarker(markerOptions); - Snackbar.make(mapView, "Marqueur : "+mapboxMap.getMarkers().size()+"/"+25, Snackbar.LENGTH_LONG).show(); + showMessage(mapView, "Marqueur : " + mapboxMap.getMarkers().size() + "/" + 25); } fetchRoute(); } - public Icon getMyIcon(int i){ + public Icon getMyIcon(int i) { IconFactory iconFactory = IconFactory.getInstance(getApplicationContext()); - switch (i){ - case 1 : + switch (i) { + case 1: return iconFactory.fromResource(R.drawable.marker1); - case 2 : + case 2: return iconFactory.fromResource(R.drawable.marker2); - case 3 : + case 3: return iconFactory.fromResource(R.drawable.marker3); - case 4 : + case 4: return iconFactory.fromResource(R.drawable.marker4); - case 5 : + case 5: return iconFactory.fromResource(R.drawable.marker5); - case 6 : + case 6: return iconFactory.fromResource(R.drawable.marker6); - case 7 : + case 7: return iconFactory.fromResource(R.drawable.marker7); - case 8 : + case 8: return iconFactory.fromResource(R.drawable.marker8); - case 9 : + case 9: return iconFactory.fromResource(R.drawable.marker9); } return iconFactory.fromResource(R.drawable.marker9); } - public void initWaypoints(){ - final String URL = BASE_URL + "chantiers/"+chantierId+"/route/"+typeRoute; - RequestQueue requestQueue = Volley.newRequestQueue(this); - // prepare the Request - JsonArrayRequest getRequest = new JsonArrayRequest(Request.Method.GET, URL, null, - response -> { - // display response - try { - initRoute(response); - } catch (JSONException e) { - e.printStackTrace(); - } - Log.d("Response", response.toString()); - }, - new Response.ErrorListener() - { - @Override - public void onErrorResponse(VolleyError error) { - Log.d("Error.Response", error.toString()); - } - } - ) { - @Override - public Map getHeaders() throws AuthFailureError { - Map params = new HashMap(); - params.put("Content-Type", "application/json; charset=UTF-8"); - params.put("Authorization", "Bearer " + token); - return params; - } - } ; - - // add it to the RequestQueue - requestQueue.add(getRequest); - } - public void sendRouteToServer(View view) { - if(mapboxMap.getMarkers().size() < 4){ - Snackbar.make(mapView, R.string.error_not_enough_waypoints, Snackbar.LENGTH_LONG ).show(); + if (mapboxMap.getMarkers().size() < 4) { + showMessage(view, "Il faut au moins deux points pour faire une route"); return; } JSONObject route = prepareRouteForSending(); RequestQueue requestQueue = Volley.newRequestQueue(this); final String requestBody = route.toString(); - String URL = BASE_URL + "chantiers/"+chantierId+"/route/"+typeRoute; + String URL = BASE_URL + "chantiers/" + chantierId + "/route/" + typeRoute; StringRequest stringRequest = new StringRequest(Request.Method.PUT, URL, new Response.Listener() { @Override public void onResponse(String response) { - Snackbar.make(mapView, "La route a été enregistrée avec succès", Snackbar.LENGTH_LONG ).show(); + showMessage(mapView, "La route a été enregistrée avec succès"); Log.i("VOLLEY", response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { - Snackbar.make(mapView, "Erreur de l'enregistrement de route, veuillez contacter un administrateur", Snackbar.LENGTH_LONG ).show(); + showMessage(mapView, "Erreur de l'enregistrement de route, veuillez contacter un administrateur"); Log.e("VOLLEY", error.toString()); } }) { - //This is for Headers If You Needed @Override public Map getHeaders() throws AuthFailureError { Map params = new HashMap(); @@ -274,10 +318,6 @@ public Map getHeaders() throws AuthFailureError { params.put("Authorization", "Bearer " + token); return params; } - //@Override - //public String getBodyContentType() { - // return "application/json; charset=utf-8"; - //} @Override public byte[] getBody() throws AuthFailureError { @@ -303,71 +343,60 @@ protected Response parseNetworkResponse(NetworkResponse response) { requestQueue.add(stringRequest); } - @Override - protected void onStart() { - super.onStart(); - mapView.onStart(); - } - - @Override - public void onResume() { - super.onResume(); - mapView.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - mapView.onPause(); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); - } - - @Override - protected void onStop() { - super.onStop(); - mapView.onStop(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); - } public void clearRoute(View view) { - if(mapboxMap.getMarkers().size() == 0){ - mapboxMap.clear(); + for (Marker m : mapboxMap.getMarkers()) { + if (!isOriginOrDestination(m)) { + mapboxMap.removeMarker(m); + } } mapRoute.removeRoute(); route = null; } + @Override public void onMapReady(MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; - this.mapboxMap.setStyle(Style.SATELLITE_STREETS, style -> { - this.mapboxMap.setOnMarkerClickListener(this); - this.mapboxMap.addOnMapLongClickListener(this); - initMapRoute(); - initWaypoints(); - initLieux(); - fetchRayon(); - boundCameraToRoute(); - }); + this.mapboxMap.setStyle(new Style.Builder() + .fromUri(Style.SATELLITE_STREETS), new Style.OnStyleLoaded() { + @Override + public void onStyleLoaded(@NonNull Style style) { + RouteEditor.this.mapboxMap.setOnMarkerClickListener(RouteEditor.this); + RouteEditor.this.mapboxMap.addOnMapLongClickListener(RouteEditor.this); + initMapRoute(); + initWaypoints(); + initLieux(); + fetchRayon(() -> { + drawRings(style); + }); + } + } + ); } - private float getDistance(MarkerOptions marker, Point lieu){ + private void drawRings(Style style) { + MapboxRing ringChargement = new MapboxRing( + CHARGEMENT, + rayonChargement, + "SOURCE_RING_CHARGEMENT", + "LAYER_RING_CHARGEMENT", + distanceSecuriteMarkerRayon + ); + ringChargement.addToStyle(style); + + MapboxRing ringDechargement = new MapboxRing( + DECHARGEMENT, + rayonDéchargement, + "SOURCE_RING_DECHARGEMENT", + "LAYER_RING_DECHARGEMENT", + distanceSecuriteMarkerRayon + ); + ringDechargement.addToStyle(style); + + } + + private float getDistance(MarkerOptions marker, Point lieu) { float[] distanceFromDestination = new float[3]; Location.distanceBetween( marker.getPosition().getLatitude(), @@ -379,17 +408,13 @@ private float getDistance(MarkerOptions marker, Point lieu){ return distanceFromDestination[0]; } - private void initLieux(){ - //IconFactory iconFactory = IconFactory.getInstance(getApplicationContext()); - //Icon icon = iconFactory.fromResource(R.drawable.icon_chargement); - //Icon icon2 = iconFactory.fromResource(R.drawable.icon_dechargement); - + private void initLieux() { this.mapboxMap.addMarker(new MarkerOptions().title("Chargement") - .position(new LatLng(ORIGIN.latitude(), ORIGIN.longitude())) + .position(new LatLng(CHARGEMENT.latitude(), CHARGEMENT.longitude())) ); this.mapboxMap.addMarker(new MarkerOptions().title("Déchargement") - .position(new LatLng(DESTINATION.latitude(), DESTINATION.longitude())) + .position(new LatLng(DECHARGEMENT.latitude(), DECHARGEMENT.longitude())) ); } @@ -397,67 +422,50 @@ private void initMapRoute() { mapRoute = new NavigationMapRoute(null, mapView, mapboxMap); } - private boolean isOriginOrDestination(Marker m){ - boolean isOrigin = m.getPosition().getLongitude() == ORIGIN.longitude() && m.getPosition().getLatitude() == ORIGIN.latitude(); - boolean isDestination = m.getPosition().getLongitude() == DESTINATION.longitude() && m.getPosition().getLatitude() == DESTINATION.latitude(); + private boolean isOriginOrDestination(Marker m) { + boolean isOrigin = m.getPosition().getLongitude() == CHARGEMENT.longitude() && m.getPosition().getLatitude() == CHARGEMENT.latitude(); + boolean isDestination = m.getPosition().getLongitude() == DECHARGEMENT.longitude() && m.getPosition().getLatitude() == DECHARGEMENT.latitude(); return (isOrigin || isDestination); } private void fetchRoute() { - NavigationRoute.Builder builder = NavigationRoute.builder(this) - .accessToken("pk." + getString(R.string.gh_key)) - .baseUrl(getString(R.string.base_url)) - .user("gh") - .profile("car"); - if (mapboxMap.getMarkers().size() < 4) { - Snackbar.make(mapView, R.string.error_not_enough_waypoints, Snackbar.LENGTH_LONG).show(); + showMessage(mapView, "Il faut au moins deux points pour faire une route"); return; } - List wp = new ArrayList<>(); - for(Marker m : mapboxMap.getMarkers()) { - if(!isOriginOrDestination(m)){ - Point p = Point.fromLngLat(m.getPosition().getLongitude(), m.getPosition().getLatitude()); - wp.add(p); - } - } + List waypointsWithoutOriginAndDestination = getWaypointsWithoutOriginAndDestination(); - for (int i = 0; i < wp.size(); i++) { - Point p = wp.get(i); - if (i == 0) { - builder.origin(p); - } else if (i < wp.size() - 1) { - builder.addWaypoint(p); - } else { - builder.destination(p); - } - } showLoading(); - builder.build().getRoute(new Callback() { - @Override - public void onResponse(Call call, retrofit2.Response response) { - if (validRouteResponse(response)) { - route = response.body().routes().get(0); - mapRoute.addRoutes(response.body().routes()); - //boundCameraToRoute(); - } else { - Snackbar.make(mapView, R.string.error_calculating_route, Snackbar.LENGTH_LONG).show(); - } + RouteGetter routeGetter = new RouteGetter(getApplicationContext(), waypointsWithoutOriginAndDestination); + routeGetter.getRoute((route) -> { + MapMatcher mapMatcher = new MapMatcher(getApplicationContext(), route); + mapMatcher.getMatch((trafficRoute) -> { + this.route = trafficRoute; + this.mapRoute.addRoute(this.route); hideLoading(); - } - - @Override - public void onFailure(Call call, Throwable t) { - Snackbar.make(mapView, R.string.error_calculating_route, Snackbar.LENGTH_LONG).show(); - hideLoading(); - } + boundCameraToRoute(); + }, (t) -> { + Log.e(TAG, t.getLocalizedMessage()); + showMessage(mapView, "Erreur au calcul de la route, veuillez contacter un administrateur"); + }); + }, (t) -> { + Log.e(TAG, t.getLocalizedMessage()); + showMessage(mapView, "Erreur au calcul de la route, veuillez contacter un administrateur"); }); } - private boolean validRouteResponse(retrofit2.Response response) { - return response.body() != null && !response.body().routes().isEmpty(); + @NotNull + private List getWaypointsWithoutOriginAndDestination() { + List wp = new ArrayList<>(); + for (Marker m : mapboxMap.getMarkers()) { + if (!isOriginOrDestination(m)) { + Point p = Point.fromLngLat(m.getPosition().getLongitude(), m.getPosition().getLatitude()); + wp.add(p); + } + } + return wp; } private void hideLoading() { @@ -473,7 +481,9 @@ private void showLoading() { } private void boundCameraToRoute() { + if (!firstFetch) return; if (route != null) { + firstFetch = false; List routeCoords = LineString.fromPolyline(route.geometry(), Constants.PRECISION_6).coordinates(); List bboxPoints = new ArrayList<>(); @@ -483,19 +493,13 @@ private void boundCameraToRoute() { if (bboxPoints.size() > 1) { try { LatLngBounds bounds = new LatLngBounds.Builder().includes(bboxPoints).build(); - // left, top, right, bottom + int[] padding = new int[]{75, 75, 75, 75}; animateCameraBbox(bounds, CAMERA_ANIMATION_DURATION, padding); } catch (InvalidLatLngBoundsException exception) { Toast.makeText(this, R.string.error_valid_route_not_found, Toast.LENGTH_SHORT).show(); } } } - CameraPosition position = new CameraPosition.Builder() - .target(new LatLng((ORIGIN.latitude()+DESTINATION.latitude())/2, (ORIGIN.longitude()+DESTINATION.longitude())/2 )) - .zoom(12) - .tilt(20) - .build(); - mapboxMap.setCameraPosition(position); } private void animateCameraBbox(LatLngBounds bounds, int animationTime, int[] padding) { @@ -505,40 +509,48 @@ private void animateCameraBbox(LatLngBounds bounds, int animationTime, int[] pad @Override public boolean onMarkerClick(@NonNull Marker marker) { - for(Marker m : mapboxMap.getMarkers()) { - if(m.getId() == marker.getId()){ - if(!isOriginOrDestination(m)){ + for (Marker m : mapboxMap.getMarkers()) { + if (m.getId() == marker.getId()) { + if (!isOriginOrDestination(m)) { mapboxMap.removeMarker(m); } } } - if(mapboxMap.getMarkers().size() > 3) { + if (mapboxMap.getMarkers().size() > 3) { fetchRoute(); } else { clearRoute(null); } - Snackbar.make(mapView, "Marqueur : "+(mapboxMap.getMarkers().size()-2)+"/"+25, Snackbar.LENGTH_LONG).show(); - Log.d("MARKER", Long.toString(marker.getId())); + showMessage(mapView, "Marqueur : " + (mapboxMap.getMarkers().size() - 2) + "/" + 25); + Log.d(TAG,"Clicked marker id: " + marker.getId()); return false; } + public void showMessage(View mapView, String message) { + Snackbar snackbar; + snackbar = Snackbar.make(mapView, message, Snackbar.LENGTH_LONG); + View snackBarView = snackbar.getView(); + snackBarView.setBackgroundColor(Color.WHITE); + snackbar.show(); + } + @Override public boolean onMapLongClick(@NonNull LatLng point) { vibrate(); MarkerOptions markerOptions = new MarkerOptions() .position(point) - .icon(getMyIcon(mapboxMap.getMarkers().size()-1)); - if(getDistance(markerOptions,ORIGIN)< rayonChargement ){ - Snackbar.make(mapView, "Le marqueur ne peut pas être dans le rayon de chargement"+getDistance(markerOptions,ORIGIN)+"<"+rayonChargement, Snackbar.LENGTH_LONG).show(); - }else if (getDistance(markerOptions,DESTINATION)< rayonDéchargement) { - Snackbar.make(mapView, "Le marqueur ne peut pas être dans le rayon de déchargement"+getDistance(markerOptions,DESTINATION)+"<"+rayonDéchargement, Snackbar.LENGTH_LONG).show(); - }else{ + .icon(getMyIcon(mapboxMap.getMarkers().size() - 1)); + if (getDistance(markerOptions, CHARGEMENT) < rayonChargement + distanceSecuriteMarkerRayon) { + showMessage(mapView, "Le marqueur ne peut pas être dans le rayon de chargement " + (getDistance(markerOptions, CHARGEMENT)) + " < " + (rayonChargement + distanceSecuriteMarkerRayon)); + } else if (getDistance(markerOptions, DECHARGEMENT) < rayonDéchargement + distanceSecuriteMarkerRayon) { + showMessage(mapView, "Le marqueur ne peut pas être dans le rayon de déchargement " + (getDistance(markerOptions, DECHARGEMENT)) + " < " + (rayonDéchargement + distanceSecuriteMarkerRayon)); + } else { mapboxMap.addMarker(markerOptions); - Snackbar.make(mapView, "Marqueur : "+(mapboxMap.getMarkers().size()-2)+"/"+23, Snackbar.LENGTH_LONG).show(); + showMessage(mapView, "Marqueur : " + (mapboxMap.getMarkers().size() - 2) + "/" + 23); + } + if (mapboxMap.getMarkers().size() > 3) { + fetchRoute(); } - //addPointToRoute(point.getLatitude(), point.getLongitude()); - //updateRouteAfterWaypointChange(); - fetchRoute(); return true; } @@ -555,17 +567,19 @@ private void vibrate() { } } - private void fetchRayon() { + private void fetchRayon(Runnable lambda) { final String URL = BASE_URL + "chantiers/" + chantierId; JsonObjectRequest getRequest = new JsonObjectRequest(Request.Method.GET, URL, null, response -> { try { rayonDéchargement = response.getJSONObject("lieuDéchargement").getInt("rayon"); rayonChargement = response.getJSONObject("lieuChargement").getInt("rayon"); + if (lambda != null) { + lambda.run(); + } } catch (JSONException e) { - e.printStackTrace(); + Log.e(TAG, e.getLocalizedMessage()); } - Log.d(TAG, response.toString()); }, new com.android.volley.Response.ErrorListener() { @Override diff --git a/android/app/src/main/res/layout/activity_navigation_launcher.xml b/android/app/src/main/res/layout/activity_navigation_launcher.xml index 7898ba1..3078d08 100644 --- a/android/app/src/main/res/layout/activity_navigation_launcher.xml +++ b/android/app/src/main/res/layout/activity_navigation_launcher.xml @@ -20,7 +20,13 @@ android:layout_gravity="top" android:layout_marginLeft="20dp" android:layout_marginTop="40dp" + android:paddingTop="7dp" + android:paddingBottom="7dp" + android:paddingRight="10dp" + android:paddingLeft="10dp" android:textStyle="bold" + android:background="#B3ffffff" + android:textColor="#1f2212" /> - {item.prenom +" "+ item.nom} + {item.prenom +" "+ item.nom} )} /> diff --git a/components/ButtonAdminSelected.js b/components/ButtonAdminSelected.js index a725191..eaeca5c 100644 --- a/components/ButtonAdminSelected.js +++ b/components/ButtonAdminSelected.js @@ -1,6 +1,7 @@ import React from 'react' import AddWorkSiteForm from "./Worksite/AddWorkSiteForm"; import ListPlace from "./Place/ListPlace"; +import ListMateriaux from "./Materiau/ListMateriaux"; export default class ButtonAdminSelected extends React.Component { @@ -14,7 +15,7 @@ export default class ButtonAdminSelected extends React.Component { }else if(this.props.index == 1){ return }else if(this.props.index == 2){ - return null; + return ; }else return null; } diff --git a/components/ButtonGroupAdmin.js b/components/ButtonGroupAdmin.js index 83ad764..48fd439 100644 --- a/components/ButtonGroupAdmin.js +++ b/components/ButtonGroupAdmin.js @@ -35,7 +35,7 @@ export default class ButtonGroupAdmin extends React.Component { } render () { - const buttons = ['Ajouter Chantier', 'Afficher Lieux', 'Afficher Entreprises']; + const buttons = ['Ajouter Chantier', 'Afficher Lieux', 'Afficher Matériaux']; const { selectedIndex } = this.state; if(this.state.typeUser !== "admin"){ diff --git a/components/Carriere/CarriereTruck.js b/components/Carriere/CarriereTruck.js new file mode 100644 index 0000000..3159e5e --- /dev/null +++ b/components/Carriere/CarriereTruck.js @@ -0,0 +1,149 @@ +import React from "react"; +import style from "../../Style"; +import axios from 'axios' +import { + ActivityIndicator, + View, + ScrollView, + AsyncStorage, + TextInput +} from "react-native"; + +import Config from "react-native-config"; +import AutoCompletePlaces from "../Place/AutoCompletePlaces"; +import ValidateButton from "../ValidateButton"; +import AutoCompleteMateriaux from "../Materiau/AutoCompleteMateriaux"; + + +export default class CarriereTruck extends React.Component { + constructor(props) { + super(props); + this.getLieux = this.getLieux.bind(this); + this.getMateriaux = this.getMateriaux.bind(this); + this.state = { + report: null, + idPlace1 : null, + idPlace2 : null, + qte : null, + materiaux : [], + }; + } + + async componentDidMount() { + await this.getLieux(); + await this.getMateriaux(); + } + + async getLieux() { + const token = await AsyncStorage.getItem('token'); + await axios({ + method : 'get', + url : Config.API_URL + 'lieux', + headers: {'Authorization': 'Bearer ' + token}, + }) + .then( response => { + if(response.status !== 200){ + console.log(response.status); + alert(response.status); + return response.status; + } + console.log(response.status); + this.setState({report : response.data}); + return response.status; + }) + .catch((error) => { + console.log(error); + }) + } + + async getMateriaux() { + const token = await AsyncStorage.getItem('token'); + await axios({ + method : 'get', + url : Config.API_URL + 'materiaux', + headers: {'Authorization': 'Bearer ' + token}, + }) + .then( response => { + if(response.status !== 200){ + console.log(response.status); + alert(response.status); + return response.status; + } + console.log(response.status); + this.setState({materiaux : response.data}); + return response.status; + }) + .catch((error) => { + console.log(error); + }) + } + + + async postPrelevement(){ + const token = await AsyncStorage.getItem('token'); + const userId = await AsyncStorage.getItem('userId'); + const data = { + "lieuChargementId": this.state.idPlace1, + "lieuDechargementId": this.state.idPlace2, + "quantite": parseFloat(this.state.qte), + "camionneur": userId, + "materiau" : this.state.materiau, + }; + axios({ + method: 'post', + url: Config.API_URL + 'prelevements', + data : data, + headers: {'Authorization': 'Bearer ' + token}, + }) + .then( response => { + if(response.status !== 201){ + console.log(response.status); + alert(response.status); + return response.status; + } + alert("le prélevement a bien été enregistré"); + + console.log(response.status); + return response.status; + }) + .catch((error) => { + console.log(error); + }) + } + + render() { + if (this.state.report === null) { + return ( + + + + ) + } else { + return ( + + + + + this.setState({idPlace1})} name={"chargement"} places={this.state.report}/> + + + this.setState({idPlace2})} name={"déchargement"} places={this.state.report}/> + + + + this.setState({materiau})} materiaux ={this.state.materiaux}/> + + + + this.setState({qte})} + value={this.state.qte} placeholder={"Quantité en tonne"}/> + + + this.postPrelevement()}/> + + + + ) + } + } +} \ No newline at end of file diff --git a/components/Crane/CraneView.js b/components/Crane/CraneView.js index c1ee993..1406960 100644 --- a/components/Crane/CraneView.js +++ b/components/Crane/CraneView.js @@ -1,10 +1,12 @@ import * as React from 'react'; -import {Text, View, ScrollView, Dimensions, StyleSheet } from 'react-native'; +import {Text, View, ScrollView, Dimensions, StyleSheet, TouchableOpacity } from 'react-native'; import ValidateButton from "../ValidateButton"; import { Ionicons } from '@expo/vector-icons'; import Icon from 'react-native-vector-icons/FontAwesome'; import StopButtons from "../StopButtons"; import TruckArrivalTime from "./TruckArrivalTime"; +import {heightPercentageToDP as hp, widthPercentageToDP as wp} from "react-native-responsive-screen"; + export default class CraneView extends React.Component { constructor(props) { @@ -100,9 +102,13 @@ export default class CraneView extends React.Component { const { width, height } = Dimensions.get('window'); const styles = StyleSheet.create({ progressBar: { - flexDirection:'row', - justifyContent:'center', - alignItems: 'center' , - backgroundColor : '#FFF', - }, + flexDirection : "row", + position: "absolute", + bottom: 80, + alignItems:'center', + justifyContent:'center', + width: wp('100%'), + backgroundColor : '#FFF', + height: hp('10%') + } }); diff --git a/components/Crane/TruckArrivalTime.js b/components/Crane/TruckArrivalTime.js index 3c51956..9c8b8b1 100644 --- a/components/Crane/TruckArrivalTime.js +++ b/components/Crane/TruckArrivalTime.js @@ -54,11 +54,13 @@ export default class TruckArrivalTime extends React.Component{ let seconds = time - minutes * 60; return( - - - {minutes}mn{seconds} + + + + {minutes}mn{seconds} + {this.state.prenom} + - {this.state.prenom} ); } diff --git a/components/FuelForm.js b/components/FuelForm.js new file mode 100644 index 0000000..2e58df3 --- /dev/null +++ b/components/FuelForm.js @@ -0,0 +1,268 @@ +import Icon from "react-native-vector-icons/FontAwesome5"; +import {Text, View, AsyncStorage, ActivityIndicator, TextInput, Button, TouchableOpacity} from "react-native"; +import * as React from "react"; +import axios from 'axios'; +import Config from "react-native-config"; +import Style from "../Style"; +import TimePicker from 'react-native-simple-time-picker'; + + +export default class FuelForm extends React.Component{ + constructor(props){ + super(props); + this.changeVolumeDebutFuel = this.changeVolumeDebutFuel.bind(this) + this.changeVolumeFinFuel = this.changeVolumeFinFuel.bind(this) + this.changeVolumeAjoutFuel = this.changeVolumeAjoutFuel.bind(this) + this.getVolumes = this.getVolumes.bind(this) + this.getHoraires =this.getHoraires.bind(this) + this.addHoraire = this.addHoraire.bind(this) + this.rearangeVolumes = this.rearangeVolumes.bind(this) + this.state = { + volumeDebutFuel: "", + volumeDebutStored : false, + volumeFinFuel : "", + volumeFinStored : false, + volumeAjoutFuel : "", + volumeAjoutStored : false, + horaireStored : false, + volumes : [], + ready : false, + horaires : undefined, + } + } + + async componentDidMount() { + await this.getVolumes(); + await this.rearangeVolumes(); + await this.getHoraires(); + } + + changeVolumeDebutFuel(e){ + e = parseInt(e) + this.setState({volumeDebutFuel : e}) + this.setState({volumeDebutStored : false}) + } + + changeVolumeFinFuel(e){ + this.setState({volumeFinFuel : e }) + this.setState({volumeFinStored : false}) + } + + changeVolumeAjoutFuel(e){ + this.setState({volumeAjoutFuel : e}) + this.setState({volumeAjoutStored : false}) + } + + async rearangeVolumes(){ + console.log(this.state.volumes.length) + for (let i = 0; i < this.state.volumes.length; i++){ + console.log("this.state.volumes[i].volume = " + this.state.volumes[i].volume) + if (this.state.volumes[i].type === "début"){ + this.changeVolumeDebutFuel(this.state.volumes[i].volume) + this.setState({volumeDebutStored : true}) + } + if(this.state.volumes[i].type === "fin") { + this.changeVolumeFinFuel(this.state.volumes[i].volume) + this.setState({volumeFinStored : true}) + } + if(this.state.volumes[i].type === "ajout"){ + this.changeVolumeAjoutFuel(this.state.volumes[i].volume) + this.setState({volumeAjoutStored : true}) + } + } + this.setState({ready : true}) + } + + async getVolumes(){ + const token = await AsyncStorage.getItem('token'); + const userId = await AsyncStorage.getItem('userId'); + await axios({ + method: 'get', + url: Config.API_URL + 'grutiers/'+ userId +'/carburant/'+Date.now(), + headers: {'Authorization': 'Bearer ' + token}, + }) + .then( response => { + if(response.status !== 200){ + console.log(response.status); + alert(response.status); + return response.status; + } + this.setState({volumes : response.data}) + console.log("axios get volumes "+ JSON.stringify(response.data)); + return response.status; + }) + .catch((error) => { + console.log(error + " getVolume"); + }) + } + + async getHoraires(){ + const token = await AsyncStorage.getItem('token'); + const userId = await AsyncStorage.getItem('userId'); + await axios({ + method: 'get', + url: Config.API_URL + 'grutiers/'+ userId +'/work-time', + headers: {'Authorization': 'Bearer ' + token}, + }) + .then( response => { + if(response.status !== 200){ + console.log(response.status); + alert(response.status); + return response.status; + } + this.setState({horaires : response.data[new Date().toISOString().split("T")[0]]}) + console.log("axios get horaires "+ JSON.stringify(response.data[new Date().toISOString().split("T")[0]])); + this.state.horaires ? ( console.log("success get") , this.setState({ horaireStored : true }) ): this.setState({horaires : {"hour":0,"minute":0}}), console.log("init horaires {0,0}") + return response.status; + }) + .catch((error) => { + console.log(error + "getHoraires"); + }) + } + + async addHoraire(){ + const token = await AsyncStorage.getItem('token'); + const userId = await AsyncStorage.getItem('userId'); + let data; + if(this.state.horaires.idWorkTime !== undefined){ + data = { + "idWorkTime" : this.state.horaires.idWorkTime, + "hour": this.state.horaires.hour, + "minute" : this.state.horaires.minute, + }; + }else{ + data = { + "hour": this.state.horaires.hour, + "minute" : this.state.horaires.minute, + }; + } + axios({ + method: 'put', + url: Config.API_URL + 'grutiers/'+userId+'/work-time', + data : data, + headers: {'Authorization': 'Bearer ' + token}, + }) + .then( response => { + if(response.status !== 201){ + console.log(response.status); + alert(response.status); + return response.status; + } + this.setState({ horaireStored : true }) + let item = this.state.horaires + item.idWorkTime = response.data[0].id + this.setState({ horaires : item }) + console.log(response.status); + return response.status; + }) + .catch((error) => { + console.log(error + " addHoraire"); + }) + } + + async addVolume(type,volume){ + const token = await AsyncStorage.getItem('token'); + const userId = await AsyncStorage.getItem('userId'); + const data = { + "type": type, + "volume" : volume, + }; + axios({ + method: 'put', + url: Config.API_URL + 'grutiers/'+userId+'/carburant', + data : data, + headers: {'Authorization': 'Bearer ' + token}, + }) + .then( response => { + if(response.status !== 200){ + console.log(response.status); + alert(response.status); + return response.status; + } + if(type === "début"){ + this.setState({volumeDebutStored : true}) + } + if(type === "fin"){ + this.setState({volumeFinStored : true}) + } + if(type === "ajout"){ + this.setState({volumeAjoutStored : true}) + } + console.log(response.status); + return response.status; + }) + .catch((error) => { + console.log(error + "addVolume"); + }) + } + + render() { + console.log(Date.now()) + console.log(this.state.volumes) + let selectedHours = this.state.horaires ? this.state.horaires.hour : 0 + let selectedMinutes = this.state.horaires ? this.state.horaires.minute : 0 + if(this.state.ready && this.state.horaires){ + console.log("horaires : " + JSON.stringify(this.state.horaires)) + return( + + + { "Formulaire pour comptabiliser l'essence :" } + { "Volume d'essence au début de la journée" } + + this.changeVolumeDebutFuel(volumeDebutFuel)} + value={this.state.volumeDebutFuel.toString()} placeholder={"Volume début de journée (L)"}/> + this.addVolume("début",this.state.volumeDebutFuel)}> + { this.state.volumeDebutStored ? : } + + + { "Volume d'essence à la fin de la journée" } + + this.changeVolumeFinFuel(volumeFinFuel)} + value={this.state.volumeFinFuel.toString()} placeholder={"Volume en fin de journée (L)"}/> + this.addVolume("fin",this.state.volumeFinFuel)}> + { this.state.volumeFinStored ? : } + + + { "Volume d'essence ajouté cette journée" } + + this.changeVolumeAjoutFuel(volumeAjoutFuel)} + value={this.state.volumeAjoutFuel.toString()} placeholder={"Volume d'essence ajouté (L)"}/> + this.addVolume("ajout",this.state.volumeAjoutFuel)}> + { this.state.volumeAjoutStored ? : } + + + + Nombre d'heure de travail effectuée + + + + { + let time = this.state.horaires ? this.state.horaires : {}; + time.hour = hours; + time.minute = minutes; + this.setState({ horaires : time, horaireStored : false })} + } + /> + + + this.addHoraire()}> + { this.state.horaireStored ? : } + + + + +