Google Maps and Directions API using Kotlin

Google Maps and Directions API using Kotlin

I will implement below on how to implement Google Maps and Directions API using Kotlin and we are going to plot a route from origin to destination. I am not gonna include here on how to get the API Key from the Google Maps Platform Console as Google has a clear directions about it.

We are going to assume that you have already obtained the API Key and already added the <meta-data> in the AndroidManifest.xml.

In your gradle file, add the following lines below and then sync after.

implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation 'com.google.maps.android:android-maps-utils:0.5'

In your XML layout add the following code anywhere you want. On my end I added it below the toolbar.

<fragment
    android:id="@+id/fragment_map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/toolbar"
    />

Then in your Activity, we need to implement OnMapReadyCallback. Your activity should look like the one below.

class HomeActivity: AppCompatActivity(), OnMapReadyCallback {

private var googleMap: GoogleMap? = null

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)
        val mapFragment = supportFragmentManager.findFragmentById(R.id.fragment_map) as SupportMapFragment
        mapFragment.getMapAsync(this)
    }

override fun onMapReady(googleMap: GoogleMap?) {
        this.googleMap = googleMap
    }
}

Run your project and it should display the Google Map.

Next we are going to plot a route from origin to destination. In your override method of onMapReady add the following lines below.

val latLngOrigin = LatLng(10.3181466, 123.9029382) // Ayala
val latLngDestination = LatLng(10.311795,123.915864) // SM City
      this.googleMap!!.addMarker(MarkerOptions().position(latLngOrigin).title("Ayala"))
        this.googleMap!!.addMarker(MarkerOptions().position(latLngDestination).title("SM City"))
       this.googleMap!!.moveCamera(CameraUpdateFactory.newLatLngZoom(latLngOrigin, 14.5f))

The following code block we created a variable latLngOrigin as this will be the origin or starting point of our route. The variable latLngDestination will be the destination or the end of the route. Next lines is adding the following variables as markers to the Google Map.

Directions API

We are going to use Google’s Directions API so that we can calculate the route between two endpoints. Try to read and get familiar with the documentation.

Since we only have the coordinates, we can pass them to the Directions API like the one below. Replace your own API Key.

https://maps.googleapis.com/maps/api/directions/json?origin=10.3181466,123.9029382&destination=10.311795,123.915864&key=<YOUR_API_KEY>

The sample JSON response can be found in the documentation. So take a look at it.

Take note I also used Volley as my HTTP request library.

val path: MutableList<List<LatLng>> = ArrayList()
val urlDirections = "https://maps.googleapis.com/maps/api/directions/json?origin=10.3181466,123.9029382&destination=10.311795,123.915864&key=<YOUR_API_KEY>"
val directionsRequest = object : StringRequest(Request.Method.GET, urlDirections, Response.Listener<String> {
            response ->
            val jsonResponse = JSONObject(response)
            // Get routes
            val routes = jsonResponse.getJSONArray("routes")
            val legs = routes.getJSONObject(0).getJSONArray("legs")
            val steps = legs.getJSONObject(0).getJSONArray("steps")
            for (i in 0 until steps.length()) {
                val points = steps.getJSONObject(i).getJSONObject("polyline").getString("points")
                path.add(PolyUtil.decode(points))
            }
            for (i in 0 until path.size) {
                this.googleMap!!.addPolyline(PolylineOptions().addAll(path[i]).color(Color.RED))
            }
        }, Response.ErrorListener {
            _ ->
        }){}
 val requestQueue = Volley.newRequestQueue(this)
 requestQueue.add(directionsRequest)

The following code block sends a HTTP GET request to the Directions API and we are going to parse the response. What we are going to need is the points data from the polyline object. Run the project and you should get a route plotted on your Google Map.

Google Maps and Directions API using Kotlin

Implement Chromecast on iOS using Swift

I am going to implement Google’s Chromecast on iOS using Swift.
I will be using Google Chromecast 2 device and Xcode 9.4.1.

First let’s read the Google Cast documentation to get familiar with it’s implementation. After getting familiar with the Get Started section, we need to register the Google Chromecast device. There will be a one time $5 fee for Google Cast Developer Registration. After paying, we will be redirected to Google Cast SDK Developer Console

Setup for development

Assuming you already have installed the Google Home app or the Chrome browser extension, if not please do. After that we will register the Chromecast device. Click on Add New Application. For now I will choose Custom Receiver and fill up the necessary informations. 

