Insider's Guide to Udacity Android Developer Nanodegree Part 7 - Full Stack Android
Written by Nikos Vaggalis   
Monday, 23 April 2018
Article Index
Insider's Guide to Udacity Android Developer Nanodegree Part 7 - Full Stack Android
Adapting to Android's Needs
HTTP Requests
Combating Memory Leaks
GSON Problems
Filtering and Comparing Devices
Functional Programming in Java
Conclusions

Adapting to Android's needs

As already said, the problem lies in the fact that the Android app cannot consume HTML.Well it could sort of through the use of a WebView, but the whole point of the Capstone was to develop a fully fledged native app. Still, if someone really wanted to make it work with HTML, but without hosting a WebView, he would have to scrape the data off the WebSite and consume it in a form that the Android app could use for populating its own UI's views. Much more difficult, error prone and un-maintainable.

The best option was for the Droplet to be amended in order to emit JSON, which in addition to easier parsing and consuming, would also open up the API to a potentially infinite number of client applications.But in order to emit JSON, the following had to be done:

New HTTP endpoints

  • /smartphones/json
  • /smartwatches/json
  • /tablets/json
  • /tablets1/smartphones/json
  • /tablets1/smartwatches/json
  • /tablets1/tablets/json

and then branch out in the Controller's code so that instead of returning HTML, return JSON

 

  
if ($json) { content_type('application/json'); return JSON::XS->new->allow_nonref->
encode($template_data); } else { return template 'tablets.tt' => $template_data; }

Unfortunately, naming the new endpoints revealed a problem created by a decision taken early on in the Website's design. Initially, the idea was to just host Tablet devices, hence the /tablets and /tablets1 endpoints. It wasn't until long after that it was decided to support Phones and Watches too and this explains  the naming of the new endpoints;'tablet' and 'tablet1' despite referring to Phones and Watches.

  • /tablets1/smartphones/json
  • /tablets1/smartwatches/json
  • /tablets1/tablets/json

Nevertheless, and here's where MVC's magic is cast, is that all the changes in the routing were constrained within the Controller's boundaries only. This is something that lead to better, cleaner and loosely coupled code.

 

The JSON endpoints in action

So a POST request to

http://smadeseek.com/smartphones/json

would emit

{
"brandhash": {
    "Oppo": [
      {
        "ModelVersion": "",
        "ModelName": "F1",
        "ModelId": "1013"
      },
      {
        "ModelName": "F1s",
        "ModelVersion": "",
        "ModelId": "1014"
      }
    ],
    "Samsung": [
      {
        "ModelId": "881",
        "ModelVersion": "",
        "ModelName": "Galaxy S7"
      },
      {
        "ModelName": "Galaxy Note7",
        "ModelVersion": "",
        "ModelId": "952"
      },
      {
        "ModelVersion": "",
        "ModelName": "Galaxy A9 Pro (2016)",
        "ModelId": "1015"
      }
],
"oslist": [
    {
      "OSName": "Android  4.3",
      "OSId": "16"
    },
    {
      "OSName": "Android  4.4",
      "OSId": "17"
    },
    {
      "OSName": "Android  5.0",
      "OSId": "20"
    },
    {
      "OSName": "Android  5.0.2",
      "OSId": "22"
    },
 ...
 ...
 ...
} 

which the Android app consumes in order to fill the Filter screen's (FragmentFilterDevices) respective menus.

 

Filter screen (FragmentFilterDevices)

A POST request to: 

http://smadeseek.com/tablets1/smartphones/json

 would emit the list of all smartphones in the database:

{
  "tablets": [
    {
      "ModelName": "27",
      "ModelCPUClock": null,
      "ModelStorageInternal": null,
      "OSVersion": "Android -4.4",
      "ModelCPUBit": null,
      "ModelScreenSize": "1.22",
      "BrandName": "Burg",
      "CPUTypeName": "None",
      "ModelRAM": "0.500",
      "ImagesPath": "28-burg27-banner-01.jpg",
      "coresQuantity": "0",
      "CPUModel": "None",
      "ModelId": "28",
      "ModelVersion": null
    },
    {
      "ModelName": "360",
      "ModelStorageInternal": "4.000",
      "OSVersion": "Android-Wear 1.3",
      "ModelCPUBit": null,
      "ModelCPUClock": "1.2",
      "ImagesPath": 
"781-moto-360-product4n4pjkl0.jpg", "coresQuantity": "2", "CPUModel": "400", "BrandName": "Motorola", "ModelScreenSize": "1.56", "CPUTypeName": "Snapdragon", "ModelRAM": "0.512", "ModelId": "781", "ModelVersion": "(2nd. Gen.)" }, { "ModelName": "A28", "OSVersion": null, "ModelCPUBit": null, "ModelStorageInternal": null, "ModelCPUClock": null, "CPUModel": "2502", "coresQuantity": "1", "ImagesPath": "772-2015070618022938.jpg", "ModelRAM": null, "CPUTypeName": "MT", "ModelScreenSize": null, "BrandName": "Oukitel", "ModelVersion": null, "ModelId": "772" }, ... ... ... }

The Android app consumes this list inside the Listing of Devices screen (FragmentListDevices), mapping each individual device to a RecyclerView element.

 

Listing screen (FragmentListDevices)

 

RecyclerView item

 

If the user then picks a device from the RecyclerView, a GET request to

 http://smadeseek.com/tabletdetails/json?id=858

is fired with the device's (model) id appended to it. This in turn retrieves that device's detailed specifications:

{
  "BrandName": "Xiaomi",
  "CPUBrandName": "Qualcomm",
  "OSVersionWithStatus": null,
  "LookUpDescription": null,
  "ModelSarUS": null,
  "CPUModel": "200",
  "ModelVersion": "",
  "CPUTypeName": "Snapdragon",
  "ImageDetails": [
    "detail#858-1.jpg",
    "promo#858-3.jpg"
  ],
  ...
  ...
  ...
}

which are presented inside the Specs screen (PageFragment).

The Specs screen(PageFragment) uses a Tablayout to present all the device's specs with its promo images under one tab, also giving the opportunity to examine each individual category in its own tab.

 

Specs screen (PageFragment) Tab 1

 

Specs screen (PageFragment) Tab2

 

Specs screen (PageFragment) Tab5



Last Updated ( Monday, 23 April 2018 )