Java - Coding Infinite https://codinginfinite.com/java/ Your infinite Coding Solutions Fri, 11 Oct 2019 05:06:02 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.3 https://codinginfinite.com/wp-content/uploads/2018/07/CODING-INFINITE-FAVICON.png Java - Coding Infinite https://codinginfinite.com/java/ 32 32 Multi-Threading in Java – Client Server Application Example https://codinginfinite.com/java-multithreading-client-server-application/ https://codinginfinite.com/java-multithreading-client-server-application/#comments Wed, 01 May 2019 21:47:28 +0000 https://codinginfinite.com/?p=2555 Multithreading in java? Multithreading in java is a process of executing multiple threads simultaneously. A thread is a lightweight sub-process, the smallest unit of processing. Multiprocessing and multithreading, both are used to achieve multitasking. … Java Multithreading is mostly used in games, animation, etc Every modern operating system has the support for multi-threading–so does the...

The post Multi-Threading in Java – Client Server Application Example appeared first on Coding Infinite.

]]>
Multithreading in java?

Multithreading in java is a process of executing multiple threads simultaneously. A thread is a lightweight sub-process, the smallest unit of processing. Multiprocessing and multithreading, both are used to achieve multitasking. … Java Multithreading is mostly used in games, animation, etc

Every modern operating system has the support for multi-threading–so does the application built to run on that operating system. No, doubt multi-threading programming increase the performance and concurrency in our application. For certain applications, it is easier to code different procedures calls and conversation in a different thread than to manage them from the same thread.

Mostly in a client-server application, you definitely want the create the server application in a multi-threaded environment where thousands of clients connected to one server. Well if you think that we’re going to build a client-server application than you’re right. Wait! don’t just close the tab because this time I have got a real-world problem.

Multithreading Meme

Application Overview

First, a little intro to my application so that we’re all on the same page. The application is about to build a smart meter (GPRS Meter) data collector. The application simply opens up a ServerSocket and after that, it accepts clients (Smart Meter) waiting for the connection. There are thousands of smart meters trying to connect to that application. So, when the smart meter connects with the application via socket, the application creates its own separate thread for communication.

Promgramming thread meme

Get Started

The application that we’re going to build will be in Java programming language. So, the first step was simple, I try to open up the ServerSocket holding the Pepsi can in my hand.

public class DeviceListenerRunnable implements Runnable {

    private final int portNumber;  // 1
    private final ServerSocket serverSocket;  
    private final IConnectionEventArgs connectionEventArgs; // 2

    public DeviceListenerRunnable(int portNumber, IConnectionEventArgs connectionEventArgs) {
        this.portNumber = portNumber;
        serverSocket = new ServerSocket(portNumber);  // 3
        this.connectionEventArgs = connectionEventArgs; 
    }

    @Override
    public void run() {
        Socket socket;
        while (!Thread.currentThread().isInterrupted()) {
            try {
                socket = serverSocket.accept();   // 4
                connectionEventArgs.onDeviceConnected(socket, socket.getRemoteSocketAddress().toString());  // 5
                if (!socket.isClosed())
                    new ReceiverThread(socket, connectionEventArgs).start();  // 6
            } catch (Exception ignored) {
            }
        }
    }
}

Here’s what’s going on inside the code:

  1. Port number for ServerSocket.
  2. An interface which notifies the caller that the new smart meter has connected. We’ll see the implementation of IConnectionEventArgs in a couple of minutes.
  3. Creates the ServerSocket with the specified port number.
  4. Waiting for a new client (Smart Meter) to become online.
  5. Sending the Socket and info of connected smart meter back to the caller class.
  6. Creating a new separate thread for the connected smart meter.

Examining the ReceiverThread class

The ReceiverThread class basically processing all the incoming data from the connected smart meter and send data back to caller class via IConnectionEventArgs interface. Every smart meter connected to an application has its own instance of this class. So, if there are a thousand devices connected to the server than it means every device has it’s own instance of ReceiverThread class because every device communicates in a separate thread. Let’s see the implementation of this class.

public class ReceiverThread extends Thread {
    
    private final Closeable closeable;
    private final IConnectionEventArgs connectionEventArgs;  

    public ReceiverThread(@Nonnull Closeable closeable, IConnectionEventArgs connectionEventArgs) {
        this.closeable = closeable;
        this.connectionEventArgs = connectionEventArgs;
    }

    @Override
    public void run() {
        if (closeable instanceof Socket) {
            Socket socket = (Socket) closeable;
            while (!isInterrupted() && !socket.isClosed()) { // 1
                String info = socket.getRemoteSocketAddress().toString();
                try {
                    byte[] data = handleTcp(socket);  // 2
                    connectionEventArgs.onDataReceived(info, socket, data)  // 3
                } catch (IOException e) {
                    connectionEventArgs.onDeviceDisconnected(info, e);  // 4
                    try {
                        interrupt();   // 5
                        socket.close();   // 6
                    } catch (IOException ignored) {
                    }
                    break;
                }
            }
        }
    }

    private byte[] handleTcp(Socket socket) throws IOException {
        // Logic for reading the input stream 
    }
}

Let’s take a brief look at different steps:

  1. Nothing out of ordinary happens simply start a while loop until the current thread is not interrupted or socket is closed.
  2. Handling the incoming TCP data from a smart meter. (If someone want to see the logic behind handleTcp data method just ask in the comment section)
  3. Sending the incoming data back to caller class. (We’ll see the caller class in a couple of minutes)
  4. If IOException occurs, notify the caller class that the smart meter has been disconnected.
  5. Needs to call the interrupt method on the current thread.
  6. Close socket for the smart meter.

Before going any further I think you guys need to see the legendary IConnectionEventArgs interface implementation.

interface IConnectionEventArgs {

     void onDeviceConnected(Socket socket, String info);

     void onDeviceDisconnected(String info, IOException e);

     void onDataReceived(String info, Closeable c, byte[] data); 
}

Implementation of caller class

We got the basics down now we just need to implement the IConnectionEventArgs interface and start the thread for ServerSocket class. So, the DeviceListenerRunnable class listens to new incoming connections from smart meters.

public class ParentMediaHelper implements IConnectionEventArgs {

    private final Thread listenerThread;
    private final DeviceCollection deviceCollection = DeviceCollection.instance();

    public ParentMediaHelper(int portNumber) {
        DeviceListenerRunnable meterListenerRunnable = new DeviceListenerRunnable(portNumber, this); 
        listenerThread = new Thread(meterListenerRunnable);
    }

    public void startListener() { 
        listenerThread.start();  
    }

    @Override
    public void onDeviceConnected(Socket socket, String info) {
        // handle new smart meter connection 
    }

    @Override
    public void onDeviceDisconnected(String info, IOException e) {
        deviceCollection.remove(info) 
    }

    @Override
    public void onDataReceived(String info, Closeable c, byte[] data) {
         if(containsSerialNumber(data)){ 
              String serialNumber = extractSerialNumber(data)
              deviceCollection.insert(Device(info, c, serialNumber))
         }
         // handle other things
    }
}

I know, many of you think that there are thousands of smart meter connected to the server. How can I determine that the data came from which smart meter? Actually when the first time smart meter sends the data (current or voltage) after a successful socket connection. It also sends its serial number (unique identifier) and with that serial number, I create a new Device model and insert the model in my DeviceCollection class. The DeviceCollection simply a singleton class performs the CRUD functionality.

Similarly when the onDeviceDisconnected calls we remove the device from our collection.

Running the Application Server

Nice! all the hard work is done. Now I just need the main function which simply calls the startListener method inside the ParentMediaHelper class.

public class ddssd {

    private static final int PORT_NUMBER = 14001; // Always use port above 1024

    public static void main(String[] args) {
        ParentMediaHelper mediaHelper = new ParentMediaHelper(PORT_NUMBER);
        mediaHelper.startListener();
    }
}

After running the above program, my application starts accepting connection from the smart meter.

Note: If you’re running application on Linux based OS you need to open the specified port.

Alright, guys, this was my story of creating a Java-based server application. If you like this article please share it with the community.

What’s Next

Implementation of above example with OpenJDK Project Loom, a light-weight concurrency framework.

Thank you for being here and keep reading…

The post Multi-Threading in Java – Client Server Application Example appeared first on Coding Infinite.

]]>
https://codinginfinite.com/java-multithreading-client-server-application/feed/ 2
Android Crop Image Using UCrop Library https://codinginfinite.com/android-image-ucrop-camera-gallery/ Wed, 06 Feb 2019 13:59:26 +0000 https://codinginfinite.com/?p=1707 Almost in every Android Application, we ask users to add profile picture when signing up and after that, we simply upload the avatar to our backend. But, before uploading that avatar, sometimes, we need to crop the selected image. So, today I would like to talk about how we can select an image from Gallery...

The post Android Crop Image Using UCrop Library appeared first on Coding Infinite.

]]>
Almost in every Android Application, we ask users to add profile picture when signing up and after that, we simply upload the avatar to our backend. But, before uploading that avatar, sometimes, we need to crop the selected image.

So, today I would like to talk about how we can select an image from Gallery or take Picture from the camera and crop that avatar. The application that we’re going to build in this article will work on all versions of Android from Honeycomb (API 11) to Pie (API 28).

For cropping the image we’re going to use UCrop libraryUCrop library aims to provide an ultimate and flexible image cropping experience. You can read more about UCrop library in this article.

Before start coding, I want to show you guys the demo of our application.

Get Started

In Android Studio go to Create Android Project, press next twice and select Empty Activity, then finish. After the Android Studio gradle builds successfully you’re ready to do the code. You can see MainActivity like below.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Include Dependencies

We’re gonna need to add the UCrop dependency in our app-level build.gradle file. Add the below dependency in the build.gradle file.

implementation 'com.github.yalantis:ucrop:2.2.2'

Now add the maven dependency in a top-level build.gradle file.

repositories {
       google()
       jcenter()
       maven { url "https://jitpack.io" }
   }

Once you’ve done these dependencies build your android project and add the following permissions in the Android Manifest file.

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

You may be curious why the hack I add the Storage permission in order to pick an image from Gallery or take a picture from Camera. Actually, when we select the image from the gallery or take pictures from the camera we need to store an image inside the device memory because of the UCrop library take the Uri instance in order to crop the image. And it would be good because we need to store the cropped image somewhere inside the memory and after that show the image inside the application.

Actually, there’s another thing which we need to discuss before start making our application. As I said at the start of this article, that our application will work on all existing Android devices. So, we need to handle the FileUriExposedException and for that, we need to implement FileProvider in our application. You can read more about FileUriExposedException in this link.

In order to implement FileProvider in your application. First, you need to add a FileProvider <provider/> tag in a AndroidManifest.xml file under the <application/> tag.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

The ${applicationId} automatically gets the package name of your Android application and concat the .provider with it.

The @xml/provider_paths we need to add it separately in the resource directory. First, you need to create an xml named directory inside the res folder and then create a file named provider_paths inside the previously created directory. Now paste the following code inside the provider_paths file.

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>

Done! FileProvider is now declared and be ready to use.

1. Open Camera And Take Picture

First of all, we need to query for all the activities in the device which will handle the  CAPTURE_REQUEST intent. Now let’s open the camera when the user clicks on openCamera dialog action.

Note: I’m not gonna ask for camera or storage permission in this article but you can get the complete code of the above demo application from the Github.

private void openCamera() {
      Intent pictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      File file = getImageFile(); // 1
      Uri uri;
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) // 2
           uri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID.concat(".provider"), file);
      else
           uri = Uri.fromFile(file); // 3
      pictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); // 4
      startActivityForResult(pictureIntent, CAMERA_ACTION_PICK_REQUEST_CODE); 
}

Here what’s going on in the openCamera method.

  1. Creating an image File with the getImageFile method. This method simply creates a new File in the external storage directory. We’ll see the implementation of the method in a couple of minutes.
  2.  If the SDK version of the device is API level 23+ then we simply create the Uri with the help of FileProvider in order to prevent the FileUriExposedException. The BuildConfig.APPLICATION_ID simply give us the package name of our application and concat the .provider with it as we did it in the AndroidManifest.xml file.
  3. Simply creates the Uri fromFile utility method if the SDK version is less than 24 API level.
  4. Sets the path of where you want to store the selected image so, that we can read the image in the onActivityResult method from the uri path. We’ll see how to do that.

Now let’s create the getImageFile method inside the Activity class.

