|
| 1 | +package fr.free.nrw.commons.LocationPicker; |
| 2 | + |
| 3 | +import android.content.Intent; |
| 4 | +import android.os.Bundle; |
| 5 | +import android.text.Html; |
| 6 | +import android.text.method.LinkMovementMethod; |
| 7 | +import android.view.Window; |
| 8 | +import android.view.animation.OvershootInterpolator; |
| 9 | +import android.widget.ImageView; |
| 10 | +import androidx.annotation.NonNull; |
| 11 | +import androidx.annotation.Nullable; |
| 12 | +import androidx.appcompat.app.ActionBar; |
| 13 | +import androidx.appcompat.app.AppCompatActivity; |
| 14 | +import androidx.appcompat.widget.AppCompatTextView; |
| 15 | +import androidx.constraintlayout.widget.ConstraintLayout; |
| 16 | +import androidx.lifecycle.Observer; |
| 17 | +import androidx.lifecycle.ViewModelProvider; |
| 18 | +import com.google.android.material.floatingactionbutton.FloatingActionButton; |
| 19 | +import com.mapbox.android.core.permissions.PermissionsManager; |
| 20 | +import com.mapbox.mapboxsdk.camera.CameraPosition; |
| 21 | +import com.mapbox.mapboxsdk.camera.CameraPosition.Builder; |
| 22 | +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; |
| 23 | +import com.mapbox.mapboxsdk.geometry.LatLng; |
| 24 | +import com.mapbox.mapboxsdk.location.LocationComponent; |
| 25 | +import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions; |
| 26 | +import com.mapbox.mapboxsdk.location.modes.CameraMode; |
| 27 | +import com.mapbox.mapboxsdk.location.modes.RenderMode; |
| 28 | +import com.mapbox.mapboxsdk.maps.MapView; |
| 29 | +import com.mapbox.mapboxsdk.maps.MapboxMap; |
| 30 | +import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener; |
| 31 | +import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener; |
| 32 | +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; |
| 33 | +import com.mapbox.mapboxsdk.maps.Style; |
| 34 | +import com.mapbox.mapboxsdk.maps.UiSettings; |
| 35 | +import fr.free.nrw.commons.R; |
| 36 | +import org.jetbrains.annotations.NotNull; |
| 37 | +import timber.log.Timber; |
| 38 | + |
| 39 | +/** |
| 40 | + * Helps to pick location and return the result with an intent |
| 41 | + */ |
| 42 | +public class LocationPickerActivity extends AppCompatActivity implements OnMapReadyCallback, |
| 43 | + OnCameraMoveStartedListener, OnCameraIdleListener, Observer<CameraPosition> { |
| 44 | + |
| 45 | + /** |
| 46 | + * cameraPosition : position of picker |
| 47 | + */ |
| 48 | + private CameraPosition cameraPosition; |
| 49 | + /** |
| 50 | + * markerImage : picker image |
| 51 | + */ |
| 52 | + private ImageView markerImage; |
| 53 | + /** |
| 54 | + * mapboxMap : map |
| 55 | + */ |
| 56 | + private MapboxMap mapboxMap; |
| 57 | + /** |
| 58 | + * mapView : view of the map |
| 59 | + */ |
| 60 | + private MapView mapView; |
| 61 | + /** |
| 62 | + * tvAttribution : credit |
| 63 | + */ |
| 64 | + private AppCompatTextView tvAttribution; |
| 65 | + |
| 66 | + @Override |
| 67 | + protected void onCreate(@Nullable final Bundle savedInstanceState) { |
| 68 | + super.onCreate(savedInstanceState); |
| 69 | + |
| 70 | + getWindow().requestFeature(Window.FEATURE_ACTION_BAR); |
| 71 | + final ActionBar actionBar = getSupportActionBar(); |
| 72 | + if (actionBar != null) { |
| 73 | + actionBar.hide(); |
| 74 | + } |
| 75 | + setContentView(R.layout.activity_location_picker); |
| 76 | + |
| 77 | + if (savedInstanceState == null) { |
| 78 | + cameraPosition = getIntent().getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION); |
| 79 | + } |
| 80 | + |
| 81 | + final LocationPickerViewModel viewModel = new ViewModelProvider(this) |
| 82 | + .get(LocationPickerViewModel.class); |
| 83 | + viewModel.getResult().observe(this, this); |
| 84 | + |
| 85 | + bindViews(); |
| 86 | + addBackButtonListener(); |
| 87 | + addPlaceSelectedButton(); |
| 88 | + addCredits(); |
| 89 | + getToolbarUI(); |
| 90 | + |
| 91 | + mapView.onCreate(savedInstanceState); |
| 92 | + mapView.getMapAsync(this); |
| 93 | + } |
| 94 | + |
| 95 | + /** |
| 96 | + * For showing credits |
| 97 | + */ |
| 98 | + private void addCredits() { |
| 99 | + tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); |
| 100 | + tvAttribution.setMovementMethod(LinkMovementMethod.getInstance()); |
| 101 | + } |
| 102 | + |
| 103 | + /** |
| 104 | + * Clicking back button destroy locationPickerActivity |
| 105 | + */ |
| 106 | + private void addBackButtonListener() { |
| 107 | + final ImageView backButton = findViewById(R.id.mapbox_place_picker_toolbar_back_button); |
| 108 | + backButton.setOnClickListener(view -> finish()); |
| 109 | + } |
| 110 | + |
| 111 | + /** |
| 112 | + * Binds mapView and location picker icon |
| 113 | + */ |
| 114 | + private void bindViews() { |
| 115 | + mapView = findViewById(R.id.map_view); |
| 116 | + markerImage = findViewById(R.id.location_picker_image_view_marker); |
| 117 | + tvAttribution = findViewById(R.id.tv_attribution); |
| 118 | + } |
| 119 | + |
| 120 | + /** |
| 121 | + * Binds the listeners |
| 122 | + */ |
| 123 | + private void bindListeners() { |
| 124 | + mapboxMap.addOnCameraMoveStartedListener( |
| 125 | + this); |
| 126 | + mapboxMap.addOnCameraIdleListener( |
| 127 | + this); |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * Gets toolbar color |
| 132 | + */ |
| 133 | + private void getToolbarUI() { |
| 134 | + final ConstraintLayout toolbar = findViewById(R.id.location_picker_toolbar); |
| 135 | + toolbar.setBackgroundColor(getResources().getColor(R.color.primaryColor)); |
| 136 | + } |
| 137 | + |
| 138 | + /** |
| 139 | + * Takes action when map is ready to show |
| 140 | + * @param mapboxMap map |
| 141 | + */ |
| 142 | + @Override |
| 143 | + public void onMapReady(final MapboxMap mapboxMap) { |
| 144 | + this.mapboxMap = mapboxMap; |
| 145 | + mapboxMap.setStyle(Style.MAPBOX_STREETS, style -> { |
| 146 | + adjustCameraBasedOnOptions(); |
| 147 | + bindListeners(); |
| 148 | + enableLocationComponent(style); |
| 149 | + }); |
| 150 | + } |
| 151 | + |
| 152 | + /** |
| 153 | + * move the location to the current media coordinates |
| 154 | + */ |
| 155 | + private void adjustCameraBasedOnOptions() { |
| 156 | + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); |
| 157 | + } |
| 158 | + |
| 159 | + /** |
| 160 | + * Enables location components |
| 161 | + * @param loadedMapStyle Style |
| 162 | + */ |
| 163 | + @SuppressWarnings( {"MissingPermission"}) |
| 164 | + private void enableLocationComponent(@NonNull final Style loadedMapStyle) { |
| 165 | + final UiSettings uiSettings = mapboxMap.getUiSettings(); |
| 166 | + uiSettings.setAttributionEnabled(false); |
| 167 | + |
| 168 | + // Check if permissions are enabled and if not request |
| 169 | + if (PermissionsManager.areLocationPermissionsGranted(this)) { |
| 170 | + |
| 171 | + // Get an instance of the component |
| 172 | + final LocationComponent locationComponent = mapboxMap.getLocationComponent(); |
| 173 | + |
| 174 | + // Activate with options |
| 175 | + locationComponent.activateLocationComponent( |
| 176 | + LocationComponentActivationOptions.builder(this, loadedMapStyle).build()); |
| 177 | + |
| 178 | + // Enable to make component visible |
| 179 | + locationComponent.setLocationComponentEnabled(true); |
| 180 | + |
| 181 | + // Set the component's camera mode |
| 182 | + locationComponent.setCameraMode(CameraMode.NONE); |
| 183 | + |
| 184 | + // Set the component's render mode |
| 185 | + locationComponent.setRenderMode(RenderMode.NORMAL); |
| 186 | + |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + /** |
| 191 | + * Acts on camera moving |
| 192 | + * @param reason int |
| 193 | + */ |
| 194 | + @Override |
| 195 | + public void onCameraMoveStarted(final int reason) { |
| 196 | + Timber.v("Map camera has begun moving."); |
| 197 | + if (markerImage.getTranslationY() == 0) { |
| 198 | + markerImage.animate().translationY(-75) |
| 199 | + .setInterpolator(new OvershootInterpolator()).setDuration(250).start(); |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * Acts on camera idle |
| 205 | + */ |
| 206 | + @Override |
| 207 | + public void onCameraIdle() { |
| 208 | + Timber.v("Map camera is now idling."); |
| 209 | + markerImage.animate().translationY(0) |
| 210 | + .setInterpolator(new OvershootInterpolator()).setDuration(250).start(); |
| 211 | + } |
| 212 | + |
| 213 | + /** |
| 214 | + * Takes action on camera position |
| 215 | + * @param position position of picker |
| 216 | + */ |
| 217 | + @Override |
| 218 | + public void onChanged(@Nullable CameraPosition position) { |
| 219 | + if (position == null) { |
| 220 | + position = new Builder() |
| 221 | + .target(new LatLng(mapboxMap.getCameraPosition().target.getLatitude(), |
| 222 | + mapboxMap.getCameraPosition().target.getLongitude())) |
| 223 | + .zoom(16).build(); |
| 224 | + } |
| 225 | + cameraPosition = position; |
| 226 | + } |
| 227 | + |
| 228 | + /** |
| 229 | + * Select the preferable location |
| 230 | + */ |
| 231 | + private void addPlaceSelectedButton() { |
| 232 | + final FloatingActionButton placeSelectedButton = findViewById(R.id.location_chosen_button); |
| 233 | + placeSelectedButton.setOnClickListener(view -> placeSelected()); |
| 234 | + } |
| 235 | + |
| 236 | + /** |
| 237 | + * Return the intent with required data |
| 238 | + */ |
| 239 | + void placeSelected() { |
| 240 | + final Intent returningIntent = new Intent(); |
| 241 | + returningIntent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION, |
| 242 | + mapboxMap.getCameraPosition()); |
| 243 | + setResult(AppCompatActivity.RESULT_OK, returningIntent); |
| 244 | + finish(); |
| 245 | + } |
| 246 | + |
| 247 | + @Override |
| 248 | + protected void onStart() { |
| 249 | + super.onStart(); |
| 250 | + mapView.onStart(); |
| 251 | + } |
| 252 | + |
| 253 | + @Override |
| 254 | + protected void onResume() { |
| 255 | + super.onResume(); |
| 256 | + mapView.onResume(); |
| 257 | + } |
| 258 | + |
| 259 | + @Override |
| 260 | + protected void onPause() { |
| 261 | + super.onPause(); |
| 262 | + mapView.onPause(); |
| 263 | + } |
| 264 | + |
| 265 | + @Override |
| 266 | + protected void onStop() { |
| 267 | + super.onStop(); |
| 268 | + mapView.onStop(); |
| 269 | + } |
| 270 | + |
| 271 | + @Override |
| 272 | + protected void onSaveInstanceState(final @NotNull Bundle outState) { |
| 273 | + super.onSaveInstanceState(outState); |
| 274 | + mapView.onSaveInstanceState(outState); |
| 275 | + } |
| 276 | + |
| 277 | + @Override |
| 278 | + protected void onDestroy() { |
| 279 | + super.onDestroy(); |
| 280 | + mapView.onDestroy(); |
| 281 | + } |
| 282 | + |
| 283 | + @Override |
| 284 | + public void onLowMemory() { |
| 285 | + super.onLowMemory(); |
| 286 | + mapView.onLowMemory(); |
| 287 | + } |
| 288 | +} |
0 commit comments