Next, under the Cast Receiver Devices click on Add New Device and enter your Chromecast’s Serial Number and your own description. After saving the device, it should indicate in the status that it is Registering. Take note that it will take several minutes to register the device.

Sender Application

Now we will create the sender application for Chromecast. Assuming you already have setup your Xcode project using Cocoapods, if not please do. Sender app refers to our mobile device or laptop which will handle the playback.

Open your Podfile and add the following line below and then type pod install on your terminal.
pod ‘google-cast-sdk’, ‘4.3.1’

Integrate CAF (Cast Application Framework)

Open AppDelegate.swift and inside the method didFinishLaunchingWithOptions add the following lines below

import UIKit
import GoogleCast

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    private let appId = "0FFF55BD"

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        // Initialize Google Cast SDK
        let discoveryCriteria = GCKDiscoveryCriteria(applicationID: appId)
        let castOptions = GCKCastOptions(discoveryCriteria: discoveryCriteria)
        GCKCastContext.setSharedInstanceWith(castOptions)
        GCKLogger.sharedInstance().delegate = self
        return true
    }

extension AppDelegate: GCKLoggerDelegate {

    func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) {
        print("Message from Chromecast = \(message)")
    }
}

Take note of the appId variable, that is the Application ID when you registered your application in the Google Cast SDK Developer Console.

Let’s add a Cast button in our navigation bar. Open ViewController.swiftand add the following code block.

import UIKit
import GoogleCast

class HomeViewController: UIViewController {

    @IBOutlet weak var navItem: UINavigationItem!

    override func viewDidLoad() {
        super.viewDidLoad()
        let castButton = GCKUICastButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
        castButton.tintColor = UIColor.red
        let castBarButtonItem = UIBarButtonItem(customView: castButton)
        navigationItem.rightBarButtonItem = castBarButtonItem
    }
}

Build and run. It should have a red Chromecast button appear on the right side of the navigation bar.


Tapping on the Cast button should display a ViewController that lists the Chromecast devices that were found.


If at first the Cast button will not display on the navigation bar, try to turn on your Chromecast device. Based on my testing, the Cast button will not appear if there are no Chromecast devices found.

I used the value https://lawgimenez.me as the value for Receiver Application URL. When everything is successful, the Chromecast should be able to cast my website to my TV. When I tap on Gimenez’s Room TV, it should display the contents of my URL. I both tested this implementation on both real device and emulator.

IMG_0714

G Suite Happy Ending

Somehow, I got Google’s attention.

A representative of Google (I am not sure if I can mention his name here, but I would love to, let me know) contacted me on LinkedIn and that he would like to help out on my G Suite problem. This is nice, straight from Mountain View.

Minutes later, someone from their APAC office called me to check up on my situation and I run down the events that happened. Their representative invited me on a calendar to a Hangout Meet, I shared my screen, logged in to my account, then figuring out and debugging. Unfortunately, there’s not much that they can do about it. And that he will talk to his managers in Mountain View.

After a few minutes, Google rep from LinkedIn called me personally and told me they were very sorry, and they will make sure this will never happen again to me or anyone. And he also said he will escalate this to the managers. That was comforting for him to call me.

A few minutes before 6PM (local time), a Google Engineer called me and that they are trying to check if they can recover my files, my emails, etc. He instructed me to cancel my new G Suite account again so they can proceed. There’s hope.

Hours after, I received a couple of emails from Google’s Technical Solutions Engineers and updating me of the status of the recovery, telling me that they should be able to recover my upgraded personal/business email along with all my data.

As of today, I was able to retrieve all my data, emails, files, docs, sheets, photos, etc.

I would like to thank Google for the effort and time to bring back my data, files and email. I am not sure if everyone on the Help Forum has the same recovery process I encountered in this post but I hope they do. I know I am not the only one who encountered this “upgrade” based on the forums, I wish Google will highlight on the signing up process that there is no way going back to your personal once you “unlock”.

Anyway, many thanks to Google and their managers, engineers who went out of their way to help me. Now it’s time to backup some data.

G Suite Horror Story

Yesterday my life long Gmail account has been deleted by Google because I integrated it with G Suite by cancelling the subscription.

G Suite for business was a mean to upgrade your personal email so that all your inbox will be unified. But the more email gets into the inbox, the more cluttered it seems. It was hard to manage.

Then, I searched for any way to break apart my personal Gmail from my G Suite account. It was complicated. This is not my first time using G Suite, previous account I just cancelled and move on with my personal Gmail. So I cancelled my subscription. And after that I can’t access my personal Gmail. Things starting to turn for the worse.