String currentPhotoPath = ""; 
private File getImageFile() {
     String imageFileName = "JPEG_" + System.currentTimeMillis() + "_";
     File storageDir = new File(
         Environment.getExternalStoragePublicDirectory(
             Environment.DIRECTORY_DCIM
         ), "Camera"
     );
     File file = File.createTempFile(
         imageFileName, ".jpg", storageDir  
     );
     currentPhotoPath = "file:" + file.getAbsolutePath();
     return file;
 }

This method simply creates a random File name with .jpg extension inside the external storage directory. Also, we’re storing the file path inside the currentPhotoPath variable so that we can read the image from specified photo path.

After taking the picture from Camera our image will be stored inside the data extra’s which we pass when creating the Intent for Camera. Now let’s retrieve our image in onActivityResult method and crop it.

Crop the Taken Picture

First, you need to override the onActivityResult method inside your Activity class and add the following code.

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if(requestCode == CAMERA_ACTION_PICK_REQUEST_CODE && resultCode == RESULT_OK) {
          Uri uri = Uri.parse(currentPhotoPath);
          openCropActivity(uri, uri);
    }
}

If you observe the code carefully you may have noticed that we’re not using the data.getData() method. It always gives us null because we’re providing a file uri, so load with the currentPhotoPath and call the openCropActivity method.

Note: Our newly taken image will be replaced from the cropped image because I pass the same Uri’s as the sourceUri and destinationUri. You can change this logic by passing a different Uri for a destination.

Add the below openCropActivity method inside your Activity class.

private void openCropActivity(Uri sourceUri, Uri destinationUri) {
        UCrop.of(sourceUri, destinationUri)
        .withMaxResultSize(maxWidth, maxHeight)
        .withAspectRatio(5f, 5f)
        .start(context);
}

The UCrop configuration is created using the builder pattern. The UCrop.of method takes the first parameter as the sourceUri where the image file actually stored and the second parameter as the destinationUri where you want to store the cropped image.

After calling the openCropActivity method the UCrop simply opens up the image cropping activity with the startActivityForResult method and send the cropped image result in the onActivityResult method. Add the updated code inside the onActivityResult method.

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if(requestCode == CAMERA_ACTION_PICK_REQUEST_CODE && resultCode == RESULT_OK) {
          Uri uri = Uri.parse(currentPhotoPath);
          openCropActivity(uri, uri);
    } else if (requestCode == UCrop.REQUEST_CROP && resultCode == RESULT_OK) {
          Uri uri = UCrop.getOutput(data);
          showImage(uri);
    }
}

In the updated code we simply get our cropped image Uri and call the showImage method.

Next, add the following method inside your Activity class.

private void showImage(Uri imageUri) {
    File file = FileUtils.getFile(context, imageUri);
    InputStream inputStream = new FileInputStream(file);
    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
    imageView.setImageBitmap(bitmap);
}

Here we’ve completed our first part of an article where we take the picture from a camera and show the cropped image inside the ImageView.

2. Select Image From Gallery

In order to allow the user to select an image, we need to create an Intent which opens up the Documents app and only shows the images. Now let’s open the Documents app when the user clicks on selectImage dialog action.

Let’s create a new method to open the images.

private void openImagesDocument() {
    Intent pictureIntent = new Intent(Intent.ACTION_GET_CONTENT);
    pictureIntent.setType("image/*");  // 1 
    pictureIntent.addCategory(Intent.CATEGORY_OPENABLE);  // 2
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
         String[] mimeTypes = new String[]{"image/jpeg", "image/png"};  // 3
         pictureIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
    }
    startActivityForResult(Intent.createChooser(pictureIntent,"Select Picture"), PICK_IMAGE_GALLERY_REQUEST_CODE);  // 4
}

The following shows the explanation of the above code.

  1. By setting the type we specify we only need images no videos or anything else.
  2. The CATEGORY_OPENABLE used to indicate that Intent only wants URI’s that can be opened. You can read more category openable here in this link.
  3. The mimeTypes specify that we are only interested in jpeg or png type images.
  4. The chooser only shows if there are multiple options available else we simply open up the Documents app.

Crop The Selected Picture

Once you’ve selected the image the onActivityResult method will get hit and we only need to update that method. Add the update method code inside the onActivityResult method.

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if(requestCode == CAMERA_ACTION_PICK_REQUEST_CODE && resultCode == RESULT_OK) {
          Uri uri = Uri.parse(currentPhotoPath);
          openCropActivity(uri, uri);
    } else if (requestCode == UCrop.REQUEST_CROP && resultCode == RESULT_OK) {
          Uri uri = UCrop.getOutput(data);
          showImage(uri);
    } else if (requestCode == PICK_IMAGE_GALLERY_REQUEST_CODE && resultCode == RESULT_OK && data != null) {
          Uri sourceUri = data.getData(); // 1 
          File file = getImageFile(); // 2
          Uri destinationUri = Uri.fromFile(file);  // 3
          openCropActivity(sourceUri, destinationUri);  // 4 
    }
}

The below number points tell you what’s happening inside the updated onActivityResult method.

  1. Get the selected image Uri from data.
  2. Create the image File where you want to store the cropped image result.
  3. Simply creates the destinationUri fromFile utility method.
  4. Calls the openCropActivity method. Here we’re passing different Uri’s because our sourceUri is different where our selected image is stored and destinationUri where we want to save our cropped image result.

Now if you run the application you’ll see that all the application functionality will work perfectly. I hope this article will help you to crop the profile picture image. If you’ve any queries regarding this post please do comment below.

You can get the complete source code of above app from GitHub.

Thank you for being here and keep reading…

The post Android Crop Image Using UCrop Library appeared first on Coding Infinite.

]]>
Android Cab Booking App Tutorial Part 3 https://codinginfinite.com/android-cab-booking-app-tutorial-custom-google-map-marker-animation/ Sun, 30 Dec 2018 09:57:09 +0000 https://codinginfinite.com/?p=2089 This story is the third part of our Android Cab Booking App tutorial If you didn’t read the previous ones you can start here. Previously In Android Cab Booking App In the previous article, we discussed how to read the online-drivers from Firebase Realtime Database, create the data structure for Drivers and animate multiples markers on Google Maps....

The post Android Cab Booking App Tutorial Part 3 appeared first on Coding Infinite.

]]>
This story is the third part of our Android Cab Booking App tutorial If you didn’t read the previous ones you can start here.

Previously In Android Cab Booking App

In the previous article, we discussed how to read the online-drivers from Firebase Realtime Database, create the data structure for Drivers and animate multiples markers on Google Maps.

Also, I update the apps on GitHub. You can follow me by looking at the code. Java application link and Kotlin application link.

9. Show progress animation inside PinView

If you guys see the demo in the first article, you’ll understand that as soon as the Google Maps camera movement start. The ProgressBar inside the PinView start animating until the nearest Driver found and after that, we’ll calculate the distance with DistanceMatrixAPI between the nearest Driver location and the PinView location. Later the distance calculation we hide the ProgressBar and show the distance duration inside the PinView.

So, in order to animate the ProgressBar, we need to implement OnCameraMoveStartedListener on MainActivity. The OnCameraMoveStartedListener has its callback function which defines what action needs to be done when the camera starts its movement. In this callback, we need to start the ProgressBar animation.

Let’s start the ninth part of Cab Booking Application by implementing the OnCameraMoveStartedListener on MainActivity.

class MainActivity : AppCompatActivity(), GoogleMap.OnCameraIdleListener, GoogleMap.OnCameraMoveStartedListener {
      private var googleMap : GoogleMap? = null
      override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            .......
            .......
            val supportMapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment supportMapFragment.getMapAsync { googleMap ->
                    googleMap.setOnCameraIdleListener(this)
                    googleMap.setOnCameraMoveStartedListener(this)  // implements the OnCameraStartedListener                        
                    this.googleMap = googleMap 
            }
     }

The OnCameraMoveStartedListener has one abstract method and we need to implement it as well. Below is the onCameraMoveStarted overridden method.

override fun onCameraMoveStarted(p: Int) {
     pinTimeTextView.visibility = GONE
     pinProgressLoader.visibility = VISIBLE
}

In the onCameraMoveStarted method, we show the ProgressBar and hide the previously shown TextView. Now if you run the application, you’ll see that when the app opens the ProgressBar start animating inside the PinView.Google Maps PinView Animation

10. Get the nearest driver and calculate the Distance

In order to get the nearest Driver, we need to know the PinView location exactly where the user stops while dragging it. If you guy’s remembered from the previous article, we exactly face the same problem when we need to Reverse Geocode a location where user drags the Pin. So, for this, we need to update our onCameraIdle method inside the MainActivity.

Update the onCameraIdle method inside the MainActivity class.

override fun onCameraIdle() {
    val position = googleMap.cameraPosition.target
    viewModel.makeReverseGeocodeRequest(position, geoCoderValue.value)
    viewModel.onCameraIdle(latLng);
}

Next, add the below method inside the MainActivityViewModel class.

fun onCameraIdle(latLng: LatLng) {
        launch(coroutineContext + Dispatchers.Default) {
            if (driverRepo.allItems().isNotEmpty()) {
                val driver = driverRepo.getNearestDriver(latLng.latitude, latLng.longitude)
                driver?.let { calculateDistance(latLng, it) }
            }
        }
    }

At first, we launch a coroutine because the getNearestDriver method is a suspended method. After that, we check if the List<Driver> that we’re keeping inside the DriverCollection is not empty. Later we call the getNearestDriver method inside DriverCollection and call the calculateDistance method if the nearest Driver is not null.

You can get the DriverCollection class from GitHub.

Add the below calculateDistance method inside the MainActivityViewModel class.

private fun calculateDistance(latLng: LatLng, driver: Driver) {
    launch(coroutineContext + Dispatchers.IO) {  // 1
        val destination = arrayOf(driver.lat.toString() + "," + driver.lng.toString())
        val origins = arrayOf(latLng.latitude.toString() + "," + latLng.longitude.toString())
        DistanceMatrixApi.getDistanceMatrix(googleMapHelper.geoContextDistanceApi(), origins, destination)  // 2
            .mode(TravelMode.DRIVING)
            .setCallback(object : PendingResult.Callback<DistanceMatrix> {

                override fun onFailure(e: Throwable?) {
                }

                override fun onResult(result: DistanceMatrix?) {
                    if (result != null)
                        _calculateDistance.postValue(result.rows[0].elements[0].duration.humanReadable)  // 3
                }
            })
    }
}

Let’s go through the logic behind the above code:

  1. launch a coroutine in IO dispatcher, because it is a good approach to execute all network requests inside the IO dispatcher.
  2. The DistanceMatrixApi class is from the dependency which we add in the first article. You can read more about how to use the library here on GitHub.
  3. If the DistanceMatrix result is not null then we pass the result to MainActivity via LiveData to show the distance duration inside the PinView.

Note: The distance we’re calculating is from Driver location to PinView location. That’s how the Careem application shows the calculated distance inside PinView.

Next, add the geoContextDistanceApi method inside the GoogleMapHelper class.

class GoogleMapHelper(private val resources : Resources) {

   companion object {
       private val geoApiContextBuilder = GeoApiContext.Builder()
   }

   .......
   .......

   private fun distanceApi(): String {
        return resources.getString(R.string.google_distance_matrix_api_key)  // replace with your own distance matrix api key.
   }

   fun geoContextDistanceApi(): GeoApiContext {
        return geoApiContextBuilder
            .apiKey(distanceApi())
            .build()
   }
}

Now that we’ve added the utility method inside the GoogleMapHelper class. We need to declare _calculateDistance inside MainActivityViewModel class in order to observe from MainActivity.

private val _calculateDistance = MediatorLiveData<String>()

val calculateDistance : LiveData<String> = _calculateDistance

Next, add the following code inside the MainActivity onCreate method to observe calculateDistance instance from MainActivityViewModel class.

// 1
viewModel.calculateDistance
            .observe(this, Observer { distance ->
                   pinTimeTextView.text = distance
                   pinTimeTextView.visibility = VISIBLE
                   pinProgressLoader.visibility = GONE
            })

When the Observer interface invoked we simply set the distance to pinTimeTextView and hide the currently showing pinProgressLoader.

So, here we’ve completed the tenth part of the Frisbee application. Now when you run the application you’ll see the nearest Driver duration inside the PinView and also the duration updates when the Google Maps camera movement stops. Build and run to view the application progress. Here is mine:

Google Map Custom PinView With Text

Bravo! this concludes that our Frisbee application is complete. Hopefully, the next article will be on Driver application and it’ll be the last article on Android cab booking app tutorial. Stay tuned!

Thank you for being here and keep reading…

Previous Part

