package com.safemobile.dispatch; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Typeface; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; import android.view.Window; import android.widget.AdapterView; import android.widget.Button; import android.widget.GridView; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MarkerOptions; import com.safemobile.activities.AbstractLiveActivity; import com.safemobile.activities.AbstractSDParentActivity; import com.safemobile.adapters.VehiclesGridViewAdapter; import com.safemobile.lib.AppParams; import com.safemobile.lib.OperationCodes; import com.safemobile.lib.SM; import com.safemobile.lib.SuperVehicle; import com.safemobile.lib.Vehicle; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Objects; public class LiveActivity extends AbstractLiveActivity implements OnMapReadyCallback { private GoogleMap googleMap; /* Context Menu */ private static final int MENU_ENABLE = 150; private static final int MENU_DISABLE = 151; private static final int MENU_REMOTE = 161; private static final int MENU_POLL = 154; private static final double LAT_OUTLIMIT = 91; private static final double LNG_OUTLIMIT = 181; private IconContextMenu iconContextMenu = null; /* Dialog */ private Dialog loadingDialog; // Need handler for callbacks to the UI thread private final Handler myHandler = new Handler(Looper.getMainLooper()); private Thread threadUI; /* Misc */ private Context context; private Resources res; private Activity activity; /* Visual Elements */ private ImageButton imageViewCheckAll; private Button displayButton; private boolean isFirstRun = true; private boolean isFirstMap = true; private boolean isAck = false; private boolean showVehicle = true; private int contextMenuPosition; private int vehStatus; private int position; // vehStatus = vehicle status received from apps /* Live Vehicle GridView */ private GridView gridVehicle; private ArrayList liveVehicle = new ArrayList<>(); private ArrayList displayedVehicles = new ArrayList<>(); private ArrayList disabledVehicles = new ArrayList<>(); private VehiclesGridViewAdapter adapter; private final HashMap> tableHashOverlay = new HashMap<>(); //value poll private double latPoll = 0; private double lngPoll = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setSavedInstanceState(savedInstanceState); // get parentTab setParentTab((AbstractSDParentActivity) getParent()); try { ((TabLayoutActivity) getParentTab()).liveActivity = this; } catch (Exception ignored) { // ignored } context = this; activity = this; res = getResources(); Locale locale = new Locale(AppParams.LANGUAGETMP); Locale.setDefault(locale); Configuration config = new Configuration(); config.locale = locale; getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); // get mapView only when creating first time if (isFirstMap) { setContentView(R.layout.tablive); isFirstMap = false; } // Obtain the SupportMapFragment and get notified when the map is ready to be used. SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); if (mapFragment != null) { mapFragment.getMapAsync(this); } // create on vehicle long click menu createIconContextMenu(); // image View for changing map type satellite or map ImageView changeMapTypeImageView = (ImageView) findViewById(R.id.changeMapType); changeMapTypeImageView.setOnClickListener(v -> { if (googleMap.getMapType() == GoogleMap.MAP_TYPE_SATELLITE) { changeMapTypeImageView.setImageResource(R.drawable.satellite); googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); } else { changeMapTypeImageView.setImageResource(R.drawable.map); googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); } }); ImageView changeTrafficImageView = (ImageView) findViewById(R.id.changeTraffic); changeTrafficImageView.setOnClickListener(v -> { if (googleMap.isTrafficEnabled()) { changeTrafficImageView.setImageResource(R.drawable.traffic_off); googleMap.setTrafficEnabled(false); } else { changeTrafficImageView.setImageResource(R.drawable.traffic); googleMap.setTrafficEnabled(true); } }); // change tab header font TextView textView1 = (TextView) findViewById(R.id.textView1); textView1.setTypeface(Typeface.createFromAsset(getAssets(), "Sketch_Block.ttf")); textView1.setTextSize(24); // get grid view and set empty data adapter gridVehicle = (GridView) findViewById(R.id.gridVehicle); if (AppParams.DEMO) { disabledVehicles = new ArrayList<>(); for (Vehicle veh : getParentTab().getAllVehicle()) disabledVehicles.add(!veh.status); } adapter = new VehiclesGridViewAdapter(activity, context, getParentTab().getAllVehicle(), disabledVehicles); adapter.notifyDataSetChanged(); gridVehicle.setAdapter(adapter); // get vehicle display click gridVehicle.setOnItemClickListener((parent, view, position, id) -> { threadUI = new Thread(() -> { myHandler.post(() -> itemClick(position, view)); threadUI.interrupt(); }); threadUI.start(); }); // get vehicle menu creation gridVehicle.setOnItemLongClickListener(itemLongClickHandler); LinearLayout slideLayout = (LinearLayout) findViewById(R.id.slidelayout); ImageView slideLayoutImageView = (ImageView) findViewById(R.id.slideLayoutImage); LinearLayout linearLayoutVehicles = (LinearLayout) findViewById(R.id.layoutBig); slideLayout.setOnClickListener(v -> { if (showVehicle) { linearLayoutVehicles.setVisibility(View.GONE); slideLayoutImageView.setImageResource(R.drawable.arrow_right); showVehicle = false; } else { linearLayoutVehicles.setVisibility(View.VISIBLE); slideLayoutImageView.setImageResource(R.drawable.arrow_left); showVehicle = true; } }); imageViewCheckAll = (ImageButton) findViewById(R.id.imageCheckAll); imageViewCheckAll.setSelected(false); imageViewCheckAll.setOnClickListener(arg0 -> { for (int i = 0; i < displayedVehicles.size(); i++) displayedVehicles.set(i, true); adapter.changeDisplayAll(!imageViewCheckAll.isSelected()); VehiclesGridViewAdapter.ViewHolder viewLive = (VehiclesGridViewAdapter.ViewHolder) gridVehicle.getChildAt(0).getTag(); if (!imageViewCheckAll.isSelected()) viewLive.imgViewChecked.setImageResource(R.drawable.checked); else viewLive.imgViewChecked.setImageResource(R.drawable.unchecked); Enumeration keyList = getParentTab().getSuperVehHash().keys(); while (keyList.hasMoreElements()) { (Objects.requireNonNull(getParentTab().getSuperVehHash().get((long) keyList.nextElement()))).needUpdate = true; } // change button title if (!imageViewCheckAll.isSelected()) { imageViewCheckAll.setSelected(true); imageViewCheckAll.setBackgroundResource(R.drawable.check_all); displayButton.setText(getString(R.string.hideAll)); // set all vehicles to be displayed for (int i = 0; i < displayedVehicles.size(); i++) displayedVehicles.set(i, true); } else { imageViewCheckAll.setSelected(false); imageViewCheckAll.setBackgroundResource(R.drawable.uncheck_all); displayButton.setText(getString(R.string.displayAll)); // set all vehicles to not be displayed for (int i = 0; i < displayedVehicles.size(); i++) displayedVehicles.set(i, false); } // refresh UI displayVehicle(true, LAT_OUTLIMIT, LNG_OUTLIMIT); }); displayButton = (Button) findViewById(R.id.buttonDisplay); displayButton.setText(getString(R.string.displayAll)); displayButton.setOnClickListener(v -> { // set all displayed vehicles to true for (int i = 0; i < displayedVehicles.size(); i++) displayedVehicles.set(i, true); Enumeration keyList = getParentTab().getSuperVehHash().keys(); while (keyList.hasMoreElements()) (Objects.requireNonNull(getParentTab().getSuperVehHash().get((long) keyList.nextElement()))).needUpdate = true; // change button title if (displayButton.getText().toString().equals(getString(R.string.displayAll))) { displayButton.setText(getString(R.string.hideAll)); // set all vehicles to be displayed for (int i = 0; i < displayedVehicles.size(); i++) displayedVehicles.set(i, true); } else { displayButton.setText(getString(R.string.displayAll)); // set all vehicles to not be displayed for (int i = 0; i < displayedVehicles.size(); i++) displayedVehicles.set(i, false); } // refresh UI displayVehicle(true, LAT_OUTLIMIT, LNG_OUTLIMIT); }); // display Vehicles displayVehicle(true, LAT_OUTLIMIT, LNG_OUTLIMIT); // register to receive broadcasts registerBroadcastIntents(); } /** * Manipulates the map once available. * This callback is triggered when the map is ready to be used. * This is where we can add markers or lines, add listeners or move the camera. In this case, * we just add a marker near Sydney, Australia. * If Google Play services is not installed on the device, the user will be prompted to install * it inside the SupportMapFragment. This method will only be triggered once the user has * installed Google Play services and returned to the app. */ @Override public void onMapReady(@NonNull GoogleMap googleMap) { this.googleMap = googleMap; // Add a marker in Sydney and move the camera LatLng sydney = new LatLng(-34, 151); this.googleMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney")); this.googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); } @Override public void onBackPressed() { // cancel loading dialog if showing if (loadingDialog.isShowing()) { cancelLoadingDialog(); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(getString(R.string.exit)) .setCancelable(false) .setNeutralButton(getString(R.string.logout), (dialog, id) -> getParentTab().whenBackPressed(AppParams.ActivityResult.logout)) .setPositiveButton(getString(R.string.ext), (dialog, id) -> getParentTab().whenBackPressed(AppParams.ActivityResult.exit)) .setNegativeButton(getString(R.string.cancel), (dialog, id) -> dialog.cancel()); AlertDialog alert = builder.create(); alert.show(); } } @Override public void onStart() { super.onStart(); if (isFirstRun) { // show loading dialog showLoadingDialog("Getting vehicles from database..."); // send liveActivity getParentTab().setLiveActivity((AbstractLiveActivity) activity); // get all vehicles isAck = false; // start a thread to wait 3 seconds for ack new Thread(() -> { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); SM.Exception(e.toString()); // Restore interrupted state Thread.currentThread().interrupt(); } // if not getVehicle not isAck if (!isAck) myHandler.post(cancelLoadingDialogRUN); }).start(); SM.Debug("GetVehs"); isFirstRun = false; } } @Override public void onPause() { super.onPause(); SM.Debug("onPause"); } @Override public void onResume() { super.onResume(); // clear previous vehicles SM.Debug("onResume"); displayVehicle(true, LAT_OUTLIMIT, LNG_OUTLIMIT); } final Runnable cancelLoadingDialogRUN = LiveActivity.this::cancelLoadingDialog; // cancel loading dialog and show sending error message private void cancelLoadingDialog() { // cancel loading dialog try { loadingDialog.cancel(); } catch (Exception ex) { SM.Exception(ex.toString()); } if (!isAck) { // show connection error Toast.makeText(context, "Could not get Vehicles... ", Toast.LENGTH_SHORT).show(); } } public void createIconContextMenu() { iconContextMenu = new IconContextMenu(this, 1); iconContextMenu.addItem(res, R.string.enable, R.drawable.enable, MENU_ENABLE); iconContextMenu.addItem(res, R.string.disable, R.drawable.disable, MENU_DISABLE); iconContextMenu.addItem(res, R.string.poll, R.drawable.poll, MENU_POLL); //set onclick listener for context menu iconContextMenu.setOnClickListener(menuId -> { int radioCode = 30; switch (menuId) { case MENU_ENABLE: { // send change to AppServer optionForUnit(radioCode, MENU_ENABLE, liveVehicle.get(contextMenuPosition).id + ""); if (AppParams.DEMO) vehicleStatusReceived(liveVehicle.get(contextMenuPosition).id, 101, 1); SM.Debug("MENU: Enable for " + liveVehicle.get(contextMenuPosition).id); break; } case MENU_DISABLE: { // send change to AppServer optionForUnit(radioCode, MENU_DISABLE, liveVehicle.get(contextMenuPosition).id + ""); if (AppParams.DEMO) vehicleStatusReceived(liveVehicle.get(contextMenuPosition).id, 101, 0); SM.Debug("MENU: Disable for " + liveVehicle.get(contextMenuPosition).id); break; } case MENU_REMOTE: { // send change to AppServer optionForUnit(radioCode, MENU_REMOTE, liveVehicle.get(contextMenuPosition).id + ""); SM.Debug("MENU: Remote for " + liveVehicle.get(contextMenuPosition).id); break; } case MENU_POLL: { // send change to AppServer optionForUnit(radioCode, MENU_POLL, liveVehicle.get(contextMenuPosition).id + ""); if (AppParams.DEMO) { getParentTab().setImei(liveVehicle.get(contextMenuPosition).sc_id + ""); getParentTab().updateDemoPosition(); getParentTab().updateResultsPollInUi("realpha"); } SM.Debug("MENU: P for " + liveVehicle.get(contextMenuPosition).id); break; } default: throw new IllegalStateException("Unexpected value: " + menuId); } }); } /** * create context menu */ @Override public Dialog onCreateDialog(int id) { if (id == 1) { return iconContextMenu.createMenu(getString(R.string.options)); } return super.onCreateDialog(id); } /* list item long click handler * used to show the context menu */ private final AdapterView.OnItemLongClickListener itemLongClickHandler = ((parent, view, position, id) -> { // save position contextMenuPosition = position; showDialog(1); return true; }); public void displayVehicle(boolean withZoom, double latZoom, double lngZoom) { //TODO: add makers for vehicles } public void showOpenedBalloon(boolean demo) { //TODO: add show balloon } public void showLoadingDialog(String message) { loadingDialog = new Dialog(context); loadingDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); loadingDialog.setContentView(R.layout.dialogloading); loadingDialog.setCancelable(true); loadingDialog.setCanceledOnTouchOutside(false); Button cancel = (Button) loadingDialog.findViewById(R.id.buttonCancel); cancel.setVisibility(View.GONE); TextView textView1 = (TextView) loadingDialog.findViewById(R.id.textView1); textView1.setText(message); loadingDialog.show(); } // save Vehicles @Override public void vehiclesReceived(ArrayList list) { SM.Debug("vehiclesReceived"); isAck = true; liveVehicle = list; disabledVehicles = new ArrayList<>(); displayedVehicles = new ArrayList<>(); // set displayed to false and add icons to hashTable int i = 0; SM.Debug("DISPLAY LIVE VEHICLE"); for (Vehicle veh : list) { SM.Debug(veh.toString()); liveVehicle.get(i).id = veh.sc_id; i++; // set vehicle to false displayedVehicles.add(AppParams.DEMO && (veh.sc_id == 101 || veh.sc_id == 102)); // set disable to false disabledVehicles.add(!veh.status); // add vehicle to hash Table according to driver_id if (tableHashOverlay.get((int) veh.driver_id) == null) // if doesn't exist { ArrayList array = new ArrayList<>(); SuperVehicle superVehicle = new SuperVehicle(veh.sc_id, veh.imei, veh.lp, veh.name, veh.driver_id, veh.time_route, veh.GPS_reporting_interval, veh.is_stolen); array.add(superVehicle); tableHashOverlay.put((int) veh.driver_id, array); } else { // another vehicle with same driver_id exists ArrayList array = tableHashOverlay.get((int) veh.driver_id); SuperVehicle superVehicle = new SuperVehicle(veh.sc_id, veh.imei, veh.lp, veh.name, veh.driver_id, veh.time_route, veh.GPS_reporting_interval, veh.is_stolen); // add vehicle to array if (array != null) { array.add(superVehicle); } } } // set adapter adapter = new VehiclesGridViewAdapter(activity, context, list, disabledVehicles); adapter.notifyDataSetChanged(); // Update UI myHandler.post(updateResultsRUN); // hide loading dialog loadingDialog.cancel(); if (!AppParams.DEMO) getLastPos(); } // Create runnable for posting final Runnable updateResultsRUN = this::updateResultsUI; // show vehicles in gridView private void updateResultsUI() { gridVehicle.setAdapter(adapter); try { // hide loading dialog loadingDialog.cancel(); } catch (Exception e) { SM.Exception(e.toString()); } } @Override public void refreshMap() { // Update UI myHandler.post(updateMapResults); } @Override public void pollReceived(int position, double lat, double lng) { // Update UI latPoll = lat; lngPoll = lng; this.position = position; myHandler.post(updatePollResults); } @Override public void vehicleStatusReceived(long imei, int opCode, int status) { vehStatus = status; SM.Debug("UpdateOptions from APP with-> imei: " + imei + " | opCode: " + opCode + " | status: " + vehStatus); contextMenuPosition = getPositionFromImei(imei); // Update UI myHandler.post(updateOptionsRUN); } @Override public void emergencyAlarmReceived(int position, double lat, double lng) { pollReceived(position,lat,lng); } public void updatePosition(int pos) { contextMenuPosition = pos; } // Create runnable for posting final Runnable updateMapResults = () -> displayVehicle(false, LAT_OUTLIMIT, LNG_OUTLIMIT); // Create runnable for posting final Runnable updatePollResults = () -> { if (position != -1) { SM.Debug("updatePosition :" + position + " last value:" + displayedVehicles.get(position)); // change in adapter adapter.changeDisplayed(position, true); } displayVehicle(true, latPoll, lngPoll); }; // Create runnable for posting final Runnable updateOptionsRUN = this::updateOptionsUI; private void updateOptionsUI() { SM.Debug("REFRESHDisableEnable" + (Boolean.TRUE.equals(disabledVehicles.get(contextMenuPosition)) ? "true" : "false")); // change Enable/Disable in adapter adapter.changeDisabled(contextMenuPosition, disabledVehicles.get(contextMenuPosition)); } /** * visual modifications when a vehicle is clicked * * @param position position in grid that was clicked * @param view View in which will do the modifications */ private void itemClick(int position, View view) { // change displayed state displayedVehicles.set(position, !Boolean.TRUE.equals(displayedVehicles.get(position))); // change in the adapter adapter.changeDisplayed(position, displayedVehicles.get(position)); // change check image for selected value VehiclesGridViewAdapter.ViewHolder viewLive = (VehiclesGridViewAdapter.ViewHolder) view.getTag(); if (Boolean.TRUE.equals(displayedVehicles.get(position))) viewLive.imgViewChecked.setImageResource(R.drawable.checked); else viewLive.imgViewChecked.setImageResource(R.drawable.unchecked); // check if all values are identical boolean identical = true; for (Boolean displ : displayedVehicles) if (!Objects.equals(displ, displayedVehicles.get(0))) { identical = false; break; } // change image when all values are identical if (identical && Boolean.TRUE.equals(displayedVehicles.get(0))) { imageViewCheckAll.setSelected(true); imageViewCheckAll.setBackgroundResource(R.drawable.check_all); } else if (identical && Boolean.TRUE.equals(!displayedVehicles.get(0))) { imageViewCheckAll.setSelected(false); imageViewCheckAll.setBackgroundResource(R.drawable.uncheck_all); } // display vehicle displayVehicle(true, LAT_OUTLIMIT, LNG_OUTLIMIT); } /** * get last position for all vehicles */ private void getLastPos() { getParentTab().executeNetworkStuff(new String[]{OperationCodes.GetLastPositions + "", AppParams.USERID + ""}); } /** * send a command that enable/disable a vehicle or request a poll * * @param radioCode is a hardcoded values set to 30 * @param opCode the operation code. It can be : * MENU_ENABLE = 150, * MENU_DISABLE = 151, * MENU_REMOTE = 161, * MENU_POLL = 154; * @param scId vehicle imei for which we do the operation */ public void optionForUnit(int radioCode, int opCode, String scId) { // last values is set to 0 for disable and 1 for others getParentTab().executeNetworkStuff(new String[]{OperationCodes.Option4Unit + "", radioCode + "", (opCode == MENU_DISABLE ? MENU_ENABLE : opCode) + "", scId, (opCode == MENU_DISABLE ? 0 : 1) + ""}); } /** * return the position in the grid for a Vehicle specified by imei * * @param imei vehicle's imei */ private int getPositionFromImei(long imei) { int positionFromImei = -1; for (int i = 0; i < liveVehicle.size(); i++) if (liveVehicle.get(i).imei.equalsIgnoreCase(imei + "")) positionFromImei = i; SM.Debug("Position: " + positionFromImei); return positionFromImei; } /** * Register for broadcasts */ private void registerBroadcastIntents() { // zone and channel change intent IntentFilter intentFilter = new IntentFilter(OperationCodes.UNIT_STATUS_UPDATE + ""); this.registerReceiver(mReceiver, intentFilter); } //The BroadcastReceiver that listens for Notification broadcasts public final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(OperationCodes.UNIT_STATUS_UPDATE + "")) { try { final String[] extra = intent.getStringExtra("unitStatus").split("#"); SM.Debug("extra has " + extra.length + " objects"); Toast.makeText(context, "Unit was " + (Integer.parseInt(extra[1]) == 0 ? "DISABLED" : "ENABLED"), Toast.LENGTH_LONG).show(); myHandler.post(() -> { vehStatus = Integer.parseInt(extra[1]); contextMenuPosition = getPositionFromImei(Integer.parseInt(extra[0])); disabledVehicles.remove(contextMenuPosition); // invert logic is used disabledVehicles.add(contextMenuPosition, vehStatus != 1); adapter.changeDisabled(contextMenuPosition, disabledVehicles.get(contextMenuPosition)); }); } catch (Exception ex) { SM.Exception("Exception in live BroadCastReceived" + ex.toString()); } } } }; }