package com.pam.codenamehippie.ui; import android.content.Intent; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.util.Log; import android.view.View; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.RelativeLayout; import android.widget.Toast; import android.widget.ViewSwitcher; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.location.LocationServices; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener; import com.google.android.gms.maps.MapView; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.model.BitmapDescriptor; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.pam.codenamehippie.R; import com.pam.codenamehippie.http.exception.HttpReponseException; import com.pam.codenamehippie.modele.AdresseModele; import com.pam.codenamehippie.modele.OrganismeModele; import com.pam.codenamehippie.modele.UtilisateurModele; import com.pam.codenamehippie.modele.depot.AlimentaireModeleDepot; import com.pam.codenamehippie.modele.depot.DepotManager; import com.pam.codenamehippie.modele.depot.ObservateurDeDepot; import com.pam.codenamehippie.modele.depot.OrganismeModeleDepot; import com.pam.codenamehippie.ui.adapter.CarteAdapterOption; import com.sothree.slidinguppanel.SlidingUpPanelLayout; import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState.ANCHORED; import static com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState.COLLAPSED; import static com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState.EXPANDED; import static com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState.HIDDEN; public class MapsActivity extends HippieActivity implements OnMapReadyCallback, ExpandableListView.OnGroupClickListener, SlidingUpPanelLayout.PanelSlideListener, ObservateurDeDepot<OrganismeModele>, OnMarkerClickListener { private static final class PrepareMarkerAsyncTask extends AsyncTask<OrganismeModele, MarkerOptions, LatLngBounds.Builder> { private final MapsActivity activity; private final int listeType; public PrepareMarkerAsyncTask(@NonNull MapsActivity activity) { super(); this.activity = activity; this.listeType = this.activity.adapter.getListeType(); } public static PrepareMarkerAsyncTask newInstance(MapsActivity activity) { return new PrepareMarkerAsyncTask(activity); } @Override protected void onPreExecute() { synchronized (this.activity.mapLock) { if (this.activity.map != null) { this.activity.map.clear(); } } } @Override protected LatLngBounds.Builder doInBackground(OrganismeModele... organismes) { if ((this.isCancelled()) || (organismes.length == 0)) { return null; } synchronized (this.activity.mapLock) { while (this.activity.map == null) { if (this.isCancelled()) { return null; } try { this.activity.mapLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } LatLngBounds.Builder boundsBuilder = LatLngBounds.builder(); BitmapDescriptor dispoMarker = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory .HUE_GREEN); BitmapDescriptor reserveeMarker = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED); BitmapDescriptor defautMarker = BitmapDescriptorFactory.defaultMarker(); for (OrganismeModele organisme : organismes) { if (this.isCancelled()) { break; } AdresseModele adresse = organisme.getAdresse(); LatLng point = this.activity.getLocationFromName(adresse.string()); if (point != null) { MarkerOptions marker = new MarkerOptions().position(point) .title(organisme.getNom()); switch (this.listeType) { case CarteAdapterOption.LISTE_TYPE_MARCHANDISE_DISPO: marker.icon(dispoMarker); break; case CarteAdapterOption.LISTE_TYPE_MARCHANDISE_RESERVEE: marker.icon(reserveeMarker); break; default: marker.icon(defautMarker); break; } this.publishProgress(marker); boundsBuilder.include(point); } } return boundsBuilder; } @Override protected void onPostExecute(LatLngBounds.Builder boundsBuilder) { LatLngBounds bounds = (boundsBuilder != null) ? boundsBuilder.build() : null; CameraUpdate cameraUpdate = null; this.activity.cacherLaProgressbar(); if (this.activity.lastKnownLocation != null) { LatLng lastKnownLocationPoint = new LatLng(this.activity.lastKnownLocation.getLatitude(), this.activity.lastKnownLocation.getLongitude() ); CameraPosition position = CameraPosition.builder() .target(lastKnownLocationPoint) .zoom(12.5f) .build(); cameraUpdate = CameraUpdateFactory.newCameraPosition(position); } else if (bounds != null) { // On set la carte pour projeter sur la taille de l'écran. // C'est pas idéal, mais ça fait. int width = this.activity.getResources().getDisplayMetrics().widthPixels; int height = this.activity.getResources().getDisplayMetrics().heightPixels; int padding = this.activity.getResources() .getDimensionPixelOffset(R.dimen.activity_vertical_margin) * 2; ActionBar bar = this.activity.getSupportActionBar(); if (bar != null) { padding += bar.getHeight(); } cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding); } if (cameraUpdate != null) { this.activity.map.animateCamera(cameraUpdate); } this.activity.prepareMarkerAsyncTask = null; } @Override protected void onProgressUpdate(MarkerOptions... markers) { synchronized (this.activity.mapLock) { this.activity.map.addMarker(markers[0]); } } } private static final int REQUEST_FINE_LOCATION = 1; private final Object mapLock = new Object(); private volatile List<OrganismeModele> listOrganisme = new ArrayList<>(); private SlidingUpPanelLayout slidingLayout; private ExpandableListView expandableListView; private volatile GoogleMap map; private volatile Location lastKnownLocation; private MapView mapView; private RelativeLayout mapViewContainer; private CarteAdapterOption adapter; private AsyncTask prepareMarkerAsyncTask; private int orgId; private Geocoder geocoder; private Boolean hasFineLocation = true; private ViewSwitcher panelViewSwitcher; private int panelHeight; /** * preparer la carte google et des donnees. * * @param savedInstanceState */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.activity_maps_plus); UtilisateurModele uc = this.authentificateur.getUtilisateur(); OrganismeModele org = (uc != null) ? uc.getOrganisme() : null; this.geocoder = new Geocoder(this); this.panelViewSwitcher = ((ViewSwitcher) this.findViewById(R.id.panel_view_switcher)); this.mapViewContainer = ((RelativeLayout) this.findViewById(R.id.mapView)); this.mapView = ((MapView) this.findViewById(R.id.map)); this.mapView.onCreate(savedInstanceState); this.slidingLayout = (SlidingUpPanelLayout) this.findViewById(R.id.sliding_layout); this.slidingLayout.setAnchorPoint(0.6f); this.panelHeight = this.slidingLayout.getPanelHeight(); this.slidingLayout.setPanelState(HIDDEN); this.slidingLayout.setPanelSlideListener(this); this.expandableListView = (ExpandableListView) this.findViewById(R.id.expandableListView); // On fait disparaitre le chevron. this.expandableListView.setGroupIndicator(null); this.adapter = new CarteAdapterOption(this, org, this.panelViewSwitcher); this.expandableListView.setAdapter(this.adapter); // mettre le listener pour le click de group de l'expandablelistview this.expandableListView.setOnGroupClickListener(this); // FIXME: Checker dans this.getIntent().getExtras() pour afficher les bonnes listes. this.googleApiClient = new GoogleApiClient.Builder(this).useDefaultAccount() .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) != PERMISSION_GRANTED) { // TODO: Expliquer la pourquoi on veux cette permission. // if (ActivityCompat.shouldShowRequestPermissionRationale(this, ACCESS_FINE_LOCATION)) { // } else { ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION}, REQUEST_FINE_LOCATION); // } } } @Override public void onStart() { super.onStart(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (this.permissionsResult != null) { switch (requestCode) { case REQUEST_FINE_LOCATION: this.hasFineLocation = this.permissionsResult.get(ACCESS_FINE_LOCATION); break; } } } @Override protected void onResume() { super.onResume(); this.mapView.onResume(); OrganismeModeleDepot organismeModeleDepot = DepotManager.getInstance().getOrganismeModeleDepot(); AlimentaireModeleDepot alimentaireModeleDepot = DepotManager.getInstance().getAlimentaireModeleDepot(); alimentaireModeleDepot.ajouterUnObservateur(this.adapter); this.peuplerListeOrganisme(organismeModeleDepot); } @Override protected void onPause() { super.onPause(); this.mapView.onPause(); if (this.prepareMarkerAsyncTask != null) { this.prepareMarkerAsyncTask.cancel(true); } AlimentaireModeleDepot alimentaireModeleDepot = DepotManager.getInstance().getAlimentaireModeleDepot(); alimentaireModeleDepot.supprimerTousLesObservateurs(); } @Override public void onStop() { super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); this.mapView.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); this.mapView.onSaveInstanceState(outState); } @Override public void onLowMemory() { super.onLowMemory(); this.mapView.onLowMemory(); } /** * Manipulates the map once available. * This callback is triggered when the map is ready to be used. */ @Override public void onMapReady(GoogleMap googleMap) { synchronized (this.mapLock) { this.map = googleMap; this.mapLock.notifyAll(); } this.map.setMyLocationEnabled((this.hasFineLocation) || (this.lastKnownLocation != null)); this.map.setBuildingsEnabled(true); this.map.getUiSettings().setMapToolbarEnabled(false); this.map.getUiSettings().setMyLocationButtonEnabled(true); this.map.setOnMarkerClickListener(this); } @Override public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) { if (!parent.isGroupExpanded(groupPosition)) { parent.expandGroup(groupPosition); this.slidingLayout.setPanelState(ANCHORED); } else if (this.slidingLayout.getPanelState() == ANCHORED) { this.slidingLayout.setPanelState(EXPANDED); } else { parent.collapseGroup(groupPosition); this.slidingLayout.setPanelState(PanelState.COLLAPSED); } return true; } /** * Méthode permettant de soumettre des requete géocoding. Retourne le premier résultat du * géocoder. * * @param name * une string à chercher * * @return LatLng la longitude et latitude de l'addresse */ public LatLng getLocationFromName(String name) { class Local { } String TAG = Local.class.getEnclosingMethod().getName(); if (!Geocoder.isPresent()) { this.runOnUiThread(new Runnable() { @Override public void run() { Snackbar.make(MapsActivity.this.mapView, "Geocoder service unavailable", Snackbar.LENGTH_LONG ).show(); } }); return null; } Log.d(TAG, name); try { List<Address> addresses = this.geocoder.getFromLocationName(name, 5); if ((addresses == null) || (addresses.size() == 0)) { Log.e(TAG, "Le géocoder a retourné aucun résultat pour " + name); return null; } Address location = addresses.get(0); if (location == null) { return null; } return new LatLng(location.getLatitude(), location.getLongitude()); } catch (Exception ex) { Log.e(TAG, "Erreur au géocoding", ex); } return null; } public void onButtonClick(View v) { OrganismeModeleDepot organismeModeleDepot = DepotManager.getInstance().getOrganismeModeleDepot(); switch (v.getId()) { case R.id.marchandiseDisponible: // affiche denree disponible sur la carte Toast.makeText(this.getApplicationContext(), " Denrées disponible ", Toast.LENGTH_SHORT ).show(); this.adapter.setOrganisme(null); this.adapter.setListeType(CarteAdapterOption.LISTE_TYPE_MARCHANDISE_DISPO); this.peuplerListeOrganisme(organismeModeleDepot); break; case R.id.mesReservation: // affiche mes reservations sur la carte if (this.orgId != -1) { this.adapter.setOrganisme(null); this.adapter.setListeType(CarteAdapterOption.LISTE_TYPE_MARCHANDISE_RESERVEE); this.peuplerListeOrganisme(organismeModeleDepot); } else { Snackbar.make(this.mapView, "Non disponible", Snackbar.LENGTH_LONG).show(); } break; case R.id.listeMarchandise: this.startActivity(new Intent(this, ListeMarchandisesDisponiblesActivity.class)); break; default: break; /* case R.id.main_liste_denree_disponible: // affiche les denrees disponible en liste Toast.makeText(this.getApplicationContext(), " Nouvelle activité ", Toast.LENGTH_SHORT ).show(); break; */ } } @Override public void surDebutDeRequete() { this.afficherLaProgressBar(); } @Override public void surChangementDeDonnees(List<OrganismeModele> modeles) { this.listOrganisme = modeles; OrganismeModele[] array = modeles.toArray(new OrganismeModele[modeles.size()]); this.prepareMarkerAsyncTask = PrepareMarkerAsyncTask.newInstance(this) .execute(array); } @Override public void surFinDeRequete() { if (this.map == null) { this.mapView.getMapAsync(this); } if (this.prepareMarkerAsyncTask == null) { this.cacherLaProgressbar(); } } @Override public void surErreur(IOException e) { if (e instanceof HttpReponseException) { switch (((HttpReponseException) e).getCode()) { case 404: //FIXME: Faire bon message. Snackbar.make(this.mapView, R.string.error_http_404, Snackbar.LENGTH_LONG) .show(); break; case 500: Snackbar.make(this.mapView, R.string.error_http_500, Snackbar.LENGTH_LONG) .show(); break; default: Snackbar.make(this.mapView, R.string.error_connection, Snackbar.LENGTH_LONG) .show(); break; } } else { Snackbar.make(this.mapView, R.string.error_connection, Snackbar.LENGTH_LONG).show(); } } @Override public void onConnected(Bundle bundle) { super.onConnected(bundle); if (this.hasFineLocation) { this.lastKnownLocation = LocationServices.FusedLocationApi.getLastLocation(this.googleApiClient); } } @Override public void onPanelSlide(View view, float v) { } @Override public void onPanelCollapsed(View view) { this.mapViewContainer.setVisibility(VISIBLE); ExpandableListAdapter adapter = this.expandableListView.getExpandableListAdapter(); if (adapter != null) { for (int i = adapter.getGroupCount(); i >= 0; i -= 1) { this.expandableListView.collapseGroup(i); } } } @Override public void onPanelExpanded(View view) { this.mapViewContainer.setVisibility(GONE); } @Override public void onPanelAnchored(View view) { this.mapViewContainer.setVisibility(VISIBLE); } @Override public void onPanelHidden(View view) { this.mapViewContainer.setVisibility(VISIBLE); } @Override public boolean onMarkerClick(Marker marker) { OrganismeModele adapterOrganisme = null; for (OrganismeModele organisme : this.listOrganisme) { if (organisme.getNom().compareToIgnoreCase((marker.getTitle())) == 0) { adapterOrganisme = organisme; break; } } this.adapter.setOrganisme(adapterOrganisme); if (this.slidingLayout.getPanelState() == ANCHORED || this.slidingLayout.getPanelState() == EXPANDED) { this.expandableListView.expandGroup(0, true); } else if (this.slidingLayout.getPanelState() == HIDDEN) { this.slidingLayout.setPanelState(COLLAPSED); } CameraPosition position = CameraPosition.builder(MapsActivity.this.map.getCameraPosition()) .target(marker.getPosition()) .build(); CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(position); this.map.animateCamera(cameraUpdate); return true; } public void peuplerListeOrganisme(OrganismeModeleDepot depot) { switch (this.adapter.getListeType()) { case CarteAdapterOption.LISTE_TYPE_MARCHANDISE_DISPO: depot.peuplerListeDonneur(); break; case CarteAdapterOption.LISTE_TYPE_MARCHANDISE_RESERVEE: depot.peuplerListeDonneurReservation(this.orgId); break; default: throw new IllegalStateException("Type de liste inconnu"); } } }