The post Android Cab Booking App Tutorial Part 3 appeared first on Coding Infinite.

]]>
Build authentication into your Java API’s with Json Web Token (Jwt) https://codinginfinite.com/authentication-java-apis-json-web-token-jwt/ https://codinginfinite.com/authentication-java-apis-json-web-token-jwt/#comments Wed, 19 Dec 2018 15:28:43 +0000 https://codinginfinite.com/?p=1984 In this article, I walk you through the development of a very basic Java JAX_RS web-services with Jwt (Json web token) authentication. For web-services, we’re going to use Jersey which is an open source framework for RESTful Web Services in Java. Before jump into coding and start authenticating the user I want to tell you a brief workflow of our...

The post Build authentication into your Java API’s with Json Web Token (Jwt) appeared first on Coding Infinite.

]]>
In this article, I walk you through the development of a very basic Java JAX_RS web-services with Jwt (Json web token) authentication. For web-services, we’re going to use Jersey which is an open source framework for RESTful Web Services in Java. Before jump into coding and start authenticating the user I want to tell you a brief workflow of our application.

Application Workflow

  • In order to use the web-services, the user sends a request to get a token by passing his/her credentials.
  • Our web-service validates the credentials, and return an encrypted privateKey aka token, which will only be valid for the next thirty minutes.
  • With every request, the user needs to send the privateKey in the request header, and if the key is invalid or expires then we send back the WebApplicationException, else we simply return the result.

We’ll implement a custom ContainerRequestFilter, which is in the simple words, a gateway of our application. Every request made by the client first came to our custom filter and we validate the privateKey before allowing the request to go to any service.

Like I said at the start of this article, we’ll create a privateKey aka token with Jwt (Json web token). In order to create a web-based token, we’ll use the Java Jwt library made by mighty lhazlewood.

There’s a great article on Json Web Token go and check it out => 5 Easy Septs to understand Json Web Token (Jwts)

Application Workflow Diagram

As mention above, in the Application Workflow section our server or web-services will generate a privateKey or Jwt token for the user. With the Jwt token, the user then safely communicate with the application. Below is the workflow diagram.

Jwt workflow diagram

This is indeed a simplistic introduction to our application.  Next, below is the demo of our application for visualization.

In case I miss something, you can follow me by looking in the code which I pushed on GitHub.

Get Started

To begin, create a new Maven web project using an IDE and open the pom.xml file. Here we need to add dependencies for Jersey and Jwt.

<dependencies>
        ...........
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
            <version>1.19.4</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-grizzly2</artifactId>
            <version>1.19.4</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>1.19.4</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.ext</groupId>
            <artifactId>jersey-entity-filtering</artifactId>
            <version>2.14</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.10.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.10.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.10.5</version>
            <scope>runtime</scope>
        </dependency>
        .........
    </dependencies>

Once you’ve added the above dependencies compile the Maven project and you’re good to go.

In order to start building our application first let’s see our EndPoint class. Below is our HomeApiService, which is an EndPoint of our application.

@Path("/")
public class HomeApiService {

    @POST
    @Path(value = "authorization_service")
    @JwtCustomToken
    @Produces(MediaType.APPLICATION_JSON)
    public Response authorizationService(@QueryParam("username") String userName, @QueryParam("password") String password) {
        if (userName.isEmpty())
            return getResponse(new BaseResponse(BaseResponse.FAILURE, "username field cannot be empty!"));
        if (password.isEmpty())
            return getResponse(new BaseResponse(BaseResponse.FAILURE, "password field cannot be empty!"));
        String privateKey = JwTokenHelper.getInstance().generatePrivateKey(userName, password);
        return getResponse(new AuthorizationResponse(BaseResponse.SUCCESS, "You're authenticated successfully. Private key will be valid for 30 mins", privateKey));
    }
    
    @GET
    @Path("allDevices")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getAllDevices() {
         return getResponse(new DeviceCollection(Arrays.asList(new Device("Electric Kettle", 1, true), new Device("Computer", 1, true), new Device("Motorcycle", 3, false), new Device("Sandwich Maker", 4, true))));
    }

As you can see the basic JAX-RX Endpoint has two methods both returning a Response object. Now if you guy’s have seen the demo, then you probably see that in order to get allDevices we first need to authenticate our user and pass the privateKey inside the header of the request. Below is the explanation of the above two methods:

  • authorizationService: The user needs to get authenticated, invoking an HTTP POST and passing a username and password . Once authenticated, we simply call our jwTokenHelper class to create a token based on the username and password also set some other claims like thirty minutes expiration. You’ll see that how to create Jwt token in a couple of minutes.
  • getAllDevices: Simply returns a List of devices.

Filter checking for json web Token (Jwt)

So, we’ve added the EndPoint to our application but where did we check that the user adds the privateKey inside the header of the request. For this, we need to implement  ConatinerRequestFilter and after that the filter allows us to check the headers for every request. Like I said at the above of this article, the filter is like a gateway to our application. Every request sent by the client first came to our custom filter and after that, it goes to the EndPoint address. Below is JsTokenFilterNeeded class.

@Provider   
@JwtCustomToken   // 1
@Priority(Priorities.AUTHENTICATION)  // 2
public class JsTokenFilterNeeded implements ContainerRequestFilter {
   
   private static final String AUTHORIZATION_SERVICE_PATH = "authorization_service";  
   private static final String PRIVATE_KEY = "privateKey";   
   private JwTokenHelper jwTokenHelper = JwTokenHelper.getInstance();

   @Override
    public ContainerRequest filter(ContainerRequest request) {  // 3
        String path = request.getPath();                     
        if(path.equals(AUTHORIZATION_SERVICE_PATH))        // |       4
             return request;
        String privateKeyHeaderValue = request.getHeaderValue(PRIVATE_KEY);  // 5
        if (privateKeyHeaderValue == null || privateKeyHeaderValue.isEmpty())
            throw new WebApplicationException(getUnAuthorizeResponse(PRIVATE_KEY + " is missing in header"))   // 6
        try {
             provider.jwTokenHelper.claimKey(privateKeyHeaderValue);   // 7
        } catch(Exception e) {
            if (e instanceof ExpiredJwtException) {
                throw new WebApplicationException(getUnAuthorizeResponse(PRIVATE_KEY + " is expired"));
            } else if (e instanceof MalformedJwtException) {
                throw new WebApplicationException(getUnAuthorizeResponse(PRIVATE_KEY + " is not correct"));
            }
        }
        return request;
    }

    private Response getUnAuthorizeResponse(String message) {
       return Response.ok(BaseResponse.FAILURE, message
       ).status(Response.Status.UNAUTHORIZED)
        .type(MediaType.APPLICATION_JSON)
        .build()
    }
}

Let’s go through the logic behind the implementation.

  1. The JwtCustomToken is just a JAX-RS name binding (think of it’s as a CDI interceptor binding), so it’s just an annotation that binds to a filter. We’ll see that how to create JwtCustomToken annotation in a couple of minutes.
  2. Priority for user authentication. You can read more about other priorities in this link.
  3. The filter method is invoked at runtime whenever a new request came.
  4. If the path of the current request is authorization_service then we simply return the ContainerRequest immediately because at here our client trying to create a new privateKey. Also in here, we don’t need to check that user added the Jwt token inside the header request or not.
  5. Gets the HTTP Authorization header from the request (the privateKey).
  6. If privateKey is not added inside the header of the request then we simply throw WebApplicationException with a message (privateKey is missing in header).
  7. It validates the token (privateKey) using the Jwt library. If the token is valid then, in the end, we simply return the ContainerRequest and if it is not valid or token expires WebApplicationException thrown with a specific message.

The following shows the code snippet for JwtCustomToken annotation class.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@NameBinding
public @interface JsonTokenNeeded {
}

Issue a json web token (Jwt) or privateKey

Now we have a filter that checks that the privateKey is passed inside the HTTP header. But how is this token issued? Here comes our JwTokenHelper class which create JJwt token based on user’s username and password also claims the token.

public class JwTokenHelper {

   private static JwTokenHelper jwTokenHelper = null;
   private static final long EXPIRATION_LIMIT = 30;
   private Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

   private JwTokenHelper() {
   }

   public static JwTokenHelper getInstance() {
      if(jwTokenHelper == null)
           jwTokenHelper = new JwTokenHelper();
      return jwTokenHelper;
   }

   //  1
   public String generatePrivateKey(String username, String password) {
       return Jwts
                .builder()
                .setSubject(username)
                .setSubject(password)
                .setExpiration(getExpirationDate())
                .signWith(key)
                .compact();
   }
   
   // 2
   public void claimKey(String privateKey) throws ExpiredJwtException, MalformedJwtException  {
      Jwts
                .parser()
                .setSigningKey(key)
                .parseClaimsJws(privateKey);
   }

   // 3
   private Date getExpirationDate() {
       long currentTimeInMillis = System.currentTimeMillis();
       long expMilliSeconds = TimeUnit.MINUTES.toMillis(EXPIRATION_LIMIT);
       return new Date(currentTimeInMillis + expMilliSeconds);
   }

}

Below is the explanation of the above code.

  1. In here we set few claims for Json Web Token. Add the username and password, set expiration date for privateKey and sign in with SignatureAlgorithm.HS256.
  2. Validating the key with Jwts parser. If the key is not valid then the method throws MalformedJwtException and if it expires then it throws ExpiredJwtException.
  3. This is the method which sets that the privateKey is only valid for the next thirty minutes.

Now that we’ve all part’s, let’s put them together and run our application. In this article, we only see the configuration by extending ResourceConfig instead of a web.xml file.

public class MyResourceConfig extends DefaultResourceConfig {

    
    public MyResourceConfig() {
        super(HomeApiService.class); 
        Map<String, Object> maps = new HashMap<String, Object>();
        maps.put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, CORSFilter.class);  
        maps.put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS, JsTokenFilterNeeded.class);
        setPropertiesAndFeatures(maps);
    }
}

The DefaultResourceConfig class is itself an extension of JAX-RX Application class and also provides some convenience methods like setPropertiesAndFeatures to register our filters. Now in our MyResourceConfig constructor, we define a Map<String,Object> that will hold our resource and response filters and later set maps to ResourceConfig.

Run your rest Service

To run your web application paste the following code snippet inside your main method and afterward run the application like you run a simple java program.

public static void main(String[] args) {
        ResourceConfig resourceConfig = new MyResourceConfig();
        try {
             // change with your application ip and port address. 
            GrizzlyServerFactory.createHttpServer(URI.create("http://000.000.000.000:xxxx"), resourceConfig);   
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Closing Up

In this article, I just wanted to show you how easy it is to issue and validate the Json Web Token with JAX-RX. Anything which I miss in this article please let me know via comment.

What’s next

Thank you for being here and keep reading…

The post Build authentication into your Java API’s with Json Web Token (Jwt) appeared first on Coding Infinite.

]]>
https://codinginfinite.com/authentication-java-apis-json-web-token-jwt/feed/ 2
10 new features or treasures in Java 11 | JDK 11 https://codinginfinite.com/ten-features-java-jdk-11/ https://codinginfinite.com/ten-features-java-jdk-11/#comments Thu, 13 Dec 2018 08:10:00 +0000 https://codinginfinite.com/?p=1937 There are a few 🏆 treasures that have been added as a part of JDK 11 and help developers to minimize coding efforts. Some of them you definitely know such as responsive Http2/API, you can run the source code directly without compiling. However, have you tried extensions of common classes such as String, Optional, Collections...

The post 10 new features or treasures in Java 11 | JDK 11 appeared first on Coding Infinite.

]]>
There are a few 🏆 treasures that have been added as a part of JDK 11 and help developers to minimize coding efforts. Some of them you definitely know such as responsive Http2/API, you can run the source code directly without compiling. However, have you tried extensions of common classes such as String, Optional, Collections and Files, and if you haven’t already, congratulations, you’ll learn about some of them in this article.

You can download the JDK 11 from this link.

1. Local parameter type inference for Lambda Expressions

JDK 11 allows var keyword to be used when declaring the formal parameters of implicitly typed lambda expressions:

Function<String, String> append = (var string) -> string + " World";
String appendedString = append.apply("Hello");
System.out.println(appendedString);

It’s not just adding the var keyword to the variable that omits the type. The JDK 11 also allow annotations to be added to lambda’s parameters without having to write full variable type name. Let’s take a look at with example:

Function<String, String> append = (@NonNull var string) -> string + " World";
String appendedString = append.apply("Hello");
System.out.println(appendedString);

// Output of above program
Hello World

Now the apply method inside the Function accepts the NonNull value.

2. Usage of String:: repeat method to copy a String

A String method for repeating sequences of characters has been long requested from a user in StackOverflow.

RepeatMethodForJDK11

Now from JDK 11, we have a repeat method that does exactly what we need.

