نمایش موقعیت کاربر

مستندات جامع اندروید

نسخه 1.0.3

هدف از این بخش از پروژه، نمایش یک نشانگر بر روی موقعیت فعلی کاربر و به‌روزرسانی مکان نمایشگر با تغییر موقعیت جغرافیایی کاربر است.

خطوط زیر را به وابستگی‌های فایل build.gradle (Module.app) اضافه کنید:

            	//Play Services
	implementation 'com.google.android.gms:play-services-gcm:17.0.0'
	implementation 'com.google.android.gms:play-services-location:21.0.1'
        

AndroidManifest.xml

دسترسی‌های زیر را برای به دست آوردن موقعیت جغرافیایی به این فایل اضافه کنید:

            	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        

activity_user_location.xml:

در این صفحه علاوه بر المان نقشه نشان، یک FloatingActionButton اضافه شده‌است که با هر بار کلیک بر روی آن، متد focusOnUserLocation صدا زده می‌شود. این متد در ادامه توضیح داده خواهد شد.

            	<?xml version="1.0" encoding="utf-8"?>
	<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
		xmlns:tools="http://schemas.android.com/tools"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		xmlns:app="http://schemas.android.com/apk/res-auto"
		tools:context=".UserLocation">

		<org.neshan.mapsdk.MapView
			android:id="@+id/map"
			android:layout_width="0dp"
			android:layout_height="0dp"
			app:layout_constraintTop_toTopOf="parent"
			app:layout_constraintBottom_toBottomOf="parent"
			app:layout_constraintStart_toStartOf="parent"
			app:layout_constraintEnd_toEndOf="parent"/>

		<android.support.design.widget.FloatingActionButton
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			app:fabSize="mini"
			android:src="@drawable/ic_my_location"
			android:layout_marginBottom="8dp"
			android:layout_marginEnd="8dp"
			app:layout_constraintBottom_toBottomOf="parent"
			app:layout_constraintEnd_toEndOf="parent"
			android:onClick="focusOnUserLocation"/>

	</android.support.constraint.ConstraintLayout>
        

UserLocation.java:

ثابت‌های جدیدی که در طول برنامه استفاده خواهد شد در این بخش تعریف شده‌است.

ثابت REQUEST_CODE برای دنبال کردن درخواست مجوز (permission) است و مقدار آن به دلخواه برابر با ۱۲۳ در نظر گرفته شده‌است.

ثابت UPDATE_INTERVAL_IN_MILLISECONDS فاصله دوره‌های زمانی جهت درخواست موقعیت جدید را مشخص می‌کند. بسته به نیاز خود می‌توانید مقدار این ثابت را کم یا زیاد کنید.

                // used to track request permissions
    final int REQUEST_CODE = 123;
    // location updates interval - 1 sec
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000;
        

موقعیت فعلی کاربر در متغیر userLocation ذخیره می‌شود.

برای به دست آوردن موقعیت مکانی، از یک شی از نوع FusedLocationProviderClient استفاده می‌شود که توضیحات مربوط به این کلاس را می‌توانید از اینجا بخوانید:

سایر متغیرهایی که برای دریافت موقعیت کاربر استفاده می‌شوند، متغیرهایی از کلاس‌های SettingsClient، LocationRequest، LocationSettingsRequest و LocationCallback هستند.

آخرین زمان به‌روزرسانی موقعیت فعلی کاربر در متغیر lastUpdateTime – که از نوع String است ذخیره می‌شود. و در آخر متغیر marker که نشانگر اضافه شده بر روی نقشه در آن نگهداری میشود تا در صورت نیاز امکان حذف یا ویرایش آن وجود داشته باشد.

            	// User's current location
	private Location userLocation;
	private FusedLocationProviderClient fusedLocationClient;
	private SettingsClient settingsClient;
	private LocationRequest locationRequest;
	private LocationSettingsRequest locationSettingsRequest;
	private LocationCallback locationCallback;
	private String lastUpdateTime;
	// boolean flag to toggle the ui
	private Boolean mRequestingLocationUpdates;
	private Marker marker;
        

متد initLayoutRefrences جهت مقداردهی اولیه کردن به تمامی المان‌های مربوط به رابط کاربری نوشته شده‌است. به دلیل این که لازم است تا المان اندرویدی نقشه نشان ابتدا به طور کامل ایجاد شود و سپس با آن تعامل برقرار شود (متدهای مختلف بر روی المان نقشه اجرا شود)، تمامی متدهای مربوط به رابط کاربری باید در متد onStart انجام شوند.

در متد onStart و پس از مقداردهی اولیه المان‌های مربوط به رابط کاربری، دو متد initLocation و startReceivingLocationUpdates صدا زده می‌شوند که در ادامه کد، نقش هر یک توضیح داده خواهد شد.

            	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// starting app in full screen
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		setContentView(R.layout.activity_user_location);
	}
	@Override
	protected void onStart() {
		super.onStart();
		// everything related to ui is initialized here
		initLayoutReferences();
		initLocation();
		startReceivingLocationUpdates();
	}
        

در متد onPause، متد stopLocationUpdates صدا زده می‌شود و دریافت موقعیت کاربر متوقف می‌شود.

                @Override
    protected void onPause() {
        super.onPause();
        stopLocationUpdates();
    }
        

