package im.actor.map; import android.Manifest; import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationManager; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v13.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.view.animation.AccelerateInterpolator; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.inputmethod.InputMethodManager; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.MapFragment; 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.Marker; import com.google.android.gms.maps.model.MarkerOptions; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import im.actor.maps.google.R; public class MapPickerActivity extends AppCompatActivity implements GoogleMap.OnMyLocationChangeListener, AdapterView.OnItemClickListener, GoogleMap.OnMapLongClickListener, GoogleMap.OnMarkerClickListener, AbsListView.OnScrollListener { private static final int PERMISSION_REQ_LOCATION = 0; private static final String LOG_TAG = "MapPickerActivity"; private GoogleMap mMap; // Might be null if Google Play services APK is not available. private Location currentLocation; private LatLng geoData; private PlaceFetchingTask fetchingTask; private Marker currentPick; View select; private ListView list; private TextView status; private View header; private ProgressBar loading; private SearchView searchView; private ImageView fullSizeButton; private View listHolder; private View mapHolder; private View defineMyLocationButton; private TextView accuranceView; private View pickCurrent; private HashMap<String, Marker> markers; private ArrayList<MapItem> places; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.picker_activity_map_picker); list = (ListView) findViewById(R.id.list); list.setOnScrollListener(this); list.setOnItemClickListener(this); list.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); loading = (ProgressBar) findViewById(R.id.loading); status = (TextView) findViewById(R.id.status); header = findViewById(R.id.header); listHolder = findViewById(R.id.listNearbyHolder); mapHolder = findViewById(R.id.mapholder); accuranceView = (TextView) findViewById(R.id.accurance); setUpMapIfNeeded(); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(false); fullSizeButton = (ImageView) findViewById(R.id.full); fullSizeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { togglePlacesList(); } }); defineMyLocationButton = findViewById(R.id.define_my_location); defineMyLocationButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Location location = mMap.getMyLocation(); if (location != null) { LatLng target = new LatLng(location.getLatitude(), location.getLongitude()); CameraPosition.Builder builder = new CameraPosition.Builder(); builder.zoom(17); builder.target(target); mMap.animateCamera(CameraUpdateFactory.newCameraPosition(builder.build())); } else { Toast.makeText(getBaseContext(), R.string.picker_map_pick_my_wait, Toast.LENGTH_SHORT).show(); } } }); pickCurrent = findViewById(R.id.pick_current); pickCurrent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (currentLocation != null) { Intent returnIntent = new Intent(); returnIntent.putExtra("latitude", currentLocation.getLatitude()); returnIntent.putExtra("longitude", currentLocation.getLongitude()); setResult(RESULT_OK, returnIntent); finish(); } } }); // we dont need these buttons select = findViewById(R.id.select); select.setEnabled(false); findViewById(R.id.select_text).setEnabled(false); select.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent returnIntent = new Intent(); returnIntent.putExtra("latitude", geoData.latitude); returnIntent.putExtra("longitude", geoData.longitude); setResult(RESULT_OK, returnIntent); finish(); } }); View cancel = findViewById(R.id.cancel); cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { finish(); } }); } private int defaultHeight = 0; protected void togglePlacesList() { // todo animate it if (listHolder.getVisibility() == View.GONE) { fullSizeButton.setEnabled(false); float startSize = findViewById(R.id.container).getHeight(); final ValueAnimator valueAnimator = ValueAnimator.ofFloat(startSize, defaultHeight); valueAnimator.setDuration(300); valueAnimator.setInterpolator(new AccelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mapHolder.getLayoutParams().height = ((Float) valueAnimator.getAnimatedValue()).intValue(); mapHolder.requestLayout(); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { fullSizeButton.setEnabled(true); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); valueAnimator.start(); listHolder.setVisibility(View.VISIBLE); fullSizeButton.setImageResource(R.drawable.picker_map_fullscreen_icon); } else { fullSizeButton.setEnabled(false); float endSize = findViewById(R.id.container).getHeight(); defaultHeight = mapHolder.getHeight(); final ValueAnimator valueAnimator = ValueAnimator.ofFloat(defaultHeight, endSize); valueAnimator.setDuration(300); valueAnimator.setInterpolator(new AccelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mapHolder.getLayoutParams().height = ((Float) valueAnimator.getAnimatedValue()).intValue(); mapHolder.requestLayout(); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { fullSizeButton.setEnabled(true); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); AlphaAnimation hideAnimation = new AlphaAnimation(1, 0); hideAnimation.setDuration(300); hideAnimation.setInterpolator(new AccelerateInterpolator()); hideAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { fullSizeButton.setImageResource(R.drawable.picker_map_halfscreen_icon); listHolder.setVisibility(View.GONE); valueAnimator.start(); } @Override public void onAnimationRepeat(Animation animation) { } }); listHolder.startAnimation(hideAnimation); } } @Override protected void onResume() { super.onResume(); setUpMapIfNeeded(); } private void setUpMapIfNeeded() { // Do a null check to confirm that we have not already instantiated the map. if (mMap == null) { // Try to obtain the map from the SupportMapFragment. ((MapFragment) getFragmentManager().findFragmentById(R.id.map)) .getMapAsync(googleMap -> { mMap = googleMap; if (mMap != null) { setUpMap(); } }); } } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case android.R.id.home: onBackPressed(); break; } return super.onOptionsItemSelected(item); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.picker_map, menu); searchView = (SearchView) menu.getItem(0).getActionView(); searchView.setIconifiedByDefault(true); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { fetchPlaces(s); hideKeyBoard(); return true; } @Override public boolean onQueryTextChange(String s) { return false; } }); searchView.setOnCloseListener(new SearchView.OnCloseListener() { @Override public boolean onClose() { hideKeyBoard(); return false; } }); return true; } /** * This is where we can add markers or lines, add listeners or move the camera. In this case, we * just add a marker near Africa. * <p/> * This should only be called once and when we are sure that {@link #mMap} is not null. */ private void setUpMap() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQ_LOCATION); im.actor.runtime.Log.d("Permissions", "MapPickerActivity.setUpMap - no permission :c"); return; } } LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); for (String provider : locationManager.getAllProviders()) { currentLocation = locationManager.getLastKnownLocation(provider); if (currentLocation != null) { break; } } if (currentLocation != null) { mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()), 14)); fetchPlaces(null); } mMap.setOnMyLocationChangeListener(this); mMap.getUiSettings().setMyLocationButtonEnabled(false); mMap.getUiSettings().setZoomControlsEnabled(false); mMap.getUiSettings().setCompassEnabled(false); mMap.setMyLocationEnabled(true); mMap.setOnMapLongClickListener(this); mMap.setOnMarkerClickListener(this); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == PERMISSION_REQ_LOCATION) { if (grantResults.length > 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { setUpMap(); } else { //FIXME: if user checks "Don't ask again" button, in next open of activity, it will be finished without any notice about permissions. Need to show toast or alert dialog (with button which redirects to app info (for granting permission manually)). finish(); } } } } private void fetchPlaces(String query) { mMap.clear(); if (currentLocation == null) { Toast.makeText(this, R.string.picker_map_sory_notdefined, Toast.LENGTH_SHORT).show(); return; } list.setAdapter(null); status.setVisibility(View.GONE); loading.setVisibility(View.VISIBLE); fetchingTask = new PlaceFetchingTask(query, 50, currentLocation.getLatitude(), currentLocation.getLongitude()) { @Override protected void onPostExecute(Object o) { Log.i(LOG_TAG, o.toString()); if (o instanceof ArrayList) { loading.setVisibility(View.GONE); status.setVisibility(View.GONE); header.setVisibility(View.VISIBLE); list.setVisibility(View.VISIBLE); places = (ArrayList<MapItem>) o; if (places.isEmpty()) { status.setText(R.string.picker_map_nearby_empty); } else { list.setAdapter(new PlacesAdapter(MapPickerActivity.this, places)); showItemsOnTheMap(places); } } else { places = new ArrayList<MapItem>(); list.setAdapter(null); header.setVisibility(View.GONE); status.setText(R.string.picker_internalerror); Toast.makeText(MapPickerActivity.this, o.toString(), Toast.LENGTH_SHORT).show(); } } }; fetchingTask.execute(); } void hideKeyBoard() { searchView.clearFocus(); this.getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); View focusedView = this.getCurrentFocus(); if (focusedView != null) { InputMethodManager inputMethodManager = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(focusedView.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); } } private void showItemsOnTheMap(ArrayList<MapItem> array) { markers = new HashMap<String, Marker>(); for (MapItem mapItem : array) { markers.put(mapItem.id, mMap.addMarker(new MarkerOptions() .position(mapItem.getLatLng()) // .title(mapItem.name) .draggable(false) .icon(BitmapDescriptorFactory.fromResource(R.drawable.picker_map_marker)) )); } } @Override public void onMyLocationChange(Location location) { if (currentLocation == null) { // do we need to attach our location on the start? this.currentLocation = location; fetchPlaces(null); mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 14)); } this.currentLocation = location; accuranceView.setText(getString(R.string.picker_map_pick_my_accuracy, (int) currentLocation.getAccuracy())); Log.d("Location changed", location.toString()); } @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { MapItem mapItem = (MapItem) adapterView.getItemAtPosition(position); Intent returnIntent = new Intent(); returnIntent.putExtra("latitude", mapItem.getLatLng().latitude); returnIntent.putExtra("longitude", mapItem.getLatLng().longitude); returnIntent.putExtra("street", mapItem.vicinity); returnIntent.putExtra("place", mapItem.name); setResult(RESULT_OK, returnIntent); finish(); /* select.setEnabled(true); findViewById(R.id.select_text).setEnabled(true); geoData = mapItem.getLatLng(); mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(geoData, 16)); */ } @Override public void onMapLongClick(LatLng latLng) { select.setEnabled(true); findViewById(R.id.select_text).setEnabled(true); //mMap.clear(); // geoData = latLng; if (currentPick == null) { MarkerOptions currentPickOptions = new MarkerOptions() .draggable(true) .position(geoData); //currentPick = mMap.addMarker(currentPickOptions); } else { //currentPick.setPosition(geoData); } } @Override public boolean onMarkerClick(Marker marker) { if (currentPick != null) currentPick.remove(); String placeId = null; for (Map.Entry<String, Marker> markerIterator : markers.entrySet()) { if (markerIterator.getValue().equals(marker)) { placeId = markerIterator.getKey(); break; } } int position = -1; for (int i = 0; i < places.size(); i++) { MapItem place = places.get(i); if (place.id.equals(placeId)) { position = i; break; } } if (position != -1) { list.setItemChecked(position, true); list.smoothScrollToPosition(position); if (listHolder.getVisibility() == View.GONE) { togglePlacesList(); } } mMap.animateCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(marker.getPosition().latitude, marker.getPosition().longitude), 16)); //currentPick = marker; return true; } @Override public void onScrollStateChanged(AbsListView absListView, int i) { switch (i) { case SCROLL_STATE_TOUCH_SCROLL: hideKeyBoard(); break; } } @Override public void onScroll(AbsListView absListView, int i, int i2, int i3) { } }