var str = "abc";
var repeated = str.repeat(3);
System.out.println(repeated);

// Output of above program
abcabcabc

3. Create a Path with Path::of method

I really like the Path API, which solves the problem of switching between paths, URIs, URLs, and FILEs. In Java 11 we can use Paths::get and Path::of methods to make them very uniform.

Path googlePath = Path.of(URI.create("www.google.com"))
Path studentFilePath = Path.of("/home/Students/student.txt")

4. Files::readString and Files::writeString for file reading and Writing

If you need to read content from a very large file, we generally use lines method which returns a lazy stream. Similarly, if you want to store content in a file at the same time, I usually write to the files using the write method by passing an Iterable.

But in JDK 11, two methods of readString and writeString have been added to Files class.

String studentFileContent = Files.readString(Path.of("student.txt"))
String modifiedStudentContent = addNewSubject(studentFileContent)
Files.writeString(Path.of("student-mod.txt"),modifiedStudentContent)

5. String::lines to get the number of data Rows

You have a multi-line string and you want to do a separate operation for each line. You can do this with String::lines method.

var multiline = "My\nname is\nahsen saeed";
Stream<String> stream = multiline.lines();
stream.map(String::toUpperCase)
      .forEach(System.out::println);

// Output of above program
MY
NAME IS
AHSEN SAEED

The lines in the Stream are in the order in which they occur in the string. Unlike split, the new lines method is lazy.

Note: The new line in a string in the above example uses \n in Windows. Although, I’m using it in Linux, lines method can still split them. In a different operating system, the lines method will use \r, \n, \r\n as terminators. Even if we mix them in a string.

6. Usage of Optional::isEmpty instead of isPresent

Almost in every application, we need to check that if the value exists or not and for that, we often use the Optional::isPresent method. I know! there is no problem in writing that, but writing does not understand the meaning of well. Now if we need to check that the value is empty inside the Optional::isPresent method then we add the ! operator at the front.

val optional: Optional<String> = Optional.empty()
if(!optional.isPresent)
    // value is empty

This ! is easily forgotten on Optional starting with Java 11, we have a better solution.

val optional: Optional<String> = Optional.empty()
    if (optional.isEmpty) {
        //  value is empty
    }

The isEmpty method is used to return true if the value is not present.

7. Handling regular expressions with Pattern::asMatchPredicate

You have a regular expression and want to do some filtering based on it. What do you do?

Pattern<String> chracterPredicate = Pattern.compile("ah").asPredicate();
Stream.of("coding","ahsan","infinite")
       .filter(chracterPredicate)
       .forEach(System.out::println);

// Output of above program
ahsan

We had the asPredicate method as part of JDK 8, which will create a Predicate if and only if the given Pattern is found in a given string.

In JDK 11 we’ve another method Pattern::asMatchPredicate. So, what is the difference between them:

  • asPredicate: Under the hood, the asPredicate method called  matcher(s).find().
  • asMatchPredicate: It will checks if the entire string conforms to this regular expression. This method behaves like matcher(s).matches().

Now let’s say you have a regular expression that verifies the email.

private static final String EMAIL_PATTERN = "^[a-zA-Z0-9#_~!$&'()*+,;=:.\"<>@\\[\\]\\\\]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*$";

var pattern = Pattern.compile(EMAIL_PATTERN).asMatchPredicate();
Stream.of("jondoe@gmail.com")
      .filter(pattern)
      .forEach(sendEmailToMySecretary);

You see the asMatchPredicate method checks our regular expression internally and return the email if it is matched with the regular expression.

8. Usage of the strip, stripLeading, and stripTrailing

JDK 11 introduces the strip methods, which have some subtle differences from trim, let’s see them one by one:

8.1 strip

The strip() method is used to remove the leading and trailing whitespaces. It is helpful to remove the white space from the beginning and the end of a string.

var s = " Michale ";
String stripResult = s.strip(); 
System.out.println(stripResult)

// Output of above program
"Michale"

8.2 stripLeading

The stripLeading() method is used to remove the white space from the beginning of a string.

var s = " Michale ";
String stripLeadingResult = s.stripLeading();
System.out.println(stripLeadingResult)

// Output o above program
"Michale "

8.3 stripTrailing

The stripTrailing() method is used to remove the white spaces from the end of a string.

var s = " Michale ";
String stripTrailingResult = s.stripTrailing();
System.out.println(stripTrailingResult)

// Output of above program
" Michale"

9. Handle empty I/O operations with null StreamReader

What do you do when you need an InputStream that doesn’t process any data? What about an empty OutputStream, Reader or maybe Writer? In Java 11 you can do the following conversions:

InputStream inputStream = InputStream.nullInputStream();
OutputStream outputStream = OutputStream.nullOutputStream();
Reader reader = Reader.nullReader();
Writer writer = Writer.nullWriter();

10. Convert collections to arrays using Collection::toArray

How did you turn the collection into an array before Java 11?

Studen[] studentArray = studentList.toArray(new Student[0]);

The above toArray method returns an array if the list fits in the specified array. Otherwise, a new array is allocated with the runtime type of the specified array and size of the list.

Now that we have a method which returns an array of specified type T. Why do we need another function? Is there a better way to handle it? In Java 11, you can do this:

Student[] studentArray = studentList.toArray(Student[]::new);

This is a new overloading method used by collection classes to receive IntFunction. The above Collection::toArray method returns an array of the corresponding length based on the length of the input data. It can be expressed in a brief and clear manner as T[]::new.

What’s next

Closing Up

Hope you found this story useful and interesting. Feel free to share your feedback and comments below.

Thank you for being here and keep reading…

The post 10 new features or treasures in Java 11 | JDK 11 appeared first on Coding Infinite.

]]>
https://codinginfinite.com/ten-features-java-jdk-11/feed/ 3
Android Cab Booking App Tutorial Part 2 https://codinginfinite.com/android-cab-booking-app-tutorial-firebase/ https://codinginfinite.com/android-cab-booking-app-tutorial-firebase/#comments Wed, 05 Dec 2018 21:27:52 +0000 https://codinginfinite.com/?p=1876 This article is the second part of the series, Frisbee an Android cab booking app tutorial. If you did not read the previous one, you can start here. Previously in Android cab booking App In the previous pit stop, we had our discussion on GoogleMaps, Marker Animation and on Reverse Geocode. We learned how to show GoogleMaps,...

The post Android Cab Booking App Tutorial Part 2 appeared first on Coding Infinite.

]]>
This article is the second part of the series, Frisbee an Android cab booking app tutorial. If you did not read the previous one, you can start here.

Previously in Android cab booking App

In the previous pit stop, we had our discussion on GoogleMaps, Marker Animation and on Reverse Geocode. We learned how to show GoogleMaps, how to animate the Marker to user current location and Reverse Geocode the location where a user drags the PinView.

You can follow me by looking commits of code that I update on Github. Java application link and Kotlin application link.

4. Setting up firebase realtime Database

In the fourth part of Android Cab booking, we need to add firebase to our project. Please see the Manually add Firebase section in this link and configure your Android project into the firebase realtime database.

Note: Please set the security rules for firebase realtime database in a test mode. 

5. Read online drivers from firebase realtime Database

After setting up the firebase project in firebase console we need to read the online drivers from the realtime database but there are some prerequisites before start reading the drivers.

5.1 Online drivers database Structure

Before start reading the online Drivers, you guys need to know how data represented in the firebase realtime database.

Firebase realtime databaseYou see each Driver in online_drivers has its own key.  Once any update happens like (Driver location changed) at a certain Driver node or a new Driver added you’ll receive a DataSnapshot. We parse snapshot to get the value, pretty straightforward! You’ll see how to do this in a couple of minutes.

5.2 Setting up the firebase DatabaseReference

In order to connect the DatabaseReference with the firebase database, we need to get the reference from FirebaseDatbase. Paste the following code at the top of the MainActivityViewModel class.

companion object {
   private const val ONLINE_DRIVERS = "online_drivers"  // 1
}

private val databaseReference = FirebaseDatabase
           .getInstance() // 2
           .reference  // 3
           .child(ONLINE_DRIVERS)  // 4

Here’s what each line means:

  1. The root node name for online drivers. All online drivers will be in this node.
  2. Get the default FirebaseDatabase instance.
  3. Get the reference of the database, which effectively points to the root of the tree/directory.
  4. Inside the root node get the reference to the online_drivers node because we only need to read the online drivers.
5.3 Read online drivers

Now that we’ve online drivers in firebase realtime database and Database Reference, why not read it back from the database and show them inside our application. To read data or receive snapshots, we need to use Firebase’s Value Listener.  There are three ways to read the online Drivers from the firebase realtime database. Let’s see what’s best for our use case:

  • First, by calling the addListenerForSingleValueEvent on DatabaseReference, you read data only once. After you received it, the listener is removed. This is great when you need to read something once to use it in your app.
  • Second, using the addValueEventListener method, you listen to a certain node and all its changes. Even if the smallest thing changes you get the entire snapshot again. This is great for showing data that isn’t large but tends to change and can benefit from a realtime update like a single Driver model.
  • Lastly, with the addChildEventListener method, you subscribe to each change for each child in a node. If we change any of them, remove them or update them, we get an event for each of the mention cases. More importantly, it will emit each of the children one by one, by the first time you attach the listener.

The addChildEventListenermethod is best suitable for our use case, it will download all online Drivers from the database at application start and notify us whenever there’s a slight change in the online driver’s node.

Now add the child event listener to DatabaseReference inside the MainActivityViewModel init block. The reason we’re setting the event listener inside the initializer block because we need to retrieve all online drivers at application startup.

val firebaseValueEventListener = FirebaseValueEventListenerHelper(this)

init {
     databaseReference.addChildEventListener(firebaseValueEventListener)
}

The addChildEventListener method accepts the ChildEventListener type instance and our FirebaseValueEventListenerHelper class implements the ChildEventListener. Another thing we need to discuss is this parameter that we’re passing inside FirebaseValueEventListenerHelper. The FirebaseValueEventListenerHelper class constructor accepts the FirebaseObjectValueListener instance as a parameter and our MainActivityViewModel needs to implement it.

Create the below FirebaseObjectValueListener interface inside the listener package.

interface FirebaseObjectValueListener {

    fun onDriverOnline(Driver driver);

    fun onDriverChanged(Driver driver);

    fun onDriverOffline(Driver driver);
}

After creating the FirebaseObjectValueListener we need to implement the listener on MainActivityViewModel class and override all the abstract methods. You can get the Driver class inside the models’ package from GitHub code.

public class MainActivityViewModel : ViewModel(), CoroutineScope, FirebaseObjectValueListener {

    .......
    .......
    .......

    override fun onDriverOnline(driver : Driver) {
      Log.e(TAG, "On driver online -> ${driver.toString()}")
    }

    override fun onDriverChanged(driver : Driver) {
      Log.e(TAG, "On driver updated -> ${driver.toString()}")
    }

    override fun onDriverOffline(driver : Driver){
      Log.e(TAG, "On driver removed -> ${driver.toString()}")
    }

}

After implementing the listener on MainActivityViewModel class, we only need to call those abstract methods from FirebaseValueEventListenerHelper class. Add the following code inside FirebaseValueEventListenerHelper class.

class FirebaseValueEventListenerHelper constrcutor (private val firebaseObjectValueListener : FirebaseObjectValueListener) : ChildEventListener {

    // 1
    override fun onChildAdded(dataSnapshot : DataSnapshot, s : String?) {
       val driver = dataSnapshot.getValue(Driver.class)
       firebaseObjectValueListener.onDriverOnline(driver)
    }

    // 2
    override fun onChildChanged(dataSnapshot : DataSnapshot, s : String?) {
       val driver = dataSnapshot.getValue(Driver.class)
       firebaseObjectValueListener.onDriverChanged(driver)
    }

    // 3
    override fun onChildRemoved(dataSnapshot : DataSnapshot) {
       val driver = dataSnapshot.getValue(Driver.class)
       firebaseObjectValue.onDriverOffline(driver)
    }

    override fun onChildMoved(dataSnapshot : DataSnapshot, s : String?) {       
    }

    override fun onCancelled(databaseError : DatabaseError) {
    }

}

Let’s go through the logic behind the implementation:

  1. This method will be triggered whenever there’s a new Driver added inside the online_drivers node. Notice how we parse the Driver model, by calling the getValue method, the snapshot is parsed to whatever data model you want. After parsing the data we simply call the onDriverOnline method, which implemented inside the MainActivityViewModel class.
  2. This method will be triggered whenever the Driver node update in the online_drivers node.
  3. The onChildRemoved method will be called when there’s a Driver goes offline inside the online_drivers node.