در این متد پس از مقداردهی اولیه Viewها و نقشه نشان، موقعیت مکانی مقداردهی اولیه می ‌شود.

            	// Initializing layout references (views, map and map events)
	private void initLayoutReferences() {
		// Initializing views
		initViews();
		// Initializing mapView element
		initMap();
	}
        

برای مقداردهی اولیه کردن موقعیت مکانی، متد getFusedLocationProviderClient از کلاس LocationServices صدا زده میشود. متغیر settingsClient با استفاده از متد getSettingsClient از کلاس LocationServices مقداردهی می‌شود. کاربرد این متغیر در متدهای بعدی توضیح داده خواهد شد.

متغیر locationCallback با یک شی جدید ایجاد شده از کلاس LocationCallback مقداردهی می‌شود. متد onLocationResult این کلاس Override شده است و در این متد آخرین موقعیت کاربر در userLocation و زمان فعلی (به عنوان آخرین زمان دریافت موقعیت کاربر) در متغیر lastUpdateTime ذخیره می‌شود. در نهایت متد onLocationChange – که مجموعه اتفاقاتی است که با هر بار تغییر موقعیت کاربر باید انجام شود – صدا زده می‌شود.

در ادامه دوره زمانی دریافت مجدد موقعیت کاربر، حداقل زمان به‌روزرسانی و اولویت این کاربر با استفاده از صدا زدن متدهای مربوطه بر روی locationRequest تنظیم می‌شود و شی locationSettingsRequest از روی آن ساخته می‌شود.

            	private void initLocation() {
		fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
		settingsClient = LocationServices.getSettingsClient(this);

		locationCallback = new LocationCallback() {
			@Override
			public void onLocationResult(LocationResult locationResult) {
				super.onLocationResult(locationResult);
				// location is received
				userLocation = locationResult.getLastLocation();
				lastUpdateTime = DateFormat.getTimeInstance().format(new Date());

				onLocationChange();
			}
		};

		mRequestingLocationUpdates = false;

		if (locationRequest == null) {
			locationRequest = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, UPDATE_INTERVAL_IN_MILLISECONDS).build();
			LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
			builder.addLocationRequest(locationRequest);
			locationSettingsRequest = builder.build();
		}
	}
        

متد startReceivingLocationUpdates ابتدا چک میکند که ورژن اندروید دستگاه کاربر 6 و بالاتر از آن است یا خیر، در صورتی که بود، چک میکند که آیا دسترسی به 2 مجوز ACCESS_FINE_LOCATION و ACCESS_COARSE_LOCATION وجود دارد یا خیر، در صورتی که وجود داشت، متد startLocationUpdates صدا زده میشود. در غیر اینصورت درخواست دسترسی مجوز این 2 مجوز ارسال میشود.

            	public void startReceivingLocationUpdates() {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
			if (ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
				mRequestingLocationUpdates = true;
				startLocationUpdates();
			} else {
				requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE);
			}
		} else {
			mRequestingLocationUpdates = true;
			startLocationUpdates();
		}
	}
        

نتیجه درخواست مجوز در این متد بررسی شده و در صورتی که مجوز توسط کاربر داده شود، مقدار mRequestingLocationUpdates برابر با true می‌شود و متد startLocationUpdates صدا زده میشود شد.

            	@Override
	public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
		if (requestCode == REQUEST_CODE) {
			if (ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
				mRequestingLocationUpdates = true;
				startLocationUpdates();
			}
		}
	}
        

در متد startLocationUpdates بررسی می‌شود که اگر تنظیمات تعریف شده در locationSettingsRequest برقرار باشد، متد requestLocationUpdates بر روی fusedLocationClient صدا زده می‌شود و سپس متد onLocationChange صدا زده می‌شود. در غیر این صورت با بررسی کد خطا، رفتار مناسب انجام می‌شود (کدهای داخل متد onFailure و کامنت‌های نوشته شده را دنبال کنید)

            	/**
	 * Starting location updates
	 * Check whether location settings are satisfied and then
	 * location updates will be requested
	 */
	private void startLocationUpdates() {
		settingsClient
				.checkLocationSettings(locationSettingsRequest)
				.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
					@SuppressLint("MissingPermission")
					@Override
					public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
						Log.i(TAG, "All location settings are satisfied.");

						if (ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
							Log.d("UserLocationUpdater", " required permissions are not granted ");
							return;
						}

						fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
					}
				})
				.addOnFailureListener(this, new OnFailureListener() {
					@Override
					public void onFailure(@NonNull Exception e) {
						int statusCode = ((ApiException) e).getStatusCode();
						switch (statusCode) {
							case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
								try {
									Log.i(TAG, "Location settings are not satisfied. Attempting to upgrade location settings");
									// Show the dialog by calling startResolutionForResult(), and check the
									// result in onActivityResult().
									ResolvableApiException rae = (ResolvableApiException) e;
									rae.startResolutionForResult(UserLocation.this, REQUEST_CODE);
								} catch (IntentSender.SendIntentException sie) {
									Log.i(TAG, "PendingIntent unable to execute request.");
								}
								break;
							case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
								String errorMessage = "Location settings are inadequate, and cannot be fixed here. Fix in Settings.";
								Log.e(TAG, errorMessage);
								Toast.makeText(UserLocation.this, errorMessage, Toast.LENGTH_LONG).show();
						}
					}
				});
	}
        
