Android Kotlin Developer Nanodegree Projects Review

In this article, I'm going to share my experience in completing Android Kotlin Developer Nanodegree projects.

Android Kotlin Developer Nanodegree Projects Review

As mentioned in my previous article - Cheated by Android Kotlin Developer Nanodegree Course from Udacity, there are total of 5 projects that you need to complete in order to graduate from the Android Kotlin Developer Nanodegree. Since I have completed all the projects, I think it may worth for me to share my thoughts on them.

All the projects come with a starter code. You basically complete these projects on your own based on the guided instructions. The reviewer will pass or fail your project submission based on project rubric (known as project specifications) and also provide feedback on your code. You can submit the projects as many as you want.

Note: I'm going to share my completed code in the following sections. Please use it as your reference only and do not copy my code to graduate from the Nanodegree program! :)

Course 1 Project: Build a Shoe Store Inventory App

If you follow all the lessons (i.e. Build your First App, Layouts, App Navigation, Activity & Fragment Lifecycle, and App Architecture UI Layer) in this course, you shouldn't have any problem to complete this project. It is pretty straight forward except for listing screen (will be explained later).

There are a total of 5 screens here (i.e. login, welcome, instructions, listing, and detail screens). This is my version of designing the UI. It is up to you how you want to design it.

completed_android_kotlin_developer_nanodegree_projects_01.gif

The login screen is just a placeholder. It doesn't really function as an actual login screen. The purpose of this project is to demonstrate that you know how to navigate from one fragment to another.

What I learned?

  • ViewModel and Live Data (observe live data and setup click listener)

  • Single Activity Architecture (implement multiple fragments and navigate between them with transition animation)

  • SafeArgs (pass data between fragment using bundle)

  • Layout Design with sytles.xml (use ConstraintLayout and standardize the design style across different layouts)

  • Data binding in layouts (implement both 1-way and 2-way data binding - e.g. in EditView)

  • ScrollView (dynamically insert item into ScrollView during runtime)

  • Logout Menu (implement menu at the top left corner of the phone)

What I struggled?

Dynamic ScrollView

The hardest part in the project is the listing screen where you need to implement the ScrollView which has LinearLayout as child. Then, you need to manually insert the TextView into the LinearLayout. It is hard because it is not taught before in the lessons. I need to google this a bit to figure this out.

This is an example of ScrollView + LinearLayout in the layout xml:

<!-- scroll view and linear layout are used so to that it supports more shoe listings -->
<ScrollView
    android:id="@+id/scrollView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@+id/add_shoe_button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <LinearLayout
        android:id="@+id/shoe_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" />
</ScrollView>

I also created item_shoe.xml layout to allow me easily to inflate the layout so I did not need to create the TextView manually in code.

This is how my item_shoe.xml looks like:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textView"
    style="@style/DefaultTitle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

In the code, I inflate the item_shoe.xml layout, update the text and add textView into the LinearLayout.

private fun addShoe(shoeName: String) {
    val view = layoutInflater.inflate(R.layout.item_shoe, null)
    view.textView.text = shoeName
    binding.shoeList.addView(view.textView)
}

This is how I implemented the listing screen. It can be done in different way. Please also note that using RecycleView (which will be learned in next project) is more practical than ScrollView.

Course 2 Project: Build an Asteroid Radar

Lessons in this course is probably the most useful and practical one in my opinion. The lessons include App Architecture (Persistence), RecycleView, Connect to the Internet, Behind the Scenes, and Designing for Everyone.

completed_android_kotlin_developer_nanodegree_projects_02.gif

What I learned?

  • RecycleView (display list of asteroids)

  • Retrofit (download data from internet)

  • Moshi (convert JSON data to usable class data)

  • Picasso (download and cache images)

  • BindingAdapter(implement custom attribute in layout xml)

  • Room Database (implement offline caching)

  • Work Manager(schedule to update database every day)

  • Accessibility (provide basic accessibility such as content description for texts and images)

What I struggled?

Parse JSON Data

The starter code provided the parseAsteroidsJsonResult() function, but it did not explain why this is needed. This is because the Moshi library is unable to convert the JSON data to the class data due to the received JSON format complexity.

So, the Retrofit service API needs to return String instead of List<Asteroid>. Then, I convert the String to JSON object using JSONObject(). After that, I pass the JSON object to this helper function parseAsteroidsJsonResult() to convert it to List<Asteroid>.