So, we create the DatabaseReference and start listening to the online_drivers node. But what did we read? because currently, there are no online drivers in our online_drivers node. If you don’t want to test and run the application you can skip the Dummy insertion in a realtime Database and jump to the Driver Data Structure part.

6. Dummy insertion in firebase realtime Database (Optional)

I’ve created a couple of dummy drivers myself inside the online_drivers node for testing. You can see the following video of how to create dummy drivers inside the firebase realtime database.

 

After creating the dummy driver in the realtime database, build and run your application you should see the online Drivers log inside the Android Studio Logcat.

firebase_driver_online

In order to see the update or remove driver log. Go ahead and change the lat or lng from any of the Driver nodes for an update and delete a single Driver node for remove.

firebase_update_remove_driver

Note: You can also insert dummy driver while opening the application, the moment you hit enter immediately you’ll see the log for online Driver inside Android Studio Logcat.

7. Drivers Data Structure

In order to store the multiples driver inside our application, we need to have some kind of Data Structure so, that we can update the Driver Marker whenever their current location changed or remove the Driver Marker when they go to offline. The same thing goes with the online, we insert the Driver inside our collection.

Now in this article, I’m not gonna discuss our Data Structure because it is a simple CRUD. Currently, we’ve DriverCollection and MarkerCollection inside our collection package. You can get these collections classes from GitHub.

The DriverCollection simply holds our online Drivers information inside List and have some utility methods regarding insert, delete, retrieve and update.

The MarkerCollection has the Map<String,Marker>, where String is for Driver’s key and Marker is for value. It also has the almost same utility method for insert, update, delete and retrieve.

7.1 Explanation of DriverCollection and MarkerCollection

Whenever a new online driver found inside the onDriverOnline method, we insert the Driver in DriverCollection, create a new instance of Marker and store the Marker with driver’s key inside the MarkerCollection’s class. Similarly, when the Driver node updates inside fiirebase database and the onDriverChanged method gets called, we get the previous Driver instance from DriverCollection and update the Driver with the updated lat, lng. After the update of Driver, we get the instance of Marker from MarkerCollection and animats the Marker to Driver’s new location.

8. Show or animate multiple markers simultaneously | remove | insert

Now that we’ve our collections classes, we only need to use these collections classes CRUD methods. Update the following methods inside MainActivityViewModel class.

public class MainActivityViewModel : ViewModel(), CoroutineScope, FirebaseObjectValueListener{

   .....
   .....
   .....

   // 1
    override fun onDriverOnline(driver: Driver) {
        if (driverRepo.insert(driver)) {
            val markerOptions = googleMapHelper.getDriverMarkerOptions(LatLng(driver.lat, driver.lng), driver.angle)
            _addNewMarker.value = Pair(driver.id, markerOptions)
        }
    }

   // 2
    fun insertNewMarker(key: String, value: Marker) {
        makerRepo.insert(key, value)
    }

   // 3
    override fun onDriverChanged(driver: Driver) {
        launch(context = coroutineContext) {
            val fetchedDriver = driverRepo.get(driver.id) ?: return@launch
            fetchedDriver.update(driver.lat, driver.lng, driver.angle)
            val marker = makerRepo.get(fetchedDriver.id) ?: return@launch
            withContext(Dispatchers.Main) {
                marker.rotation = fetchedDriver.angle + 90
                MarkerAnimationHelper.animateMarkerToGB(
                    marker,
                    LatLng(fetchedDriver.lat, fetchedDriver.lng),
                    LatLngInterpolator.Spherical()
                )
            }
        }
    }

    // 4
    override fun onDriverOffline(driver: Driver) {
        launch(context = coroutineContext) {
            if (driverRepo.remove(driver.id))
                makerRepo.remove(driver.id)
        }
    }

}

Let’s go through the logic behind the implementation:

  1. The driver will be stored inside the DriverCollection. After the insertion, we get the MarkerOptions from GoogleMapHelper class and send a Pair(String, MarkerOptions) back to our MainActivity via LiveData.
  2. To insert the new Marker inside the MarkerCollection, we call this method from MainActivity.
  3. We launch a coroutine because our get method for DriverCollection and get method for MarkerCollection are suspending functions. After the update of Driverwe switch context to Main Dispatcher from IO because in order to update Marker we need to be in Main Thread.
  4. Again launch a coroutine because of the remove method for DriverCollection and remove method for MarkerCollection are suspending. After launching a coroutine we simply remove the Driver from DriverCollection and remove Marker from MarkerCollection.

Before start observing the Pair<String,MarkerOptions> inside MainActivity, we need to define the _addNewMarker in the MainActivityViewModel class. Add the following code inside the MainActivityViewModel class.

private val _addNewMarker = MediatorLiveData<Pair<String, MarkerOptions>>()

val addNewMarker: LiveData<Pair<String, MarkerOptions>> = _addNewMarker

Now go to MainActivity and paste the following code inside the onCreate method to observe the addNewMarker instance from MainActivityViewModel.

viewModel.addNewMarker
      .observe(this, Observer<Pair<String,MarkerOptions> { markerPair ->
          val marker = googleMap.addMarker(markerPair.second)
          viewModel.insertNewMarker(markerPair.first, marker)
      })

When the Observer interface invoked we simply add the Marker in the GoogleMaps and call the insertNewMarker method.

Build and run to view your progress so far. You should see the Driver car markers in Google Maps. In order to see the Marker animation, you need to change the Driver location from firebase realtime database. I know! I know this is not a good solution but for now, we’ve to work like this. In the next article, we’ll see how to update the Driver location from a mobile application like real-time and the Frisbee app able to see the Driver car moving without updating it from the database.

Car Marker AnimationNote: You’ll not able to see the Driver car markers if the driver’s location inside the firebase realtime database is not near you.

Alright, guy’s, this was all from this article. See you in the next article.

Thank you for being here and keep reading…

Previous Part

Next Part

The post Android Cab Booking App Tutorial Part 2 appeared first on Coding Infinite.

]]>
https://codinginfinite.com/android-cab-booking-app-tutorial-firebase/feed/ 5
Everything you need to know about using Google Maps in Cab Booking App (Part 1) https://codinginfinite.com/current-location-reverse-geocode-cab-booking-app/ https://codinginfinite.com/current-location-reverse-geocode-cab-booking-app/#comments Thu, 29 Nov 2018 13:06:22 +0000 https://codinginfinite.com/?p=1765 Hey, Android Developers! I would like to share my experience with you guys when I got to make Cab booking application. I learn a lot during the development of this application. The following are the features which we’re going to discuss in this article: Show GoogleMaps. Animate multiples marker simultaneously on GoogleMaps. Calculate the distance...

The post Everything you need to know about using Google Maps in Cab Booking App (Part 1) appeared first on Coding Infinite.

]]>
Hey, Android Developers! I would like to share my experience with you guys when I got to make Cab booking application. I learn a lot during the development of this application. The following are the features which we’re going to discuss in this article:

  • Show GoogleMaps.
  • Animate multiples marker simultaneously on GoogleMaps.
  • Calculate the distance from the PinView location to the nearest driver.
  • Show multiples online drivers on GoogleMaps.
  • Animate the User PinView and show nearest driver distance time.
  • Working with Firebase Real-time Database.
  • Reverse Geocode location where the user drags current Marker.

What our app will be?

To show you how to implement above mention features in an app, we’re going to create a simple app called Frisbee: it will start listening to drivers from firebase realtime database and show the online drivers in the application. Animate the multiple driver markers simultaneously whenever their current location changed in firebase realtime database. Calculate the distance from PinView location to the nearest driver location, animate the Marker while calculating the distance and show the result inside the custom Marker.

Before to start coding the Frisbee, I want to show you guys the demo of application:

Prerequisite

  • Requires Google Maps API key to show Google Maps. Go to Developer Console -> Sign In with your Gmail account -> Create a new project -> API & Services -> Library -> Search for Google Maps and click on Maps SDK for Android -> Enable API. After enabling the Maps SDK, go to API & Services -> Credentials -> Create Credentials -> API key -> Restrict API key for your Android project.
  • Google Distance Matrix API key to calculate distance. Go to the previously created project in Developer Console -> API & Services -> Library -> Search for Distance Matrix and click on Distance Matrix API -> Enable API. After enabling the Distance Matrix API, go to API & Services -> Credentials -> Create Credentials -> API key -> Restrict key, change the name (optional), and finally click on save button. To add restrictions to an API key see this link.

Note: This tutorial will not focus on how to build the app for scale or for performance. It’s basically designed so that you can have fun while building it, and how you can create something that mimics Uber or Careem. Think of this as though building a Minimum Viable Product to demonstrate your idea or startup, for a proof-of-concept.

Now that everything is done, let’s make our hands dirty and create a new Android Studio Empty project:

Android Studio

In Android Studio go to Create Android Project, press next twice and select Empty Activity, then finish. After the Android Studio gradle builds successfully you’re ready to do the code. You can see MainActivity like below:

import android.support.v7.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Before start coding our application we need to add some dependencies to our app level build.gradle file. The following shows the dependencies:

// Google dependencies
implementation 'com.google.android.gms:play-services-maps:16.0.0'
implementation 'com.google.firebase:firebase-database:16.0.5'
implementation 'com.google.android.gms:play-services-location:16.0.0'

// Material dialog dependency
implementation 'com.afollestad.material-dialogs:core:0.9.6.0'

// ViewModel dependencies
implementation 'android.arch.lifecycle:viewmodel:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'

// Kotlin coroutines dependencies
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'

// Map services dependency
implementation 'com.google.maps:google-maps-services:0.2.4'

Sync the project and everything should be fine without any kind of gradle error.

Let’s make our activity_main.xml file to show the views on the screen.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        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"
        android:id="@+id/mainActivityRootView"
        tools:context=".activities.main.ui.MainActivity">
    
    // 1
    <fragment xmlns:tools="http://schemas.android.com/tools"
              android:id="@+id/map"
              android:name="com.google.android.gms.maps.SupportMapFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              tools:context="spartons.com.frisbee.activities.main.ui.MainActivity"/>

    // 2
    <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true">

        <FrameLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="100dp"
                tools:ignore="UselessParent">

            <FrameLayout
                    android:id="@+id/pinViewLine"
                    android:layout_width="2dp"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginTop="40dp"
                    android:background="@drawable/line_background"/>

            <FrameLayout
                    android:id="@+id/pinViewCircle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:background="@drawable/circle_background">

                <TextView
                        android:id="@+id/pinTimeTextView"
                        android:layout_width="40dp"
                        android:layout_height="45dp"
                        android:layout_gravity="top|center_horizontal"
                        android:gravity="center"
                        android:padding="2dp"
                        android:textColor="@android:color/white"
                        android:textSize="12sp"/>

                <ProgressBar
                        android:id="@+id/pinProgressLoader"
                        android:layout_width="45dp"
                        android:layout_height="45dp"
                        android:layout_gravity="top|center_horizontal"
                        android:layout_margin="4dp"
                        android:contentDescription="@null"
                        android:indeterminate="true"
                        android:visibility="gone"/>

            </FrameLayout>

        </FrameLayout>

    </FrameLayout>

    //  3
    <TextView
            android:id="@+id/currentPlaceTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerInParent="true"
            android:ellipsize="end"
            android:gravity="center"
            android:maxLines="1"
            android:background="@drawable/place_search_style"
            android:layout_margin="30dp"
            android:padding="10dp"
            android:text="@string/search_place"
            android:textColor="@color/colorPrimaryText"/>

    // 4

    <ImageButton
            android:id="@+id/currentLocationImageButton"
            android:layout_width="40dp"
            android:layout_height="wrap_content"
            android:layout_above="@+id/currentPlaceTextView"
            android:layout_alignParentEnd="true"
            android:layout_marginBottom="10dp"
            android:layout_marginEnd="2dp"
            android:contentDescription="@null"
            android:src="@drawable/current_location_icon"
            android:layout_alignParentRight="true"
            android:layout_marginRight="2dp"/>

</RelativeLayout>

Below is the explanation of the above code:

  1. The fragment tag to show the Google Maps.
  2.  FrameLayout for the custom Marker. The custom Marker is for a user to drag anywhere on the Google Map and set the pick-up location for a ride.
  3.  TextView for the current location place name.
  4. Finally, the ImageButton is for to moves the camera to a user’s current location.

Build your application and run to view your progress so far.

Custom Google Map MarkerThere are many key points in this application. So, we’re gonna need to break the application into smaller parts.

I will try to explain as many of lines of code as I can. You can follow looking on commits of code that I published on GitHub. Java application link and Kotlin application link.