در صورتی که GPS غیرفعال باشد، پنجره مربوط به فعالسازی GPS ظاهر میشود. در صورتی که درخواست فعالسازی تایید شود، متد onActivityResult صدا زده میشود و در این متد با توجه به نتیجه درخواست که RESULT_OK است، متد startLocationUpdates صدا زده میشود. در غیر اینصورت متغیر mRequestingLocationUpdates برابر مقدار false خواهد شد.
            	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		switch (requestCode) {
			// Check for the integer request code originally supplied to startResolutionForResult().
			case REQUEST_CODE:
				switch (resultCode) {
					case Activity.RESULT_OK:
						Log.e(TAG, "User agreed to make required location settings changes.");
						mRequestingLocationUpdates = true;
						startLocationUpdates();
						break;
					case Activity.RESULT_CANCELED:
						Log.e(TAG, "User choose not to make required location settings changes.");
						mRequestingLocationUpdates = false;
						break;
				}
				break;
		}
	}
        

در این متد، متد removeLocationUpdates بر روی fusedLocationClient صدا زده شده و به‌روزرسانی موقعیت کاربر متوقف می‌شود.

            	public void stopLocationUpdates() {
		// Removing location updates
		fusedLocationClient
				.removeLocationUpdates(locationCallback)
				.addOnCompleteListener(this, new OnCompleteListener<Void>() {
					@Override
					public void onComplete(@NonNull Task<Void> task) {
						Toast.makeText(getApplicationContext(), "Location updates stopped!", Toast.LENGTH_SHORT).show();
					}
				});
	}
        

در صورتی که این متد صدا زده شود و userLocation به درستی مقدار گرفته باشد، متد addUserMarker با موقعیت جدید کاربر صدا زده می‌شود. سپس دوربین روی موقعیت کاربر قرار میگیرد.

            	private void onLocationChange() {
		if (userLocation != null) {
			addUserMarker(new LatLng(userLocation.getLatitude(), userLocation.getLongitude()));
			map.moveCamera(new LatLng(userLocation.getLatitude(), userLocation.getLongitude()), .5f);
		}
	}
        

متد addUserMarker یک نشان‌گر در موقعیت فعلی کاربر نمایش می‌دهد. این متد دقیقا مشابه با متد addMarker که در اضافه کردن نشانگر توضیح داده‌شده است می‌باشد.

            	// This method gets a LngLat as input and adds a marker on that position
	private void addUserMarker(LatLng loc){
		//remove existing marker from map
		if(marker!=null){
			map.removeMarker(marker);
		}
		// Creating marker style. We should use an object of type MarkerStyleCreator, set all features on it
		// and then call buildStyle method on it. This method returns an object of type MarkerStyle
		MarkerStyleBuilder markStCr = new MarkerStyleBuilder();
		markStCr.setSize(30f);
		markStCr.setBitmap(BitmapUtils.createBitmapFromAndroidBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_marker)));
		MarkerStyle markSt = markStCr.buildStyle();

		// Creating user marker
		marker = new Marker(loc, markSt);

		// Adding user marker to map!
		map.addMarker(marker);
	}
        

با هر بار لمس دکمه موجود در رابط کاربری، در صورتی که متغیر userLocation مقداردهی شده باشد (موقعیت کاربر دریافت شده باشد) دوربین بر روی موقعیت کاربر متمرکز می‌شود. در غیر اینصورت متد startReceivingLocationUpdates صدا زده میشود.

            	public void focusOnUserLocation(View view) {
		if (userLocation != null) {
			map.moveCamera(new LatLng(userLocation.getLatitude(), userLocation.getLongitude()), 0.25f);
			map.setZoom(15, 0.25f);
		} else {
			startReceivingLocationUpdates();
		}
	}
        

نمایش محدوده تقریبی کاربر

زمانی که از موقعیت تقریبی کاربر (ACCESS_COARSE_LOCATION) برای نمایش موقعیت کاربر استفاده میکنید، همانطور که از نامش پیداست، موقعیتی که روی نقشه نمایش داده میشود تقریبی است. برای نمایش محدوده تقریبی کاربر میتوانید از متد زیر استفاده نمایید:

            
map.showAccuracyCircle(userLocation);

        

پارامتر ورودی این متد از نوع Location است که طبق توضیحات بالا باید متغیر userLocation به آن پاس داده شود. طبق مثال بالا این متد باید در متد onLocationChange صدا زده شود.

نمایش جهت کاربر

برای نمایش جهت ایستادن کاربر روی نقشه میتوانید از متد زیر استفاده نمایید:

            map.enableUserMarkerRotation(marker);
        

نکته: متد بالا باید پس از افزودن مارکر کاربر به نقشه صدا زده شود.

نسخه 1.0.2

هدف از این بخش از پروژه، نمایش یک نشانگر بر روی موقعیت فعلی کاربر و به‌روزرسانی مکان نمایشگر با تغییر موقعیت جغرافیایی کاربر است.

خطوط زیر را به وابستگی‌های فایل build.gradle (Module.app) اضافه کنید:

            	//Play Services
	implementation 'com.google.android.gms:play-services-gcm:17.0.0'
	implementation 'com.google.android.gms:play-services-location:21.0.1'
        

AndroidManifest.xml

دسترسی‌های زیر را برای به دست آوردن موقعیت جغرافیایی به این فایل اضافه کنید:

            	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        

activity_user_location.xml:

در این صفحه علاوه بر المان نقشه نشان، یک FloatingActionButton اضافه شده‌است که با هر بار کلیک بر روی آن، متد focusOnUserLocation صدا زده می‌شود. این متد در ادامه توضیح داده خواهد شد.

            	<?xml version="1.0" encoding="utf-8"?>
	<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
		xmlns:tools="http://schemas.android.com/tools"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		xmlns:app="http://schemas.android.com/apk/res-auto"
		tools:context=".UserLocation">

		<org.neshan.mapsdk.MapView
			android:id="@+id/map"
			android:layout_width="0dp"
			android:layout_height="0dp"
			app:layout_constraintTop_toTopOf="parent"
			app:layout_constraintBottom_toBottomOf="parent"
			app:layout_constraintStart_toStartOf="parent"
			app:layout_constraintEnd_toEndOf="parent"/>

		<android.support.design.widget.FloatingActionButton
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			app:fabSize="mini"
			android:src="@drawable/ic_my_location"
			android:layout_marginBottom="8dp"
			android:layout_marginEnd="8dp"
			app:layout_constraintBottom_toBottomOf="parent"
			app:layout_constraintEnd_toEndOf="parent"
			android:onClick="focusOnUserLocation"/>

	</android.support.constraint.ConstraintLayout>
        

UserLocation.java:

ثابت‌های جدیدی که در طول برنامه استفاده خواهد شد در این بخش تعریف شده‌است.

ثابت REQUEST_CODE برای دنبال کردن درخواست مجوز (permission) است و مقدار آن به دلخواه برابر با ۱۲۳ در نظر گرفته شده‌است.

ثابت UPDATE_INTERVAL_IN_MILLISECONDS فاصله دوره‌های زمانی جهت درخواست موقعیت جدید را مشخص می‌کند. بسته به نیاز خود می‌توانید مقدار این ثابت را کم یا زیاد کنید.

                // used to track request permissions
    final int REQUEST_CODE = 123;
    // location updates interval - 1 sec
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000;
        

موقعیت فعلی کاربر در متغیر userLocation ذخیره می‌شود.

برای به دست آوردن موقعیت مکانی، از یک شی از نوع FusedLocationProviderClient استفاده می‌شود که توضیحات مربوط به این کلاس را می‌توانید از اینجا بخوانید:

سایر متغیرهایی که برای دریافت موقعیت کاربر استفاده می‌شوند، متغیرهایی از کلاس‌های SettingsClient، LocationRequest، LocationSettingsRequest و LocationCallback هستند.

آخرین زمان به‌روزرسانی موقعیت فعلی کاربر در متغیر lastUpdateTime – که از نوع String است ذخیره می‌شود. و در آخر متغیر marker که نشانگر اضافه شده بر روی نقشه در آن نگهداری میشود تا در صورت نیاز امکان حذف یا ویرایش آن وجود داشته باشد.

            	// User's current location
	private Location userLocation;
	private FusedLocationProviderClient fusedLocationClient;
	private SettingsClient settingsClient;
	private LocationRequest locationRequest;
	private LocationSettingsRequest locationSettingsRequest;
	private LocationCallback locationCallback;
	private String lastUpdateTime;
	// boolean flag to toggle the ui
	private Boolean mRequestingLocationUpdates;
	private Marker marker;
        

متد initLayoutRefrences جهت مقداردهی اولیه کردن به تمامی المان‌های مربوط به رابط کاربری نوشته شده‌است. به دلیل این که لازم است تا المان اندرویدی نقشه نشان ابتدا به طور کامل ایجاد شود و سپس با آن تعامل برقرار شود (متدهای مختلف بر روی المان نقشه اجرا شود)، تمامی متدهای مربوط به رابط کاربری باید در متد onStart انجام شوند.

در متد onStart و پس از مقداردهی اولیه المان‌های مربوط به رابط کاربری، دو متد initLocation و startReceivingLocationUpdates صدا زده می‌شوند که در ادامه کد، نقش هر یک توضیح داده خواهد شد.

            	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// starting app in full screen
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		setContentView(R.layout.activity_user_location);
	}
	@Override
	protected void onStart() {
		super.onStart();
		// everything related to ui is initialized here
		initLayoutReferences();
		initLocation();
		startReceivingLocationUpdates();
	}
        

در متد onPause، متد stopLocationUpdates صدا زده می‌شود و دریافت موقعیت کاربر متوقف می‌شود.

                @Override
    protected void onPause() {
        super.onPause();
        stopLocationUpdates();
    }
        

در این متد پس از مقداردهی اولیه Viewها و نقشه نشان، موقعیت مکانی مقداردهی اولیه می ‌شود.

            	// Initializing layout references (views, map and map events)
	private void initLayoutReferences() {
		// Initializing views
		initViews();
		// Initializing mapView element
		initMap();
	}
        

برای مقداردهی اولیه کردن موقعیت مکانی، متد getFusedLocationProviderClient از کلاس LocationServices صدا زده میشود. متغیر settingsClient با استفاده از متد getSettingsClient از کلاس LocationServices مقداردهی می‌شود. کاربرد این متغیر در متدهای بعدی توضیح داده خواهد شد.

متغیر locationCallback با یک شی جدید ایجاد شده از کلاس LocationCallback مقداردهی می‌شود. متد onLocationResult این کلاس Override شده است و در این متد آخرین موقعیت کاربر در userLocation و زمان فعلی (به عنوان آخرین زمان دریافت موقعیت کاربر) در متغیر lastUpdateTime ذخیره می‌شود. در نهایت متد onLocationChange – که مجموعه اتفاقاتی است که با هر بار تغییر موقعیت کاربر باید انجام شود – صدا زده می‌شود.