See example below:

suspend fun getAsteroids() : List<Asteroid> {
    val responseStr = retrofitService.getAsteroids("","", NetworkConstants.API_KEY)
    val responseJsonObject = JSONObject(responseStr)

    return parseAsteroidsJsonResult(responseJsonObject)
}

Issue with Glide?

The project mentioned Glide has issue downloading the image, but it did NOT explain why. Thus, Picasso is used in the starter code

So, I tried to replace Picasso

Picasso.with(imageView.context)
    .load(it.url)
    .into(imageView)

with Glide

Glide.with(imageView.context)
    .load(it.url)
    .into(imageView)

and everything still worked as expected. So, I do not really understand why. I guess it is likely the issue is no longer valid anymore?

Honestly, I wish they explain why and gives more detailed information.

Tip: By the way for the Asteroid API, you don't need request an API key. You can use the DEMO_KEY directly and commit it directly to GitHub (which okay since this for demonstration purpose).

What will I do next? This is the useful feedback that I get from the reviewer.

You can now also use the KotlinX Serialization library which is in the stable release. This library supports JSON deserializing without Reflection based lookups and hence can save a lot of memory and time for large projects and also supports MultiPlatform. The syntax is fairly easy but very powerful when deserializing large JSON.

So this is going to be my to-do list to understand how this library works.

[Updated - Aug 06, 2022]: I finally tried KotlinX Serialization but the result is NOT impressive at all. The claim it saves memory and time is simply NOT true. See my following article:

Course 3 Project: Design an App with Application Loading Status

This course is all about notification and custom animation. The lessons include Using Notification, Creating Custom Views, Drawing on Canvas Objects, Clipping Canvas Objects, Android Property Animations, and Using MotionLayout to Animate Android Apps.

This is how the app looks like:

completed_android_kotlin_developer_nanodegree_projects_03.gif

What I learned?

  • Custom View (create custom view / button by inheriting View)

  • Custom Attributes (create custom attributes using withStyledAttributes())

  • ValueAnimator (use ValueAnimator and Paint to perform animations)

  • Canvas (use paint to draw the Canvas UI)

  • MotionLayout (use MotionLayout and MotionScene to perform animation)

  • Notification (send notification and use pendingIntent to create the notification action)

What I struggled? What I struggled is to make this learning stick. Maybe this the least used feature and I do not have chance to practice it? So, I write down some notes here for my future reference.

How to use ValueAnimator?

  • Create a ValueAnimator - define returned value and duration
 private val valueAnimator = ValueAnimator.ofInt(0, 360).setDuration(2000)
  • Set up the animation callbacks - progress is the callback value which is used to perform drawing animation
valueAnimator.apply {
    addUpdateListener {
        progress = it.animatedValue as Int
        invalidate()
    }
    repeatCount = ValueAnimator.INFINITE
    repeatMode = ValueAnimator.RESTART
}
  • Override onDraw() - use progress and paint to draw the canvas.
override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)

    // loading button
    paint.color = buttonLoadingColor
    canvas?.drawRect(0f, 0f, widthSize * progress/360f, heightSize.toFloat(), paint)
}

How to start MotionLayout without user interaction?

The course shows you how to trigger the animation but it doesn't show you how to runs the animation automatically. So I added this app:autoTransition="animateToEnd" in my MotionScene file to auto start the animation when fragment is created.

<Transition
    app:constraintSetStart="@+id/start"
    app:constraintSetEnd="@+id/end"
    app:duration="3000"
    app:autoTransition="animateToEnd">
</Transition>

Include layout is used

The <include> tag is used in all the layout xml files in the starter code which I wasn't really familiar with since this is not taught in the course too. However, it is not a big deal to understand it.

For example, you can see this in activity_main.xml file:

<include layout="@layout/content_main" />

What will I do next? Animation is my weak area but I probably won't focus in this area now. There are too many higher priority things to learn in the Android development.

Course 4 Project: Build a Location Reminder App

Note: Please read the README.md in the completed code. You need to setup the API Keys in order to run the app.

This is the worst project because the starter code has bunch of compilation errors. I attempted to fix but failed. I ended up just use the version provided by one of the mentors in the knowledge forum.

The lessons in this course include Wandering in Goolge Maps with Kotlin, Virtual Treasure Hunt with Geofences, Testing Basic, Intro to Test Double and Dependency Injection, Testing: Survey of Advanced Topics, and Implementing Login on Android with FirebaseUI.