1. Initialize The Google Maps

In order to use Google Maps, we need to initialize it first in our MainActivity onCreate method and store the instance of Google Maps so that later we can use it.

class MainActivity : AppCompatActivity() {

    private var googleMap : GoogleMap? = null

    override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
           .......
           .......

          val supportMapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
          supportMapFragment.getMapAsync { googleMap ->
                this.googleMap = googleMap
          }
    }

}

2. Animate Google Maps to user current location:

In this part of an application, we’re going to request user current location from LocationServices and animate the Google Map to that location. Before requesting the last known location from a device we need to check that if the user has PlayServices installed in his/her mobile. Add the below code in MainActivity onCreate method:

if(!uiHelper.isPlayServicesAvailable()) {
     toast("Play Services is not installed!")
     finish()
} else requestLocationUpdates()

If the user has not installed PlayServices in his/her mobile phone then we’re gonna exit the application and show Toast else, we simply request location updates from the device. The UiHelper is the utility class with just a bunch of helper method.

Next, add the following code inside the UiHelper class.

fun isPlayServicesAvailable() : Boolean {
    val googleApiAvailability = GoogleApiAvailability.getInstance()
    val status = googleApiAvailability.isGooglePlayServicesAvailable(context)
    return ConnectionResult.SUCCESS == status
}

As you may have noticed that if the user has PlayServices installed then we’re calling our requestLocationUpdates method. Add the below method inside MainActivity.

private fun requestLocationUpdates() {
        if (!uiHelper.isHaveLocationPermission()) {
            ActivityCompat.requestPermissions(
                this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION
            )
            return
        }
        viewModel.requestLocationUpdates()
    }

The code above checks if the app has been granted the ACCESS_FINE_LOCATION permission. If it hasn’t, then request it from the user because, starting with Android 6.0, user permission is handled little differently than before. You don’t request permission during the installation of your app; rather, you request them at runtime when the permission is actually required.

If the user granted the location permission then simply we ‘re gonna call our requestLocationUpdates method inside the ViewModel. All of our network calls and logical stuff will be handled inside MainActivityViewModel and listens to the data via LiveData.

You can check out my other articles on LiveData and ViewModel.

Add the below method inside MainActivityViewModel class.

fun requestLocationUpdates() {
   locationProviderClient.requestLocationUpdates(uiHelper.getLocationRequest(), locationCallback, Looper.myLooper());
}

The FusedLocationProviderClient request location updates with LocationCallback on a specified Looper. Also, you may have noticed that we need a locationCallback and  LocationRequest to successfully receive location updates.

Add the following getLocationRequest method inside the UiHelper class.

fun getLocationRequest() : LocationRequest {
   val locationRequest = LocationRequest.create()
   locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
   locationRequest.interval = 3000
   return locationRequest
}

The LocationRequest object is usually used to request a quality of service for location updates. You see we’re setting high priority accuracy and location interval to 3 seconds. Now after every 3 seconds our application will receive a Location update via LocationCallback which we’ve passed previously when requesting location updates.

After adding LocationRequest we only need to create our LocationCallback instance. Add the LocationCallback object creation as follows inside MainActivityViewModel class.

private val locationCallback: LocationCallback = object : LocationCallback() {

    override fun onLocationResult(locationResult: LocationResult?) {
        super.onLocationResult(locationResult)
        if (locationResult?.lastLocation == null) return
        _currentLocation.postValue(locationResult.lastLocation)
    }
}

The onLocationResult method called immediately when the device location information is available. The other thing we’re doing in this method is sending the location back to our MainActivity via LiveData.

Before start observing the current location in MainActivity we need to define this _currentLocation LiveData inside our MainActivityViewModel class.

private val _currentLocation = MediatorLiveData<Location>()

val currentLocation: LiveData<Location> get() = _currentLocation

You see we’re not exposing our MediatorLiveData instance publically instead we simply given a LiveData just to observe the location. By doing this we’re keeping our immutability principle safe.

Now that we’ve successfully requested location updates. Go to MainActivity and paste the following code inside the onCreate method to observe the user’s current location from MainActivityViewModel.

viewModel.currentLocation
         .observe(this, Observer<Location> { location ->
               if (firstTimeFlag) {
                        firstTimeFlag = false;
                        animateCamera(location);
               }
               showOrAnimateMarker(location);         
         })

The Observer interface is immediately invoked providing the most recent Location stored in currentLocation. The firstTimeFlag is our globally initialized bool, a default set to true. The reason we’re checking the firstTimeFlag condition, is because we need to animate the Google Maps camera to user current location when a user opens the application only the first time. After that, we simply animate the user current location Marker from previous coordinates to new coordinates.

Add the below animateCamera method inside MainActivity class.

private fun animateCamera(location : Location) {
     val cameraUpdate = googleMapHelper.buildCameraUpdate(location)
     googleMap.animateCamera(cameraUpdate, 10, null)
}

The animateCamera method animates the camera to the user’s current location. The GoogleMapHelper class provides several utility methods (CameraUpdate, default map settings, UserMarker) for Google Maps.

Next, add the following buildCameraUpdate method inside GoogleMapHelper class.

fun buildCameraUpdate(location: Location): CameraUpdate {
    val cameraPosition = CameraPosition.Builder()
        .target(LatLng(location.latitude, location.longitude))
        .tilt(TILT_LEVEL.toFloat())
        .zoom(ZOOM_LEVEL.toFloat())
        .build()
    return CameraUpdateFactory.newCameraPosition(cameraPosition)
}

Now add the showOrAnimateMarker method in the MainActivity class.

private fun showOrAnimateMarker(Location location) {
     if(currentLocationMarker == null){
           currentLocationMarker = googleMap.addMarker(googleMapHelper.getUserMarker(location))
     } else {
         MarkerAnimationHelper.animateMarkerToGB(currentLocationMarker, location, LatLngInterpolator.Spherical())
     }
}

You see first we check if the currentLocatinMarker is null then we create a new MarkerOptions with the help of GoogleMapHelper utility class, add Marker inside googleMap and store the Marker instance in the currentLocationMarker. Afterward, if it is not null then we simply animate the currentLocationMarker to user new location.

The LatLngInterpolator and MarkerAnimationHelper class are same which I explain in my previous article.

Build and run to view your progress so far. You should see a pin on your location and also your blue dot location Marker will animate if you’re traveling, walking, running etc.

Google Map Current Location MarkerNow that everything is done for showing the current location on Google Map and animate the Marker with user current location let’s start building our third application part.

3. Reverse Geocode PinView Coordinates And Show PlaceName

In order to Reverse Geocode the coordinates, we need to have user PinView Location. We can easily get these coordinates from CameraPosition target method. The target method will always give us the center point coordinates of Google Map which we see on screen and our PinView also stays in the middle of the screen. Now if we get the center point coordinates from target method, it means we’re getting the PinView coordinates.

Another thing we need to discuss is this, where do we call our Reverse Geocode method? If you guys have clearly see the demo of application you’ll understand that as soon the Google Map camera movement stopped the place name appeared inside the TextView. So, for this, we can easily implement OnCameraIdleListener on MainActivity. The OnCameraIdleListener has its callback function which defines what actions needs to done when the camera stopped its movement. In this callback, we need to get the coordinates from PinView and execute our Reverse Geocode request.

Let’s start the third part of an application by implementing the OnCameraIdleListener on MainActivity.

class MainActivity : AppCompatActivity(), GoogleMap.OnCameraIdleListener {

      private var googleMap : GoogleMap? = null

      override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            
            .......
            .......

            val supportMapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment supportMapFragment.getMapAsync { googleMap ->
                    googleMap.setOnCameraIdleListener(this)                          
                    this.googleMap = googleMap 
            }
     }

Now in onCameraIdle method, we need to get the current camera target coordinates from Google Maps and call the Reverse Geocode method.

override fun onCameraIdle() {
    val position = googleMap.cameraPosition.target
    viewModel.makeReverseGeocodeRequest(position, geoCoderValue.value); 
}

I know you guys must be thinking what is geoCoderValue. The geoCoderValue is nothing just a lazy initialized GeoCoder instance. It’s a good approach that we only initialized our GeoCoder once instead of creating a new instance every time. Add the below code inside the MainActivity to create lazy GeoCoder.

private val geoCoderValue = lazy {
      Geocoder(this)
  }

After creating GeoCoder inside MainActivity add the following code inside the MainActivityViewModel class to Reverse Geocode the PinView coordinates:

fun makeReverseGeocodeRequest(latLng: LatLng, geoCoder: Geocoder) {

    //1
    launch(context = coroutineContext) {

        // 2
        val result = geoCoder.getFromLocation(latLng.latitude, latLng.longitude, 1)
        if (result != null && result.size > 0) {
            val address = result[0]

             // 3
            _reverseGeocodeResult.postValue(address.getAddressLine(0).plus(" , ").plus(address.locality))
        }
    }
}

Here’s what’s going on in the makeReverseGeocodeRequest  method:

  1.  launch a coroutine builder so that our called thread will not be blocked. The Google documentation recommend us to Reverse Geocode in a separate thread instead of UI thread.
  2.  The getFromLocation method returns an array of Addresses that are known to describe the area.
  3. If the GeoCoder results are not null then we pass the result to MainActivity via LiveData to show the result in the TextView.

Before going to MainActivity and start observing the result we need to create our _reverseGeocodeResult LiveData inside MainActivityViewModel class.

private val _reverseGeocodeResult = MediatorLiveData<String>()

val reverseGeocodeResult : LiveData<String> = _reverseGeocodeResult

Now that we’ve successfully got the place name from coordinates. Go to MainActivity and paste the following code inside the onCreate method to observe the result from MainActivityViewModel.

viewModel.reverseGeocodeResult
            .observe(this, Observer<String> { placeName
                currentPlaceTextView.text = placeName
            })

When the Observer interface invoked we simply set the placeName in the TextView.

As a result, run the application you’ll see that the place name will be updated when you drag the PinView.

Reverse Geocode And Marker AnimationAlright, guys, that was all for this article. In the next article, we’ll see how to read online drivers from firebase realtime database and show or animate multiple Markers simultaneously.

Thank you for being here and keep reading…

Next Part

The post Everything you need to know about using Google Maps in Cab Booking App (Part 1) appeared first on Coding Infinite.

]]>
https://codinginfinite.com/current-location-reverse-geocode-cab-booking-app/feed/ 8
Basic Tips For Application Performance Optimization | Java https://codinginfinite.com/application-performance-optimization-java/ Sat, 22 Sep 2018 21:54:03 +0000 https://codinginfinite.com/?p=1068 Hello everyone! In this story, I want to give a brief introduction to how we can improve our application performance. There are many OS versions which developers find hards to keep up when it comes to development. Each system has different aspects regarding hardware or software making it development a nightmare. To ensure your app...

The post Basic Tips For Application Performance Optimization | Java appeared first on Coding Infinite.

]]>
Hello everyone! In this story, I want to give a brief introduction to how we can improve our application performance. There are many OS versions which developers find hards to keep up when it comes to development. Each system has different aspects regarding hardware or software making it development a nightmare.

To ensure your app performance well across a wide variety of systems, ensure your code is efficient at all levels and optimize your performance.

Below are the factors which can slow down the app performance and improvements.

Usage Enhance for loop syntax

The enhanced loop (also known as foreach loop) control flow statement for traversing items in a collection. With the collection, an iterator is allocated to make calls to next()and hasNext()

Let’s take a look at different alternatives for iterating through an array.

static class Student {
    int age;
    int marks;
}

Studen[] mArray = ...

public void first() {
    int totalAllStudentMarks = 0;
    for (int i = 0; i < mArray.length; ++i) {
        totalAllStudentMarks += mArray[i].marks;
    }
}

public void second() {
    int totalAllStudentMarks = 0;
    Student[] localArray = mArray;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        totalAllStudentMarks += localArray[i].marks;
    }
}

public void third() {
    int totalAllStudentMarks = 0;
    for (Student a : mArray) {
        sum += a.marks;
    }
}

first(): It is slow because of the cost of getting the array length once for every iteration through the loop.

second(): It is faster it pulls everything out into local variables avoiding the look-up. There’s array length offers a performance benefit.

third(): It is fastest from both of them, it uses the enhanced for-loop introduced in Java 1.5 version.

Avoid Creating Unnecessary Objects