در ادامه دوره زمانی دریافت مجدد موقعیت کاربر، حداقل زمان به‌روزرسانی و اولویت این کاربر با استفاده از صدا زدن متدهای مربوطه بر روی locationRequest تنظیم می‌شود و شی locationSettingsRequest از روی آن ساخته می‌شود.

            	private void initLocation() {
		fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
		settingsClient = LocationServices.getSettingsClient(this);

		locationCallback = new LocationCallback() {
			@Override
			public void onLocationResult(LocationResult locationResult) {
				super.onLocationResult(locationResult);
				// location is received
				userLocation = locationResult.getLastLocation();
				lastUpdateTime = DateFormat.getTimeInstance().format(new Date());

				onLocationChange();
			}
		};

		mRequestingLocationUpdates = false;

		if (locationRequest == null) {
			locationRequest = new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, UPDATE_INTERVAL_IN_MILLISECONDS).build();
			LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
			builder.addLocationRequest(locationRequest);
			locationSettingsRequest = builder.build();
		}
	}
        

متد startReceivingLocationUpdates ابتدا چک میکند که ورژن اندروید دستگاه کاربر 6 و بالاتر از آن است یا خیر، در صورتی که بود، چک میکند که آیا دسترسی به 2 مجوز ACCESS_FINE_LOCATION و ACCESS_COARSE_LOCATION وجود دارد یا خیر، در صورتی که وجود داشت، متد startLocationUpdates صدا زده میشود. در غیر اینصورت درخواست دسترسی مجوز این 2 مجوز ارسال میشود.

            	public void startReceivingLocationUpdates() {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
			if (ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
				mRequestingLocationUpdates = true;
				startLocationUpdates();
			} else {
				requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE);
			}
		} else {
			mRequestingLocationUpdates = true;
			startLocationUpdates();
		}
	}
        

نتیجه درخواست مجوز در این متد بررسی شده و در صورتی که مجوز توسط کاربر داده شود، مقدار mRequestingLocationUpdates برابر با true می‌شود و متد startLocationUpdates صدا زده میشود شد.

            	@Override
	public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
		if (requestCode == REQUEST_CODE) {
			if (ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
				mRequestingLocationUpdates = true;
				startLocationUpdates();
			}
		}
	}
        

در متد startLocationUpdates بررسی می‌شود که اگر تنظیمات تعریف شده در locationSettingsRequest برقرار باشد، متد requestLocationUpdates بر روی fusedLocationClient صدا زده می‌شود و سپس متد onLocationChange صدا زده می‌شود. در غیر این صورت با بررسی کد خطا، رفتار مناسب انجام می‌شود (کدهای داخل متد onFailure و کامنت‌های نوشته شده را دنبال کنید)

            	/**
	 * Starting location updates
	 * Check whether location settings are satisfied and then
	 * location updates will be requested
	 */
	private void startLocationUpdates() {
		settingsClient
				.checkLocationSettings(locationSettingsRequest)
				.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
					@SuppressLint("MissingPermission")
					@Override
					public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
						Log.i(TAG, "All location settings are satisfied.");

						if (ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(UserLocation.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
							Log.d("UserLocationUpdater", " required permissions are not granted ");
							return;
						}

						fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
					}
				})
				.addOnFailureListener(this, new OnFailureListener() {
					@Override
					public void onFailure(@NonNull Exception e) {
						int statusCode = ((ApiException) e).getStatusCode();
						switch (statusCode) {
							case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
								try {
									Log.i(TAG, "Location settings are not satisfied. Attempting to upgrade location settings");
									// Show the dialog by calling startResolutionForResult(), and check the
									// result in onActivityResult().
									ResolvableApiException rae = (ResolvableApiException) e;
									rae.startResolutionForResult(UserLocation.this, REQUEST_CODE);
								} catch (IntentSender.SendIntentException sie) {
									Log.i(TAG, "PendingIntent unable to execute request.");
								}
								break;
							case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
								String errorMessage = "Location settings are inadequate, and cannot be fixed here. Fix in Settings.";
								Log.e(TAG, errorMessage);
								Toast.makeText(UserLocation.this, errorMessage, Toast.LENGTH_LONG).show();
						}
					}
				});
	}
        
در صورتی که GPS غیرفعال باشد، پنجره مربوط به فعالسازی GPS ظاهر میشود. در صورتی که درخواست فعالسازی تایید شود، متد onActivityResult صدا زده میشود و در این متد با توجه به نتیجه درخواست که RESULT_OK است، متد startLocationUpdates صدا زده میشود. در غیر اینصورت متغیر mRequestingLocationUpdates برابر مقدار false خواهد شد.
            	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		switch (requestCode) {
			// Check for the integer request code originally supplied to startResolutionForResult().
			case REQUEST_CODE:
				switch (resultCode) {
					case Activity.RESULT_OK:
						Log.e(TAG, "User agreed to make required location settings changes.");
						mRequestingLocationUpdates = true;
						startLocationUpdates();
						break;
					case Activity.RESULT_CANCELED:
						Log.e(TAG, "User choose not to make required location settings changes.");
						mRequestingLocationUpdates = false;
						break;
				}
				break;
		}
	}
        