This project use almost all the things that I learned in these lessons.

completed_android_kotlin_developer_nanodegree_projects_04.gif

What I learned?

  • Firebase (setup Firebase project, register app with Firebase, add sign-in provider for authentication, download google-services.json to app folder directory)

  • Google Map (enable Maps SDK for Android in Google Cloud Platform API & Services, setup API usage restriction to my app only, add SupportMapFragment in layout xml, implement OnMapReadyCallback interface)

  • Map Features (show current location, add marker, add POI, implement custom map style - map_style.json)

  • GeoFences (add geofencing request)

  • Location Permission (request ACCESS_FINE_LOCATION permission)

  • Device Location (auto resolve device location if disabled)

  • Unit Test (test ViewModel with fake data source)

  • Instrumentation Test (use Espresso and Mockito to test the app UI and Fragments Navigation)

    1. Expresso - library to interact with UI and check UI state

    2. Mockito - library to check whether method is called

  • Koin (use Koin to inject dependencies into repository)

What I struggled?

Only My Location Layer is needed to show Current Location?

In theory, device location is not required to be enabled to show the current location in Google Map. Only the My Location Layer needs to be enabled to do that.

I'm not able verify this scenario. In my emulator, the current location will be shown correctly only if I enable the device location. If I disable the device location, it doesn't work.

So, I posted the question to the mentor in the Udacity knowledge forum and he ran fine with my code in his emulator. So, I have no ideas why?

One thing I don't understand is when you try to run a "Google Map" in your phone, it does explicitly require user to enable device location. Maybe device location is needed after all?

Koin Dependency Injection

The test code uses Koin but the course doesn't teach you anything about Koin. The course only teaches you constructor depedency injection. To be honest, I tried to study the code and I did not 100% understand it.

What will I do next?

Custom Login Screen

Instead of the default login screens of FirebaseUI, we can customize them. I will need to learn how to customize it. The resource here is for my reference.

RequestPermission Contract

There are 2 ways to request permission. We can either manage the request code ourselves or use the RequestPermission contract (allow the system to manage the permission request code for you). The current code manages the request code ourselves.

Because using the RequestPermission contract simplifies the logic, it's recommended that we use it when possible. So I gave this a try, I implemented this RequestPermission Contract already in my Final Capstone project and it worked great!

Hilt & Dagger Depedency Injection

I am totaly not familar in this area. Instead of Koin (it seems a bit harder to understand), I will try to understand Hilt & Dagger first since there is pretty well official documentation here.

[Updated - Feb 06, 2022]: I have tried Hilt and documented the steps to implement it here.

Final Capstone Project: Design and Build an Android Application

Note: Please read the README.md in the completed code. It requires you to setup the API Key in order to run the app.

This is the final capstone project. You have 2 options. First option is implement a custom app. If you choose this option, you need to submit a design document. Second option is implement a Political Preparedness app.

I chose second option - Political Preparedness app. I renamed the app to US Election Info.

completed_android_kotlin_developer_nanodegree_projects_05.gif

What I learned? What I learned here are pretty much have already been covered in the previous courses. So there isn't any thing new here.

  • Fragment Navigation (implement navigate graph, implement Parcelable object with @Parcelize annotation to pass data between fragments)

  • MVVM Architecture (implement clean architecture using Repository acting as Model entry)

  • Motion Layout (hide the form while dragging up the RecycleView)

  • Retrofit (connect to and consume data from a remote data source such as a RESTful API)

  • Glide (load network resources, such as Bitmap Images, dynamically and on-demand)

  • Room Database (store data locally on the device for use between application sessions and/or offline use)

  • Location (request location permission and resolve device location setting if it is turned off)

[Updated - Apr 30, 2023]: For MVVM architecture, you can refer to the following article.

What I struggled?

Code does Not Compile

The code doesn't compile and has many errors. I found troublesome to fix them. So I decided to build the app from scratch and use the starter code as a reference.

In fact, this helped me a lot to understand the project. For example, I figured out mininum SDK 24 is required because of @color resource reference in ballot_logo.xml (which is only supported in SDK 24 and above). The starter code uses minimum SDK 26 which is technically unnecessary. It can be lower down to SDK 24.

I also reorganized the package / folder structure based on my own recommendation in my previous article below:

So, my folder / package structure looks like this:

completed_android_kotlin_developer_nanodegree_projects_06.JPG

Motion Layout does NOT really work as Intended

When dragging up the RecycleView, the form supposes to drag up too like the following and vice-versa.

completed_android_kotlin_developer_nanodegree_projects_07.gif

However, I took the short cut to just simply hide the form.

completed_android_kotlin_developer_nanodegree_projects_08.gif

This is how I did in in the motion scene file (simplified version) by using android:visibility="gone" attribute at the end ConstraintSet

<?xml version="1.0" encoding="utf-8"?>  
<MotionScene      
    xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:app="http://schemas.android.com/apk/res-auto">  

    <ConstraintSet android:id="@+id/start">  
        <Constraint android:id="@id/search_title" />  
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">  
        <Constraint  android:id="@id/search_title"  
        android:visibility="gone" />    
    </ConstraintSet> 

    <Transition  
        app:constraintSetStart="@id/start"  
        app:constraintSetEnd="@id/end">  

        <OnSwipe  app:dragDirection="dragUp"  
           app:touchAnchorId="@id/representatives_recycler_view" />     
    </Transition>  

</MotionScene>

What will I do next? I want to publish this app in Google Play store. In fact, I have already done it. You can check it out here!

[Updated - Jan 15, 2021]: I faced the app rejection issue (i.e. missing clear source of information) while I tried to publish. Here is what I needed to do to fix it.

Career Service

Once you have completed the 5 projects, you have one extracurricular, which consists of these 2 projects:

  • Take 30 Minutes to Improve your LinkedIn

  • Optimize Your GitHub Profile

There are some lessons in these 2 projects. At the end of the project, you're going to submit your LinkedIn / GitHub profile to the reviewer for feedback. You don't need to resubmit them. One submission is considered passed.

These projects are optional but you need to manually opt out. I gave it a try, and I'm going to share my views on them.

Most of the stuff here is common sense. So, I'm going to share what is new to me or something that I can improve on?

Take 30 Minutes to Improve your LinkedIn

What I learned?

  • Use present tense for current duties, past tense for prior duties and accomplishments. What I have before is all present tenses.

  • There is a project section on LinkedIn, which I wasn't aware previously. You need to manually add them into your profile.

What will I do next?

  • Fix wording to include the correct tense.

  • Update project section with my Android development projects

  • Reduce my skills section and put the most relevant one and use optimized keywords. I have too many of them which are not applicable anymore.

Feedback that I get from my reviewer is not very valuable because most of the stuff is pretty generic or already been covered in the lessons. What I learned above is mainly part of the course content. I did not learn any extra from the reviewer.

Optimize Your GitHub Profile

What I learned?

  • Having a great README is important. I probably knew this already but haven't got chance to improve it.

  • Commit graph that shows as many as green squares significant. Example of good commit graph:

    completed_android_kotlin_developer_nanodegree_projects_09.png

What will I do next?

  • Improve README in my GitHub, enroll to this free course from Udacity: Writting READMEs

  • Collaborate with others and contribution to open source - I'm not sure if I can do this. It seems a bit hard. However, this definitely will be my goal.

The reviewer gave me a lot of feedback. He even read my article in this blog and provided his opinion. A lot of resources have been shared by him and these resources are not part of the lesson which is good.

My Final Thoughts

  • The issue with all these started code projects is the code and libraries are outdated. So what I did the first thing, is updating the started code to use the latest libraries and fix all the warnings. It is basically all the update recommendations by Android Studio.

I managed to do this for all projects except for course 4 project. It has too many legacy codes which makes the upgrade almost impossible. You can try that, but it is going to waste your time.

  • I commit my initial working version into GitHub using Android Studio. If you don't know how to do this, you can refer to the following:

  • I always perform incremental commit for every successful run. This way when things go wrong, I can always rollback to my previous working version.

  • My suggestion is, don't look at the solution or completed code and work on the starter code directly. Then, you compare your solution with mine or other students. This way you learn more and make your knowledge stick!

  • If you get stuck, ask the mentor (if you enroll in this program).

  • Overall, the projects provided by Android Kotlin Developer Nanodegree are well-structured.

  • However, the course is quite expensive. Whether it is worth it or not, it is up to you. You can check out my article below.

I hope this review is useful.

Did you find this article valuable?

Support Vincent Tsen - AndroidDev Blog by becoming a sponsor. Any amount is appreciated!