As you allocate more objects in your app you will force a periodic garbage collection. The concurrent garbage collection introduced, but unnecessary work will always be avoided.

  • Always creates an array of int instead of creating an array of Integer objects. The int is a wrapper class of Integer. Two parallel arrays of int are also a lot more efficient than an array of (int, int) objects. The same goes for any combination of primitive types.
  • When returning a String from a method is always appended StringBuffer to anyway, change your function signature and implementation so that it does not append directly, instead of creating a short-lived temporary object.
  • Do not create a new instance of String, an example is below.
// Don't do this
String s  = new String("Bad way to creating a string");

// Create a String like this
String s = "Hey there";

Generally speaking, avoid creating short-term temporary objects if you can. Fewer objects creation means less frequent garbage collection.

Reusing mutable objects

We need to reuse our objects if we know they can’t be modified. The following shows the example.

class Person {
  String name;
  Calendar bornDate;

  public int getCurrentAge() {
      Calendar currentDate = Calendar.getInstance()
      int years = currentDate.get(Calendar.YEAR) - bornData.get(Calendar.YEAR)
      return years;
  }
}

You see the getCurrentAge method always create a new instance of Calendar every-time it is invoked. So, how we can avoid unnecessary object creation. Below is the example is a modified example of Person class.

class Person {
  String name;
  Calendar bornDate;
  private static final Calendar currentDate = Calendar.getInstance();

  public int getCurrentAge() {
     int years = currentDate.get(Calendar.YEAR) - bornDate.get(Calendar.YEAR);
     return years;
  }
}

 

Prefer static over virtual

If you don’t need to access an object field, make your method static. An invocation will be about 15% to 20% faster.

Avoid using floating point

The floating-point is about 2x slower than an integer. In speed terms, there’s no difference between float  and double on the more modern hardware. On desktop machines space is not an issue so you should prefer double to float.

Use static final for constants

Fields with the static final go into dex file. Whenever the constants fields are required it will use relatively inexpensive “string constant” instruction instead of a field lookup.

Eliminate obsolete object reference

An obsolete reference is one that is kept around but will never be used, preventing the object it refers to from being eligible for garbage collection, thus causing a memory leak. The best way to eliminate the obsolete reference by falling it out of scope.

Define each variable in the narrowest possible scope.

Don’t just start measure

Before you start optimizing, make sure you have a problem that you need to solve. Make sure you can accurately measure your existing performance or you won’t be able to measure the benefits of the alternative you try.

Alright, guys, this was all from performance optimization article. I know there’s a lot more which I miss in this article but I’m just a beginner like you guys. I write what I learned. Please let me know what did I miss in this article.

My new article Ten Simple Rules For Best Programming Practices.

Thank you for being here and keep reading…

The post Basic Tips For Application Performance Optimization | Java appeared first on Coding Infinite.

]]>
Android ViewPager And ViewPagerIndicator Example https://codinginfinite.com/android-viewpager-viewpagerindicator-example/ Sat, 01 Sep 2018 19:36:28 +0000 https://codinginfinite.com/?p=998 A friend of mine came to me and said I want to make a Payment Transaction app. We’ve talked for a while about the project and clarify some points regarding development point of view. After some days he came up with designs, I made some changes to the designs and start developing the app. In...

The post Android ViewPager And ViewPagerIndicator Example appeared first on Coding Infinite.

]]>
A friend of mine came to me and said I want to make a Payment Transaction app. We’ve talked for a while about the project and clarify some points regarding development point of view. After some days he came up with designs, I made some changes to the designs and start developing the app. In the designs, there’s an App Intro screen with a ViewPager, a ViewPagerIndicator, and a beautiful animation when swiping through pages. I personally really like the App Intro design and thought why not write an article on it.

ViewPager With ViewPagerIndicator Example

You see the demo right..? Now, let’s start making it in Android. This is a very simple app and I did not include any kind of dependencies so we’re not gonna see the build. gradle file or Manifest file.

Android App Coding

The design is very basic first, at the top we have a ViewPager, then the ViewPagerIndicator dots, and last at the bottom there’s a Button. Below is the activity_main.xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    android:background="@drawable/intro_background"
    tools:context=".MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/myViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/viewPagerIndicatorIconsLayout"
        android:layout_marginBottom="10dp" />


    <LinearLayout
        android:id="@+id/viewPagerIndicatorIconsLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/getStartedButton"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/firstDotImageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@null"
            android:src="@drawable/current_position_icon" />

        <ImageView
            android:id="@+id/secondDotImageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="7dp"
            android:contentDescription="@null"
            android:src="@drawable/disable_position_icon" />

        <ImageView
            android:id="@+id/thirdDotImageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="7dp"
            android:contentDescription="@null"
            android:src="@drawable/disable_position_icon" />

    </LinearLayout>

    <Button
        android:id="@+id/getStartedButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="15dp"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="20dp"
        android:background="@drawable/button_drawable"
        android:text="@string/get_started"
        android:textAllCaps="false"
        android:textColor="@color/colorSubText" />

</RelativeLayout>

Main Activity

class MainActivity : AppCompatActivity() {

    companion object {
        private const val MIN_SCALE = 0.65f
        private const val MIN_ALPHA = 0.3f
    }

    private lateinit var pagerAdapterView: MyPagerAdapter
    private val uiHelper = UiHelper()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
        setContentView(R.layout.activity_main)
        pagerAdapterView = MyPagerAdapter(supportFragmentManager)
        addPagerFragments()
        myViewPager.adapter = pagerAdapterView
        myViewPager.setPageTransformer(true, this::zoomOutTransformation)
        getStartedButton.typeface = uiHelper.getTypeFace(TypeFaceEnum.BUTTON_TEXT, this)
        myViewPager.addOnPageChangeListener(ViewPagerListener(this::onPageSelected))
    }

    private fun onPageSelected(position: Int) {
        when (position) {
            0 -> {
                firstDotImageView.setImageResource(R.drawable.current_position_icon)
                secondDotImageView.setImageResource(R.drawable.disable_position_icon)
                thirdDotImageView.setImageResource(R.drawable.disable_position_icon)
            }
            1 -> {
                firstDotImageView.setImageResource(R.drawable.disable_position_icon)
                secondDotImageView.setImageResource(R.drawable.current_position_icon)
                thirdDotImageView.setImageResource(R.drawable.disable_position_icon)
            }
            2 -> {
                firstDotImageView.setImageResource(R.drawable.disable_position_icon)
                secondDotImageView.setImageResource(R.drawable.disable_position_icon)
                thirdDotImageView.setImageResource(R.drawable.current_position_icon)
            }
        }
    }

    private fun addPagerFragments() {
        pagerAdapterView.addFragments(IntroFirstFragment())
        pagerAdapterView.addFragments(IntroSecondFragment())
        pagerAdapterView.addFragments(IntroThirdFragment())
    }

    private fun zoomOutTransformation(page: View, position: Float) {
        when {
            position < -1 ->
                page.alpha = 0f
            position <= 1 -> {
                page.scaleX = Math.max(MIN_SCALE, 1 - Math.abs(position))
                page.scaleY = Math.max(MIN_SCALE, 1 - Math.abs(position))
                page.alpha = Math.max(MIN_ALPHA, 1 - Math.abs(position))
            }
            else -> page.alpha = 0f
        }
    }
}

Below is the MainActivity explanation.

  • onPageSelected: This method called every time when the ViewPager changes its page position and with that, we’re changing the ViewPagerIndicator dot icons.
  • addPagerFragments: In this function, we’re adding the pages of ViewPager means all the Fragment.
  • zoomOutTransformation: This method helps the ViewPager to animate the view or zoom out when swiping through the pages.
  • onCreate: In this method, we’re setting a FULL_SCREEN flag, creating a ViewPagerAdapter, setting page transformation for zoom out, and finally adding a ViewPager page change listener.

MyPagerAdapter

class MyPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {

    private var fragmentList: MutableList<Fragment> = ArrayList()

    override fun getItem(position: Int) = fragmentList[position]

    override fun getCount() = fragmentList.size

    fun addFragments(fragment: Fragment) = fragmentList.add(fragment)
}

The PagerAdapter class is the base class providing the adapter to populates pages inside of a ViewPager. It simply adds the fragments to the collection and represents each page as a Fragment.

IntroFirstFragment

Before start making the fragment UI. I wanna show you how the first-fragment design looks like.

first_intro_fragment_design

The UI is very basic, we have an ImageView, the image title text, and a description text. Below is the intro_first_fragment.xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".fragments.IntroFirstFragment">

    <ImageView
        android:id="@+id/notificationAlertsImageView"
        android:layout_width="200dp"
        android:layout_height="180dp"
        android:layout_centerInParent="true"
        android:contentDescription="@null"
        android:src="@drawable/notification_alerts_icon" />

    <TextView
        android:id="@+id/notificationAlertsTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/notificationAlertsImageView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="15dp"
        android:text="@string/notification_alerts"
        android:textColor="@color/colorTitleText"
        android:textSize="45sp" />

    <TextView
        android:id="@+id/notificationAlertsSubTitleTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/notificationAlertsTextView"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="25dp"
        android:gravity="center"
        android:text="@string/lorem_ipsum"
        android:textColor="@color/colorSubText"
        android:textSize="15sp" />

</RelativeLayout>

IntroFirstFragment Kotlin Class

class IntroFirstFragment : Fragment() {

    private val uiHelper = UiHelper()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = layoutInflater.inflate(R.layout.intro_first_fragment, container, false)
        view.findViewById<TextView>(R.id.notificationAlertsTextView).typeface = uiHelper.getTypeFace(TypeFaceEnum.HEADING_TYPEFACE, activity!!)
        view.findViewById<TextView>(R.id.notificationAlertsSubTitleTextView).typeface = uiHelper.getTypeFace(TypeFaceEnum.SEMI_TITLE_TYPEFACE, activity!!)
        return view
    }

}

In this fragment, we’re simply finding its views and setting Typeface to these views.

SecondIntroFragment

Below is the second-fragment design.


intro_second_fragment_design

In this fragment UI, we have the same ImageView, title, and subtitle texts. The following shows the intro_second_fragment.xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".fragments.IntroSecondFragment">

    <ImageView
        android:id="@+id/easyPaymentImageView"
        android:layout_width="200dp"
        android:layout_height="180dp"
        android:layout_centerInParent="true"
        android:contentDescription="@null"
        android:src="@drawable/easy_payment_icon" />

    <TextView
        android:id="@+id/easyPaymentTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/easyPaymentImageView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="15dp"
        android:text="@string/easy_payments"
        android:textColor="@color/colorTitleText"
        android:textSize="45sp" />

    <TextView
        android:id="@+id/easyPaymentSubTitleTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/easyPaymentTextView"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="25dp"
        android:gravity="center"
        android:text="@string/lorem_ipsum"
        android:textColor="@color/colorSubText"
        android:textSize="15sp" />

</RelativeLayout>

SecondIntroFragment Kotlin Class

class IntroSecondFragment : Fragment() {

    private val uiHelper = UiHelper()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = layoutInflater.inflate(R.layout.intro_second_fragment, container, false)
        view.findViewById<TextView>(R.id.easyPaymentTextView).typeface = uiHelper.getTypeFace(TypeFaceEnum.HEADING_TYPEFACE, activity!!)
        view.findViewById<TextView>(R.id.easyPaymentSubTitleTextView).typeface = uiHelper.getTypeFace(TypeFaceEnum.SEMI_TITLE_TYPEFACE, activity!!)
        return view
    }
}

In here we’re doing the same work which we have done in our previous fragment like finding and setting the Typeface of TextViews.

ThirdIntroFragment

The following shows the third-fragment UI screen.

intro_third_fragment_design

Below is the intro_third_fragment.xml file.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".fragments.IntroThirdFragment">

    <ImageView
        android:id="@+id/moneyRecordImageView"
        android:layout_width="200dp"
        android:layout_height="180dp"
        android:layout_centerInParent="true"
        android:contentDescription="@null"
        android:src="@drawable/money_record_icon" />

    <TextView
        android:id="@+id/moneyRecordTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/moneyRecordImageView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="15dp"
        android:text="@string/money_records"
        android:textColor="@color/colorTitleText"
        android:textSize="45sp" />

    <TextView
        android:id="@+id/moneyRecordSubTitleTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/moneyRecordTextView"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="25dp"
        android:gravity="center"
        android:text="@string/lorem_ipsum"
        android:textColor="@color/colorSubText"
        android:textSize="15sp" />

</RelativeLayout>

ThirdIntroFragment Kotlin Class

class IntroThirdFragment : Fragment() {