در این متد، متد removeLocationUpdates بر روی fusedLocationClient صدا زده شده و به‌روزرسانی موقعیت کاربر متوقف می‌شود.

            	public void stopLocationUpdates() {
		// Removing location updates
		fusedLocationClient
				.removeLocationUpdates(locationCallback)
				.addOnCompleteListener(this, new OnCompleteListener<Void>() {
					@Override
					public void onComplete(@NonNull Task<Void> task) {
						Toast.makeText(getApplicationContext(), "Location updates stopped!", Toast.LENGTH_SHORT).show();
					}
				});
	}
        

در صورتی که این متد صدا زده شود و userLocation به درستی مقدار گرفته باشد، متد addUserMarker با موقعیت جدید کاربر صدا زده می‌شود. سپس دوربین روی موقعیت کاربر قرار میگیرد.

            	private void onLocationChange() {
		if (userLocation != null) {
			addUserMarker(new LatLng(userLocation.getLatitude(), userLocation.getLongitude()));
			map.moveCamera(new LatLng(userLocation.getLatitude(), userLocation.getLongitude()), .5f);
		}
	}
        

متد addUserMarker یک نشان‌گر در موقعیت فعلی کاربر نمایش می‌دهد. این متد دقیقا مشابه با متد addMarker که در اضافه کردن نشانگر توضیح داده‌شده است می‌باشد.

            	// This method gets a LngLat as input and adds a marker on that position
	private void addUserMarker(LatLng loc){
		//remove existing marker from map
		if(marker!=null){
			map.removeMarker(marker);
		}
		// Creating marker style. We should use an object of type MarkerStyleCreator, set all features on it
		// and then call buildStyle method on it. This method returns an object of type MarkerStyle
		MarkerStyleBuilder markStCr = new MarkerStyleBuilder();
		markStCr.setSize(30f);
		markStCr.setBitmap(BitmapUtils.createBitmapFromAndroidBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_marker)));
		MarkerStyle markSt = markStCr.buildStyle();

		// Creating user marker
		marker = new Marker(loc, markSt);

		// Adding user marker to map!
		map.addMarker(marker);
	}
        

با هر بار لمس دکمه موجود در رابط کاربری، در صورتی که متغیر userLocation مقداردهی شده باشد (موقعیت کاربر دریافت شده باشد) دوربین بر روی موقعیت کاربر متمرکز می‌شود. در غیر اینصورت متد startReceivingLocationUpdates صدا زده میشود.

            	public void focusOnUserLocation(View view) {
		if (userLocation != null) {
			map.moveCamera(new LatLng(userLocation.getLatitude(), userLocation.getLongitude()), 0.25f);
			map.setZoom(15, 0.25f);
		} else {
			startReceivingLocationUpdates();
		}
	}
        

نسخه 1.0.1

هدف از این بخش از پروژه، نمایش یک نشانگر بر روی موقعیت فعلی کاربر و به‌روزرسانی مکان نمایشگر با تغییر موقعیت جغرافیایی کاربر است.

خطوط زیر را به وابستگی‌های فایل build.gradle (Module.app) اضافه کنید:

            //Play Services
    implementation 'com.google.android.gms:play-services-gcm:15.0.1'
    implementation 'com.google.android.gms:play-services-location:15.0.1'

    // dexter runtime permissions
    implementation 'com.karumi:dexter:4.2.0'
        

AndroidManifest.xml

دسترسی‌های زیر را برای به دست آوردن موقعیت جغرافیایی به این فایل اضافه کنید:

            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        

activity_user_location.xml:

در این صفحه علاوه بر المان نقشه نشان، یک FloatingActionButton اضافه شده‌است که با هر بار کلیک بر روی آن، متد focusOnUserLocation صدا زده می‌شود. این متد در ادامه توضیح داده خواهد شد.

            <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".UserLocation">

    <org.neshan.mapsdk.MapView
        android:id="@+id/map"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:fabSize="mini"
        android:src="@drawable/ic_my_location"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:onClick="focusOnUserLocation"/>

</android.support.constraint.ConstraintLayout>
        

UserLocation.java:

ثابت‌های جدیدی که در طول برنامه استفاده خواهد شد در این بخش تعریف شده‌است.

ثابت REQUEST_CODE برای دنبال کردن درخواست مجوز (permission) است و مقدار آن به دلخواه برابر با ۱۲۳ در نظر گرفته شده‌است.

ثابت UPDATE_INTERVAL_IN_MILLISECONDS فاصله دوره‌های زمانی جهت درخواست موقعیت جدید را مشخص می‌کند. بسته به نیاز خود می‌توانید مقدار این ثابت را کم یا زیاد کنید. همچنین ثابت FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS سریع‌ترین زمان برای دریافت موقعیت جدید کاربر را مشخص می‌کند. در صورتی که نرم‌افزارهای دیگر درخواست موقعیت کاربر را داشته باشند، در حداقل زمان‌های ۱۰۰۰ میلی‌ثانیه‌ای – در این‌جا – موقعیت جدید دریافت می‌شود.

                // used to track request permissions
    final int REQUEST_CODE = 123;
    // location updates interval - 1 sec
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000;
    // fastest updates interval - 1 sec
    // location updates will be received if another app is requesting the locations
    // than your app can handle
    private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 1000;
        

موقعیت فعلی کاربر در متغیر userLocation ذخیره می‌شود.

برای به دست آوردن موقعیت مکانی، از یک شی از نوع FusedLocationProviderClient استفاده می‌شود که توضیحات مربوط به این کلاس را می‌توانید از اینجا بخوانید:

سایر متغیرهایی که برای دریافت موقعیت کاربر استفاده می‌شوند، متغیرهایی از کلاس‌های SettingsClient، LocationRequest، LocationSettingsRequest و LocationCallback هستند.

آخرین زمان به‌روزرسانی موقعیت فعلی کاربر در متغیر lastUpdateTime – که از نوع String است ذخیره می‌شود. و در آخر متغیر marker که نشانه اضافه شده بر روی نقشه در آن نگهداری میشود تا در صورت نیاز امکان حذف یا ویرایش آن وجود داشته باشد.

            // User's current location
    private Location userLocation;
    private FusedLocationProviderClient fusedLocationClient;
    private SettingsClient settingsClient;
    private LocationRequest locationRequest;
    private LocationSettingsRequest locationSettingsRequest;
    private LocationCallback locationCallback;
    private String lastUpdateTime;
    // boolean flag to toggle the ui
    private Boolean mRequestingLocationUpdates;
    private Marker marker;
        

متد initLayoutRefrences جهت مقداردهی اولیه کردن به تمامی المان‌های مربوط به رابط کاربری نوشته شده‌است. به دلیل این که لازم است تا المان اندرویدی نقشه نشان ابتدا به طور کامل ایجاد شود و سپس با آن تعامل برقرار شود (متدهای مختلف بر روی المان نقشه اجرا شود)، تمامی متدهای مربوط به رابط کاربری باید در متد onStart انجام شوند.

در متد onStart و پس از مقداردهی اولیه المان‌های مربوط به رابط کاربری، دو متد initLocation و startReceivingLocationUpdates صدا زده می‌شوند که در ادامه کد، نقش هر یک توضیح داده خواهد شد.

            @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // starting app in full screen
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_user_location);
    }
@Override
    protected void onStart() {
        super.onStart();
        // everything related to ui is initialized here
        initLayoutReferences();
        initLocation();
        startReceivingLocationUpdates();
    }
        

در متد onResume متد startLocationUpdates صدا زده می شود و دریافت موقعیت مکانی کاربر در بازه‌های زمانی مشخص شده در ثابت UPDATE_INTERVAL_IN_MILLISECONDS شروع می‌شود.

در متد onPause، متد stopLocationUpdates صدا زده می‌شود و دریافت موقعیت کاربر متوقف می‌شود.

            @Override
    protected void onResume(){
        super.onResume();
        startLocationUpdates();
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopLocationUpdates();
    }
        

در این متد پس از مقداردهی اولیه Viewها و نقشه نشان، موقعیت مکانی مقداردهی اولیه می ‌شود.

             // Initializing layout references (views, map and map events)
    private void initLayoutReferences() {
        // Initializing views
        initViews();
        // Initializing mapView element
        initMap();
    }
        

برای مقداردهی اولیه کردن موقعیت مکانی، متد getFusedLocationProviderClient از کلاس LocationServices صدا زده میشود. متغیر settingsClient با استفاده از متد getSettingsClient از کلاس LocationServices مقداردهی می‌شود. کاربرد این متغیر در متدهای بعدی توضیح داده خواهد شد.

متغیر locationCallback با یک شی جدید ایجاد شده از کلاس LocationCallback مقداردهی می‌شود. متد onLocationResult این کلاس Override شده است و در این متد آخرین موقعیت کاربر در userLocation و زمان فعلی (به عنوان آخرین زمان دریافت موقعیت کاربر) در متغیر lastUpdateTime ذخیره می‌شود. در نهایت متد onLocationChange – که مجموعه اتفاقاتی است که با هر بار تغییر موقعیت کاربر باید انجام شود – صدا زده می‌شود.

در ادامه دوره زمانی دریافت مجدد موقعیت کاربر، حداقل زمان به‌روزرسانی و اولویت این کار بر با استفاده از صدا زدن متدهای مربوطه بر روی locationRequest تنظیم می‌شود و شی locationSettingsRequest از روی آن ساخته می‌شود.

            private void initLocation() {
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        settingsClient = LocationServices.getSettingsClient(this);

        locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
                // location is received
                userLocation = locationResult.getLastLocation();
                lastUpdateTime = DateFormat.getTimeInstance().format(new Date());

                onLocationChange();
            }
        };

        mRequestingLocationUpdates = false;

        locationRequest = new LocationRequest();
        locationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        locationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
        builder.addLocationRequest(locationRequest);
        locationSettingsRequest = builder.build();

    }
        