I searched for solutions, it turned out I had “unlock” some features that I upgraded my personal account into a business account and there was no coming back. I had setup that G Suite months ago and I can’t remember any “unlock” features prompting me.

As I kept digging Google’s Help Forums, it turns out I am not the only one in this predicament. Below are examples. Take notice on Google’s Customer Support’s passive solutions.

Canceled my G suite account and my @gmail.com is no longer working

Another one, I cancelled g-suite trial but now can’t access my email. In that thread, Google’s Customer Support replied with

If you cancel and delete Gsuite this will have no effects on your personal Gmail account there is not no way to restore a deleted the Gsuite account

So you just need sign into your personal Gmail account.to access your mail and contacts

Whew! That was reassuring. But when you login back to your Gmail account, me and this user has the same problem. It will display a server error

Server error

Sorry, you’ve reached a login page for a domain that isn’t using G Suite. Please check the web address and try again.

Shit. My Gmail account is using G Suite. I opened a ticket to Google’s Help Forum. Cancel GSuite subscription and now cannot login

So, the thing is I setup a business email powered by G Suite instead of another G Suite? Confusing. Their setup and landing page looks the same. Anyway, I looked into their Cancel your business email documentation and it mentioned

After you cancel, you can no longer send or receive mail from your business email address (for example, you@yourcompany.com); you return to using free, basic Gmail.

You return using free, basic Gmail. But how come I can’t access my personal Gmail account anymore? I’ve used that email for over 8 years, all my history is gone. All the Google Drive files, the Google Docs and Sheets of important data and documents.

To anyone using Business G Suite just make sure that you’ve “downgrade” instead of cancelling your subscription. This is very sad on my part, a hard lesson to learn. I should have read carefully the details. To Google, I lost trust in your service.

This post went viral on Hacker News back in 2018.

Implementing the expandable cell in iOS UITableView

I’m going to implement below on how to create the expandable UITableView cell for example on iOS stock calendar.

1-K0Xsn4n9Lx2lqpUDNO-xXA

From scratch, create a new project on Xcode. In your main.storyboard remove the default ViewController (delete also the extra unused ViewController.swift file in the project explorer) and drag a new TableViewController.

1-ti0Kq9gWuhSdxOPXvRjDDw

Now that we have our TableViewController set on the storyboard. Let’s create a new Swift file, and call it for example FormTableViewController.swift. And set is as the custom class in your TableViewController.

1-XPDBMIMfwL7hBdvehkUnjQ
1-366I_Aw3939Z5uXtdVX0fg

Now we are all set. We are gonna change the TableView’s Content from Dynamic Prototypes to Static Cells.

After setting it to Static Cells, your TableView will have a default of 3 static cells created.

For this demo it won’t matter how many cells we are going to use.

The secret to this is the proper measurement of a cell. Click on the first cell and change the row height to 250. Our plan is, inside the cell we will have 2 views. The Label which has a height of 50 and DatePicker with a height of 200. A total of 250. On the storyboard, drag a Label and DatePicker on the first cell, setup its constraints and embed our TableViewController with a NavigationController, it should look like the image below.

1-VOvLjMRH2f8Gyu7LmfFvYA

Now let’s start coding. Inside your FormTableViewController you need to override two functions, namely TableView’s didSelectRowAt and heightForRowAt.

import UIKit

class FormTableViewController: UITableViewController {

    private var dateCellExpanded: Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // For removing the extra empty spaces of TableView below
        tableView.tableFooterView = UIView()
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row == 0 {
            if dateCellExpanded {
                dateCellExpanded = false
            } else {
                dateCellExpanded = true
            }
            tableView.beginUpdates()
            tableView.endUpdates()
        }
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.row == 0 {
            if dateCellExpanded {
                return 250
            } else {
                return 50
            }
        }
        return 50
    }
}


I’m going to explain shortly about the code. The variable dateCellExpanded is a flag to determine if the current cell selected has been expanded or not. Then, after always call tableView.beginUpdates() and tableView.endUpdates() and it will trigger the delegate function of TableView heightForRowAt to determine what height value should we return or not. Yep, it is that simple. Click RUN!

1-_TMKiaY6yEn4iGw2w_vdvQ

Hopefully this demo should give you an idea on how to implement it on your app.

The example project can be found at https://github.com/lawgimenez/expandable-cell-test