    private val uiHelper = UiHelper()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = layoutInflater.inflate(R.layout.intro_third_fragment, container, false)
        view.findViewById<TextView>(R.id.moneyRecordTextView).typeface = uiHelper.getTypeFace(TypeFaceEnum.HEADING_TYPEFACE, activity!!)
        view.findViewById<TextView>(R.id.moneyRecordSubTitleTextView).typeface = uiHelper.getTypeFace(TypeFaceEnum.SEMI_TITLE_TYPEFACE, activity!!)
        return view
    }
}

UiHelper

class UiHelper {

    /**
     * check which type is requested as an enum parameter and return the actual typeface.
     *
     * @param typeFaceEnum enum type like maybe RisingStar_Regular.otf
     * @param activity required to create assets.
     * @return returns a typeface which is requested by as an enum parameter.
     */

    fun getTypeFace(typeFaceEnum: TypeFaceEnum, activity: Activity): Typeface {
        return Typeface.createFromAsset(activity.assets, typeFaceEnum.getName())
    }
}

This is the helper class which provides the Typeface with the requested params. The createFromAsset is the static method creates a new typeface from the specified data.

TypeFaceEnum

enum class TypeFaceEnum(val value: String) {
    HEADING_TYPEFACE("fonts/RisingStar_Regular.otf"),
    SEMI_TITLE_TYPEFACE("fonts/SourceSansPro-Regular.otf"),
    BUTTON_TEXT("fonts/Ubuntu-M.ttf");

    fun getName() = value
}

One last thing we need to complete this app is the ViewPagerListener class.

ViewPagerListener

class ViewPagerListener(private val closure: (Int) -> Unit) : ViewPager.OnPageChangeListener {

    override fun onPageScrollStateChanged(p0: Int) {
    }

    override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {
    }

    override fun onPageSelected(position: Int) = closure(position)
}

This class implements the ViewPager.OnPageChangeListener class which response to changing the state of the selected page.

Alright, guys, that was all from this article. If you want the source code, images and icons of the above app get it from GitHub. If you’ve any queries regarding this article please do comment below.

Download Complete Code

Thank you for being here and keep reading…

The post Android ViewPager And ViewPagerIndicator Example appeared first on Coding Infinite.

]]>
Car Location Tracking Android App With Firebase Tutorial | Part 2 https://codinginfinite.com/location-tracking-app-firebase-android-part-2/ https://codinginfinite.com/location-tracking-app-firebase-android-part-2/#comments Tue, 28 Aug 2018 14:49:51 +0000 https://codinginfinite.com/?p=937 This is the second part of the location tracking tutorial. I hope you guy’s have seen my previous article in which we’ve developed the Driver App. In this tutorial, we’re going to develop the Passenger App. Here, we are not going to do the same work which we’ve done in the previous article like creating...

The post Car Location Tracking Android App With Firebase Tutorial | Part 2 appeared first on Coding Infinite.

]]>
This is the second part of the location tracking tutorial. I hope you guy’s have seen my previous article in which we’ve developed the Driver App.

In this tutorial, we’re going to develop the Passenger App. Here, we are not going to do the same work which we’ve done in the previous article like creating a Google Map API key or creating a Firebase project.

So, without wasting a time let’s dive into Android Studio and start making the Passenger App.

Passenger App

First, add the below dependencies to your app level build.gradle file.

implementation 'com.google.android.gms:play-services-location:15.0.1'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation 'com.google.firebase:firebase-database:16.0.1'

After adding the dependencies you need to sync or build your project. Later the successful build adds the Internet and Location permission to Android. Manifest file.

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

There’s one more thing we need to do, to show the Google Maps in the Android App. Add the below meta tags in Android. Manifest file.

<meta-data android:name="com.google.android.gms.version" 
           android:value="@integer/google_play_services_version" />
 <meta-data android:name="com.google.android.geo.API_KEY" 
            android:value="@string/map_api_key" />  // change it your Google Maps Api key or directly paste API key here.

Now in this app, we don’t need to create a new Firebase project. Below are the steps you need to do for add firebase in Passenger App.

  1. Sign In Firebase Console and open the previous project which you create when working with Driver App.
  2. Click on Settings Icon -> Project Settings.
  3. In General, tab click on Add app -> Add Firebase to your Android app.
  4. After adding the Android app to firebase project download the latest google-services.json file and add to the app directory of your Android app.

All the prep work being done regarding firebase and Google Maps. Now let’s start making Passenger app. Below is the UI of Passenger App that we’re gonna make.

Passenger App UI

You see the UI is very basic, we’ve got a Google Maps and a TextView which tells how many drivers are currently online. The following shows the code of the above UI.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/supportMap"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="spartons.com.frisbeeGo.fragments.MapFragment" />

    <TextView
        android:id="@+id/totalOnlineDrivers"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="30dp"
        android:layout_marginStart="30dp"
        android:layout_marginTop="40dp"
        android:background="#FFFFFF"
        android:gravity="center"
        android:padding="10dp"
        android:text="@string/total_online_drivers"
        android:textColor="#000000"
        android:textSize="15sp" />

</RelativeLayout>

MainActivity

class MainActivity : AppCompatActivity(), FirebaseDriverListener {

    companion object {
        private const val MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 6161
        private const val ONLINE_DRIVERS = "online_drivers"
    }

    private lateinit var googleMap: GoogleMap
    private lateinit var locationProviderClient: FusedLocationProviderClient
    private lateinit var locationRequest: LocationRequest
    private lateinit var locationCallback: LocationCallback
    private var locationFlag = true
    private lateinit var valueEventListener: FirebaseEventListenerHelper
    private val uiHelper = UiHelper()
    private val googleMapHelper = GoogleMapHelper()
    private val databaseReference = FirebaseDatabase.getInstance().reference.child(ONLINE_DRIVERS)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val mapFragment: SupportMapFragment = supportFragmentManager.findFragmentById(R.id.supportMap) as SupportMapFragment
        mapFragment.getMapAsync { googleMap = it }
        createLocationCallback()
        locationProviderClient = LocationServices.getFusedLocationProviderClient(this)
        locationRequest = uiHelper.getLocationRequest()
        if (!uiHelper.isPlayServicesAvailable(this)) {
            Toast.makeText(this, "Play Services did not installed!", Toast.LENGTH_SHORT).show()
            finish()
        } else requestLocationUpdate()
        valueEventListener = FirebaseEventListenerHelper(this)
        databaseReference.addChildEventListener(valueEventListener)
    }

    @SuppressLint("MissingPermission")
    private fun requestLocationUpdate() {
        if (!uiHelper.isHaveLocationPermission(this)) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION)
            return
        }
        if (uiHelper.isLocationProviderEnabled(this))
            uiHelper.showPositiveDialogWithListener(this, resources.getString(R.string.need_location), resources.getString(R.string.location_content), object : IPositiveNegativeListener {
                override fun onPositive() {
                    startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
                }
            }, "Turn On", false)
        locationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
    }

    private fun createLocationCallback() {
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                super.onLocationResult(locationResult)
                if (locationResult!!.lastLocation == null) return
                val latLng = LatLng(locationResult.lastLocation.latitude, locationResult.lastLocation.longitude)
                Log.e("Location", latLng.latitude.toString() + " , " + latLng.longitude)
                if (locationFlag) {
                    locationFlag = false
                    animateCamera(latLng)
                }
            }
        }
    }

    private fun animateCamera(latLng: LatLng) {
        val cameraUpdate = googleMapHelper.buildCameraUpdate(latLng)
        googleMap.animateCamera(cameraUpdate, 10, null)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION) {
            val value = grantResults[0]
            if (value == PackageManager.PERMISSION_DENIED) {
                Toast.makeText(this, "Location Permission denied", Toast.LENGTH_SHORT).show()
                finish()
            } else if (value == PackageManager.PERMISSION_GRANTED) requestLocationUpdate()
        }
    }

    override fun onDriverAdded(driver: Driver) {
        val markerOptions = googleMapHelper.getDriverMarkerOptions(LatLng(driver.lat, driver.lng))
        val marker = googleMap.addMarker(markerOptions)
        marker.tag = driver.driverId
        MarkerCollection.insertMarker(marker)
        totalOnlineDrivers.text = resources.getString(R.string.total_online_drivers).plus(" ").plus(MarkerCollection.allMarkers().size)
    }

    override fun onDriverRemoved(driver: Driver) {
        MarkerCollection.removeMarker(driver.driverId)
        totalOnlineDrivers.text = resources.getString(R.string.total_online_drivers).plus(" ").plus(MarkerCollection.allMarkers().size)
    }

    override fun onDriverUpdated(driver: Driver) {
        val marker = MarkerCollection.getMarker(driverId = driver.driverId)
        MarkerAnimationHelper.animateMarkerToGB(marker!!, LatLng(driver.lat, driver.lng), LatLngInterpolator.Spherical())
    }

    override fun onDestroy() {
        super.onDestroy()
        databaseReference.removeEventListener(valueEventListener)
        locationProviderClient.removeLocationUpdates(locationCallback)
        MarkerCollection.clearMarkers()
    }
}

The MainActivity got a bunch of important points which I’m going to explain below.

  • databaseReference: The DatabaseReference will be the online-drivers node because the passenger wants to track all drivers. Now if there is more than one driver then the passenger will be able to see all drivers on Google Map. If you want to listen to the specific driver then another child node will be added with the driverId.
  • onDriverAdded: This method will be called whenever new driver online in the firebase real-time database. When the new driver comes online we show the Marker to Google Maps and store the Marker in our MarkerCollection.
  • onDriverRemoved: This function will be called when the driver goes offline in the firebase real-time database. Later that we’ll remove the driver Marker from Google Maps and from MarkerCollection as well.
  • onDriverUpdated: This method will be called when the driver current position changed. Later that we get the driver Marker from MarkerCollection and animate the Marker to driver new position.
  • onDestroy: In this method, we’re removing firebase events, removing the current location updates and clearing the MarkerCollection.

Other methods of MainActivity are same as Driver App which I’ve explained in my previous article.

MarkerCollection

object MarkerCollection {

    private val markers: MutableList<Marker> = LinkedList()

    fun insertMarker(marker: Marker) = apply {
        markers.add(marker)
    }

    fun getMarker(driverId: String): Marker? {
        for (marker in markers)
            if (marker.tag == driverId) return marker
        return null
    }

    fun clearMarkers() = apply {
        markers.clear()
    }

    fun removeMarker(driverId: String) {
        val marker = getMarker(driverId)
        marker?.remove()
        if (marker != null) markers.remove(marker)
    }

    fun allMarkers() = markers
}

The following explains about MarkerCollection class.

  • insertMarker: Add the Marker instance to markers collection.
  • getMarker: When creating a Marker in MainActivity we set the driverId as a tag so, we search the Marker on the basis of Tag.
  • removeMarker: This method remove the specific driver Marker from the Google Maps when he goes offline.
  • clearMarker: Empty the Marker Collection.

FirebaseEventListenerHelper

class FirebaseEventListenerHelper(private val firebaseDriverListener: FirebaseDriverListener) : ChildEventListener {

    override fun onCancelled(p0: DatabaseError) {

    }

    override fun onChildMoved(p0: DataSnapshot, p1: String?) {

    }

    override fun onChildChanged(p0: DataSnapshot, p1: String?) {
        val driver = p0.getValue(Driver::class.java)
        firebaseDriverListener.onDriverUpdated(driver!!)
    }

    override fun onChildAdded(p0: DataSnapshot, p1: String?) {
        val driver = p0.getValue(Driver::class.java)
        firebaseDriverListener.onDriverAdded(driver!!)
    }

    override fun onChildRemoved(p0: DataSnapshot) {
        val driver = p0.getValue(Driver::class.java)
        firebaseDriverListener.onDriverRemoved(driver!!)
    }
}

This is a helper class for listening to the events in firebase real-time database. The FirebaseEventListenerHelper class implements ChildEventListener interface.

FirebaseDriverListener

interface FirebaseDriverListener {

    fun onDriverAdded(driver: Driver)

    fun onDriverRemoved(driver: Driver)

    fun onDriverUpdated(driver: Driver)
}

The GoogleMapHelper, UiHelper, MarkerAnimationHelper, Driver, IPositiveNegativeListener and LatLngInterpolator classes I’ve discussed these classes in my previous article.

Alright, guys, this is here we complete both of our apps. If you’ve any queries regarding this post please do comment below.

Download Complete Code

Thank you for being here and keep reading…

Here’re some more related articles:

Everything You Need To Know About Using Google Maps In Cab Booking App

5 Best Android Tracking Apps to Find It All

Building A Location Distance Tracking App | Android

Previous Part

The post Car Location Tracking Android App With Firebase Tutorial | Part 2 appeared first on Coding Infinite.

]]>
https://codinginfinite.com/location-tracking-app-firebase-android-part-2/feed/ 20