در این متد بررسی می‌شود که اگر تنظیمات تعریف شده در locationSettingsRequest برقرار باشد، متد requestLocationUpdates بر روی fusedLocationClient صدا زده می‌شود و سپس متد onLocationChange صدا زده می‌شود. در غیر این صورت با بررسی کد خطا، رفتار مناسب انجام می‌شود (کدهای داخل متد onFailure و کامنت‌های نوشته شده را دنبال کنید)

            /**
     * Starting location updates
     * Check whether location settings are satisfied and then
     * location updates will be requested
     */
    private void startLocationUpdates() {
        settingsClient
                .checkLocationSettings(locationSettingsRequest)
                .addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
                    @SuppressLint("MissingPermission")
                    @Override
                    public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
                        Log.i(TAG, "All location settings are satisfied.");

                        //noinspection MissingPermission
                        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());

                        onLocationChange();
                    }
                })
                .addOnFailureListener(this, new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        int statusCode = ((ApiException) e).getStatusCode();
                        switch (statusCode) {
                            case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                                Log.i(TAG, "Location settings are not satisfied. Attempting to upgrade " +
                                        "location settings ");
                                if (mRequestingLocationUpdates) {
                                    try {
                                        // Show the dialog by calling startResolutionForResult(), and check the
                                        // result in onActivityResult().
                                        ResolvableApiException rae = (ResolvableApiException) e;
                                        rae.startResolutionForResult(UserLocation.this, REQUEST_CODE);
                                    } catch (IntentSender.SendIntentException sie) {
                                        Log.i(TAG, "PendingIntent unable to execute request.");
                                    }
                                }
                                break;
                            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                                String errorMessage = "Location settings are inadequate, and cannot be " +
                                        "fixed here. Fix in Settings.";
                                Log.e(TAG, errorMessage);

                                Toast.makeText(UserLocation.this, errorMessage, Toast.LENGTH_LONG).show();
                        }

                        onLocationChange();
                    }
                });
    }
        

در این متد، متد removeLocationUpdates بر روی fusedLocationClient صدا زده شده و به‌روزرسانی موقعیت کاربر متوقف می‌شود.

            public void stopLocationUpdates() {
        // Removing location updates
        fusedLocationClient
                .removeLocationUpdates(locationCallback)
                .addOnCompleteListener(this, new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        Toast.makeText(getApplicationContext(), "Location updates stopped!", Toast.LENGTH_SHORT).show();
                    }
                });
    }
        

در این متد پس از دریافت دسترسی ACCESS_FINE_LOCATION در زمان اجرا، متد startLocationUpdates صدا زده می‌شود و در صورتی که دسترسی دریافت نشود، متد openSettings صدا زده می‌شود.

            public void startReceivingLocationUpdates() {
        // Requesting ACCESS_FINE_LOCATION using Dexter library
        Dexter.withActivity(this)
                .withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
                .withListener(new PermissionListener() {
                    @Override
                    public void onPermissionGranted(PermissionGrantedResponse response) {
                        mRequestingLocationUpdates = true;
                        startLocationUpdates();
                    }

                    @Override
                    public void onPermissionDenied(PermissionDeniedResponse response) {
                        if (response.isPermanentlyDenied()) {
                            // open device settings when the permission is
                            // denied permanently
                            openSettings();
                        }
                    }

                    @Override
                    public void onPermissionRationaleShouldBeShown(com.karumi.dexter.listener.PermissionRequest permission, PermissionToken token) {
                        token.continuePermissionRequest();
                    }

                }).check();
    }
        

نتیجه درخواست مجوز در این متد بررسی شده و در صورتی که مجوز توسط کاربر داده نشود، مقدار mRequestingLocationUpdates برابر با false می‌شود و درخواست به‌روزرسانی موقعیت کاربر انجام نخواهد شد.

            @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            // Check for the integer request code originally supplied to startResolutionForResult().
            case REQUEST_CODE:
                switch (resultCode) {
                    case Activity.RESULT_OK:
                        Log.e(TAG, "User agreed to make required location settings changes.");
                        // Nothing to do. startLocationupdates() gets called in onResume again.
                        break;
                    case Activity.RESULT_CANCELED:
                        Log.e(TAG, "User chose not to make required location settings changes.");
                        mRequestingLocationUpdates = false;
                        break;
                }
                break;
        }
    }
        

این متد جهت باز کردن پنجره دریافت مجوز در زمان اجرا نوشته شده است.

            private void openSettings() {
        Intent intent = new Intent();
        intent.setAction(
                Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package",
                BuildConfig.APPLICATION_ID, null);
        intent.setData(uri);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
        

در صورتی که این متد صدا زده شود و userLocation به درستی مقدار گرفته باشد، متد addUserMarker با موقعیت جدید کاربر صدا زده می‌شود.

            private void onLocationChange() {
        if(userLocation != null) {
            addUserMarker(new LatLng(userLocation.getLatitude(), userLocation.getLongitude()));
        }
    }
        

متد addUserMarker یک نشان‌گر در موقعیت فعلی کاربر نمایش می‌دهد. این متد دقیقا مشابه با متد addMarker که در اضافه کردن نشانگر توضیح داده‌شده است می‌باشد.

            // This method gets a LngLat as input and adds a marker on that position
    private void addUserMarker(LatLng loc){
        //remove existing marker from map
        if(marker!=null){
            map.removeMarker(marker);
        }
        // Creating marker style. We should use an object of type MarkerStyleCreator, set all features on it
        // and then call buildStyle method on it. This method returns an object of type MarkerStyle
        MarkerStyleBuilder markStCr = new MarkerStyleBuilder();
        markStCr.setSize(30f);
        markStCr.setBitmap(BitmapUtils.createBitmapFromAndroidBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_marker)));
        MarkerStyle markSt = markStCr.buildStyle();

        // Creating user marker
        marker = new Marker(loc, markSt);

        // Adding user marker to map!
        map.addMarker(marker);
    }
        

با هر بار لمس دکمه موجود در رابط کاربری، دوربین بر روی موقعیت کاربر متمرکز می‌شود.

            public void focusOnUserLocation(View view) {
        if(userLocation != null) {
            LatLng LatLng = new LatLng(userLocation.getLatitude(), userLocation.getLongitude());
            map.moveCamera(LatLng, 0);
            map.setZoom(15, 0.25f);
        }
    }