package io.github.droidkaigi.confsched.fragment; import android.Manifest; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.UiSettings; import com.google.android.gms.maps.model.BitmapDescriptorFactory; 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.HashMap; import java.util.List; import java.util.Map; import io.github.droidkaigi.confsched.R; import io.github.droidkaigi.confsched.databinding.FragmentMapBinding; import io.github.droidkaigi.confsched.model.PlaceMap; import io.github.droidkaigi.confsched.util.ResourceUtil; import permissions.dispatcher.NeedsPermission; import permissions.dispatcher.OnNeverAskAgain; import permissions.dispatcher.OnPermissionDenied; import permissions.dispatcher.OnShowRationale; import permissions.dispatcher.PermissionRequest; import permissions.dispatcher.RuntimePermissions; import rx.Observable; import static io.github.droidkaigi.confsched.fragment.MapFragmentPermissionsDispatcher.initGoogleMapWithCheck; @RuntimePermissions public class MapFragment extends BaseFragment { public static final LatLng LAT_LNG_CENTER = new LatLng(35.604757, 139.683788); private static final int DEFAULT_ZOOM = 17; private FragmentMapBinding binding; private List<PlaceMap> placeMapList; private Map<Integer, Marker> markers = new HashMap<>(); public static MapFragment newInstance() { return new MapFragment(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); placeMapList = PlaceMap.createList(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentMapBinding.inflate(inflater, container, false); binding.mapSearchView.setOnVisibilityChangeListener(() -> getActivity().invalidateOptionsMenu()); initGoogleMapWithCheck(this); setHasOptionsMenu(true); initBackPressed(); return binding.getRoot(); } @Override public void onDestroyView() { FragmentManager fm = getChildFragmentManager(); SupportMapFragment map = (SupportMapFragment) fm.findFragmentById(R.id.map); FragmentTransaction ft = fm.beginTransaction(); ft.remove(map); ft.commitAllowingStateLoss(); super.onDestroyView(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { menuInflater.inflate(R.menu.menu_map, menu); if (binding.mapSearchView.isVisible()) { menu.findItem(R.id.item_search).setIcon(R.drawable.ic_place_white_24dp_vector); } else { menu.findItem(R.id.item_search).setIcon(R.drawable.ic_view_list_white_24dp_vector); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.item_search: binding.mapSearchView.toggle(); break; } return super.onOptionsItemSelected(item); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); MapFragmentPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); } @NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION) void initGoogleMap() { SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map); mapFragment.getMapAsync(googleMap -> { //noinspection MissingPermission googleMap.setMyLocationEnabled(true); binding.mapSearchView.bindData(placeMapList, placeMap -> { LatLng latLng = new LatLng(placeMap.latitude, placeMap.longitude); int duration = getResources().getInteger(R.integer.map_camera_move_mills); googleMap.animateCamera(CameraUpdateFactory.newLatLng(latLng), duration, null); Marker marker = markers.get(placeMap.nameRes); if (marker != null) marker.showInfoWindow(); }); binding.loadingView.setVisibility(View.GONE); googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); googleMap.setIndoorEnabled(true); googleMap.setBuildingsEnabled(true); googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LAT_LNG_CENTER, DEFAULT_ZOOM)); UiSettings mapUiSettings = googleMap.getUiSettings(); mapUiSettings.setCompassEnabled(true); renderMarkers(placeMapList, googleMap); }); } @OnPermissionDenied(Manifest.permission.ACCESS_FINE_LOCATION) void onFineLocationDenied() { Toast.makeText(getContext(), R.string.map_fine_location_denied, Toast.LENGTH_SHORT).show(); } @OnNeverAskAgain(Manifest.permission.ACCESS_FINE_LOCATION) void onFineLocationNeverAskAgain() { Toast.makeText(getContext(), R.string.map_fine_location_never_askagain, Toast.LENGTH_SHORT).show(); } @OnShowRationale(Manifest.permission.ACCESS_FINE_LOCATION) void showRationaleForFineLocation(PermissionRequest request) { showRationaleDialog(R.string.map_fine_location_rationale, request); } private void showRationaleDialog(@StringRes int messageResId, final PermissionRequest request) { new AlertDialog.Builder(getContext()) .setPositiveButton(R.string.map_button_allow, (dialog, which) -> { request.proceed(); }) .setNegativeButton(R.string.map_button_deny, (dialog, which) -> { request.cancel(); }) .setCancelable(false) .setMessage(messageResId) .show(); } private void renderMarkers(List<PlaceMap> placeMaps, GoogleMap googleMap) { Observable.from(placeMaps).forEach(placeMap -> { MarkerOptions options = new MarkerOptions() .position(new LatLng(placeMap.latitude, placeMap.longitude)) .title(getString(placeMap.nameRes)) .icon(BitmapDescriptorFactory.fromBitmap(ResourceUtil.getBitmap(getContext(), placeMap.markerRes))) .snippet(getString(placeMap.buildingNameRes)); Marker marker = googleMap.addMarker(options); markers.put(placeMap.nameRes, marker); }); } private void initBackPressed() { binding.getRoot().setFocusableInTouchMode(true); binding.getRoot().setOnKeyListener((v, keyCode, event) -> { if (keyCode == KeyEvent.KEYCODE_BACK && binding.mapSearchView.isVisible()) { binding.mapSearchView.revealOff(); return true; } return false; }); } }