Dependency Injection with Kotlin Koin in Eventyay Attendee

Eventyay Attendee Android app contains a lot of shared components between classes that should be reused. Dependency Injection with Koin really comes in as a great problem solver.

Dependency Injection is a common design pattern used in various projects, especially with Android Development. In short, dependency injection helps to create/provide instances to the dependent class, and share it among other classes.

  • Why using Koin?
  • Process of setting up Koin in the application
  • Results
  • Conclusion
  • Resources

Let’s get into the details

WHY USING KOIN?

Before Koin, dependency injection in Android Development was mainly used with other support libraries like Dagger or Guice. Koin is a lightweight alternative that was developed for Kotlin developers. Here are some of the major things that Koin can do for your project:

  • Modularizing your project by declaring modules
  • Injecting class instances into Android classes
  • Injecting class instance by the constructor
  • Supporting with Android Architecture Component and Kotlin
  • Testing easily

SETTING UP KOIN IN THE ANDROID APPLICATION

Adding the dependencies to build.gradle

// Koin
implementation "org.koin:koin-android:$koin_version"
implementation "org.koin:koin-androidx-scope:$koin_version"
implementation "org.koin:koin-androidx-viewmodel:$koin_version"

Create a folder to manage all the dependent classes.

Inside this Modules class, we define modules and create “dependency” class instances/singletons that can be reused or injected. For Eventyay Attendee, we define 5 modules: commonModule, apiModule, viewModelModule, networkModule, databaseModule. This saves a lot of time as we can make changes like adding/removing/editing the dependency in one place.

Let’s take a look at what is inside some of the modules:

DatabaseModule

val databaseModule = module {

   single {
       Room.databaseBuilder(androidApplication(),
           OpenEventDatabase::class.java, "open_event_database")
           .fallbackToDestructiveMigration()
           .build()
   }

   factory {
       val database: OpenEventDatabase = get()
       database.eventDao()
   }

   factory {
       val database: OpenEventDatabase = get()
       database.sessionDao()
   }

CommonModule

val commonModule = module {
   single { Preference() }
   single { Network() }
   single { Resource() }
   factory { MutableConnectionLiveData() }
   factory<LocationService> { LocationServiceImpl(androidContext()) }
}

ApiModule

val apiModule = module {
   single {
       val retrofit: Retrofit = get()
       retrofit.create(EventApi::class.java)
   }
   single {
       val retrofit: Retrofit = get()
       retrofit.create(AuthApi::class.java)
   }

NetworkModule

single {
   val connectTimeout = 15 // 15s
   val readTimeout = 15 // 15s

   val builder = OkHttpClient().newBuilder()
       .connectTimeout(connectTimeout.toLong(), TimeUnit.SECONDS)
       .readTimeout(readTimeout.toLong(), TimeUnit.SECONDS)
       .addInterceptor(HostSelectionInterceptor(get()))
       .addInterceptor(RequestAuthenticator(get()))
       .addNetworkInterceptor(StethoInterceptor())

   if (BuildConfig.DEBUG) {
       val httpLoggingInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }
       builder.addInterceptor(httpLoggingInterceptor)
   }
   builder.build()
}

single {
   val baseUrl = BuildConfig.DEFAULT_BASE_URL
   val objectMapper: ObjectMapper = get()
   val onlineApiResourceConverter = ResourceConverter(
       objectMapper, Event::class.java, User::class.java,
       SignUp::class.java, Ticket::class.java, SocialLink::class.java, EventId::class.java,
       EventTopic::class.java, Attendee::class.java, TicketId::class.java, Order::class.java,
       AttendeeId::class.java, Charge::class.java, Paypal::class.java, ConfirmOrder::class.java,
       CustomForm::class.java, EventLocation::class.java, EventType::class.java,
       EventSubTopic::class.java, Feedback::class.java, Speaker::class.java, FavoriteEvent::class.java,
       Session::class.java, SessionType::class.java, MicroLocation::class.java, SpeakersCall::class.java,
       Sponsor::class.java, EventFAQ::class.java, Notification::class.java, Track::class.java,
       DiscountCode::class.java, Settings::class.java, Proposal::class.java)

   Retrofit.Builder()
       .client(get())
       .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
       .addConverterFactory(JSONAPIConverterFactory(onlineApiResourceConverter))
       .addConverterFactory(JacksonConverterFactory.create(objectMapper))
       .baseUrl(baseUrl)
       .build()
}

As described in the code, Koin support single for creating a singleton object, factory for creating a new instance every time an object is injected.

With all the modules created, it is really simple to get Koin running in the project with the function startKoin() and a few lines of code. We use it inside the application class:

startKoin {
   androidLogger()
   androidContext(this@OpenEventGeneral)
   modules(listOf(
       commonModule,
       apiModule,
       viewModelModule,
       networkModule,
       databaseModule
   ))
}

Injecting created instances defined in the modules can be used in two way, directly inside a constructor or injecting into Android classes.  

Here is an example of dependency injection to the constructor that we used for a ViewModel class and injecting that ViewModel class into the Fragment:

class EventsViewModel(
   private val eventService: EventService,
   private val preference: Preference,
   private val resource: Resource,
   private val mutableConnectionLiveData: MutableConnectionLiveData,
   private val config: PagedList.Config,
   private val authHolder: AuthHolder
) : ViewModel() {
class EventsFragment : Fragment(), BottomIconDoubleClick {
   private val eventsViewModel by viewModel<EventsViewModel>()
   private val startupViewModel by viewModel<StartupViewModel>()

For testing, it is also really easy with support library from Koin.

@Test
fun testDependencies() {
   koinApplication {
       androidContext(mock(Application::class.java))
       modules(listOf(commonModule, apiModule, databaseModule, networkModule, viewModelModule))
   }.checkModules()
}

CONCLUSION

Koin is really easy to use and integrate into Kotlin Android project. Apart from some of the basic functionalities mention above, Koin also supports other helpful features like Scoping or Logging with well-written documentation and examples. Even though it is only developed a short time ago, Koin has proved to be a great use in the Android community. So the more complicated your project is, the more likely it is that dependency injection with Koin will be a good idea.

RESOURCES 

Documentation: https://insert-koin.io/

Eventyay Attendee Android Codebase: https://github.com/fossasia/open-event-android

Continue ReadingDependency Injection with Kotlin Koin in Eventyay Attendee

Correct the API for Downloading GitHub Content in SUSI.AI android app

The content from github in the SUSI.AI android app is downloaded through simple links and the data is parsed through them and is used in the app depending upon what needs to be done with that data at that time.

A simple example for this that was used in the app was  :

private val imageLink = “https://raw.githubusercontent.com/fossasia/susi_skill_data/master/models/general/”

Above is the link that is used to download and display the images of the skills in the app. All the api calls that generally take place in SUSI are through the SUSI server, and making the call to display the images for the skills which takes place through the github links should be replaced by making the calls to the SUSI server instead, as this is a terrible programming style, with this style the project cannot be cloned from other developers and it cannot be moved to other repositories.

We see that there was a huge programming style issue present in the android app and hence, it was fixed by adding the API that calls the SUSI server for external source images and removing the existing implementation that downloads the image from github directly.

Below is an example of the link to the API call made in the app that was needed for the request to be made to the SUSI server :

${BaseUrl.SUSI_DEFAULT_BASE_URL}/cms/getImage.png?model=${skillData.model}&language=${skillData.language}&group=${skillData.group}&image=${skillData.image}

The link is displayed in the kotlin string interpolation manner, here is what the actual URL would look like :

https://api.susi.ai/cms/getImage.pngmodel=${skillData.model}&language=${skillData.language}&group=${skillData.group}&image=${skillData.image}

Here the values with ‘$’ symbol are the parameters for the API taken from the SkillData.kt file and are put inside the link so that the image needed can be extracted.

Now, since we use this link to set the images, to avoid the duplicate code, an Object class was made for this purpose. The object class contained two functions, one for setting the image and one for parsing the skilldata object and forming a URL out of it. Here is the code for the object class :

object Utils {

  fun setSkillsImage(skillData: SkillData, imageView: ImageView) {
      Picasso.with(imageView.context)
              .load(getImageLink(skillData))
              .error(R.drawable.ic_susi)
              .fit()
              .centerCrop()
              .into(imageView)
  }

  fun getImageLink(skillData: SkillData): String {
      val link = “${BaseUrl.SUSI_DEFAULT_BASE_URL}/cms/getImage.png?model=${skillData.model}&language=${skillData.language}&group=${skillData.group}&image=${skillData.image}”
              .replace(” “,“%20”)
      Timber.d(“SUSI URI” + link)
      return link
  }
}

setSkillsImage() method sets the image in the ImageView and the getImageLink() method returns the image formed out of the SkillData object.

References

 

Continue ReadingCorrect the API for Downloading GitHub Content in SUSI.AI android app

Handle response for API calls to the smart speaker

The SUSI.AI android app has the feature to connect to the smart speaker through the WiFi access point of the smart speaker. This connection takes place without internet and is completely offline and takes place locally between the device and the app.

When the APIs are hit, the parameters are sent to the server and are extracted by the speaker and then stored to perform further actions, but like any other API there is some response sent by the server once the parameters are fetched and only when this response is correctly parsed we show the confirmation that the data was required to be sent to the speaker is successfully done.

The SUSI.AI android app was unable to parse this response and so , every time there was a request made to the speaker the error message was displayed on the screen, just like this :

This is a cropped screenshot of the error message shown.

To handle the response from the local server there was a need to add the response parsing classes in the app, also since the app is written in kotlin the specialised classes for this purpose are already present in kotlin which are specifically for the purpose of handling responses. The data classes are added for the three APIs for the local server that are made, the response class added for the WiFi credentials response is :

data class SpeakerWifiResponse(
      val wifi: String,
      val wifi_ssid: String,
      val wifi_password: String
)

Here the first string by the name of ‘wifi’ is used to display a success message and other two variables are for the ssid and password of the wifi network respectively.

The API that is used to set the configuration of the speaker has the response class as follows  :

data class SpeakerConfigResponse(
      val configuration: String,
      val hotword: String,
      val stt: String,
      val tts: String,
      val wake: String
)

The third API which is used to send the auth credentials to the speaker has the following response class :

data class SpeakerAuthResponse(
      val auth: String,
      val authentication: String,
      val email: String,
      val password: String? = null
)

Now these response classes have the variable name similar to the key values in the JSON response generated by the local server. Due to the similar names to the keys an extra step and extra lines of code are eliminated which had to be added otherwise denoting the “Serializable” values to match the JSON response values.

After these data classes were added the Call<> functions of the retrofit service DeviceApi.java were made of the type of the types of the above data classes , which then were able to successfully parse the response from the local server of the speaker.

So, after this the app to speaker connection was successful and a success message was shown as :

References

 

Continue ReadingHandle response for API calls to the smart speaker

Ticket Details in the Open Event Android App

After entering all the attendee details and buying a ticket for an event the user expects to see the ticket so that he can use it later. This is why ticket details are shown in a separate fragment in the Open Event Android App. Let’s see how the tickets fragment was made in the Open Event Android App.

Two things that we require from the previous fragment are the event id and the order identifier so that we show the information related to the event as well as the order.

if (bundle != null) {
id = bundle.getLong(EVENT_ID, -1)
orderId = bundle.getString(ORDERS)
}

 

We are requesting data from the following two endpoints. In the first GET request we are passing the order identifier in the URL and we get the list of attendees from the server. In the second endpoint we simply pass the event identifier and get the event details from the server.

 

@GET("/v1/orders/{orderIdentifier}/attendees")
fun attendeesUnderOrder(@Path("orderIdentifier") orderIdentifier: String): Single<List<Attendee>>

@GET("/v1/events/{eventIdentifier}")
fun getEventFromApi(@Path("eventIdentifier") eventIdentifier: Long): Single<Event>

 

Here we are observing the attendees live data and adding the list of attendees returned from the server to the recyclerview so that we can show the user all the details of the attendees like the first name, last name etc. We then notify the adapter that the list of attendees have been added. In the end we log the number of attendees so that it is easier to debug in case there are any bugs.

orderDetailsViewModel.attendees.observe(this, Observer {
it?.let {
ordersRecyclerAdapter.addAll(it)
ordersRecyclerAdapter.notifyDataSetChanged()
}
Timber.d("Fetched attendees of size %s", ordersRecyclerAdapter.itemCount)
})

 

As mentioned earlier we need the event id and order identifier to show event and attendee related information to the user so here we are using the event id and appending it to the url. We are sending a GET request in a background thread and storing the list of events returned from the server in a mutable live data. In case of any errors we log it and show the error message to the user. Similarly we will use the order identifier to get the list of orders from the server and show it to the user.

compositeDisposable.add(eventService.getEventFromApi(id)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.value = it
}, {
Timber.e(it, "Error fetching event %d", id)
message.value = "Error fetching event"
}))

 

After fetching the list of attendees and event details, the only thing that we need to do is extract the important information and show it to the user so we pass the order and event objects to the ViewHolder. This can be done simply by using the attendee and event objects and accessing the fields required.

itemView.name.text = "${attendee.firstname} ${attendee.lastname}"
itemView.eventName.text = event?.name
itemView.date.text = "$formattedDate\n$formattedTime $timezone"

 

Resources

  1. ReactiveX official documentation: http://reactivex.io/
  2. Retrofit Android: http://square.github.io/retrofit/
  3. Google Android Developers Recycler View: https://developer.android.com/guide/topics/ui/layout/recyclerview
Continue ReadingTicket Details in the Open Event Android App

Attendee details in the Open Event Android App

To be able to create an order we first need to create an attendee with whom we can associate an order. Let’s see how in Open Event Android App we are creating an attendee.

We are loading the event details from our local database using the id variable. Since only logged in users can create an attendee, if the user is not logged in then the user is redirected to the login screen. If any errors are encountered while creating an attendee then they are shown in a toast message to the user. When the user clicks on the register button a POST request is sent to the server with the necessary details of the attendee. In the POST request we are passing an attendee object which has the id, first name, last name and email of the attendee. The ticket id and event id is also sent.

attendeeFragmentViewModel.loadEvent(id)

if (!attendeeFragmentViewModel.isLoggedIn()) {
redirectToLogin()
Toast.makeText(context, "You need to log in first!", Toast.LENGTH_LONG).show()
}

 

attendeeFragmentViewModel.message.observe(this, Observer {
Toast.makeText(context, it, Toast.LENGTH_LONG).show()
})

attendeeFragmentViewModel.progress.observe(this, Observer {
it?.let { Utils.showProgressBar(rootView.progressBarAttendee, it) }
})

 

attendeeFragmentViewModel.event.observe(this, Observer {
it?.let { loadEventDetails(it) }
})

rootView.register.setOnClickListener {
val attendee = Attendee(id = attendeeFragmentViewModel.getId(),
firstname = firstName.text.toString(),
lastname = lastName.text.toString(),
email = email.text.toString(),
ticket = ticketId,
event = eventId)

attendeeFragmentViewModel.createAttendee(attendee)

 

We are using a method called loadEvent in the above code which is defined in the View Model let’s have a look. We are throwing an IllegalStateException if the id is equal to -1 because this should never happen. Then we are fetching the event from the database in a background thread. If we face any errors while fetching the event we report it to the user

 

fun loadEvent(id: Long) {
if (id.equals(-1)) {
throw IllegalStateException("ID should never be -1")
}
compositeDisposable.add(eventService.getEvent(id)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
event.value = it
}, {
Timber.e(it, "Error fetching event %d", id)
message.value = "Error fetching event"
}))
}

 

This method is used to create an attendee. We are checking if the user has filled all the fields if any of the fields is empty a toast message is shown. Then we send a POST request to the server in a background thread. The progress bar starts loading as soon as the request is made and then finally when the attendee has been created successfully, the progress bar stops loading and a success message is shown to the user. If we face any errors while creating an attendee, an error message is shown to the user.

fun createAttendee(attendee: Attendee) {
if (attendee.email.isNullOrEmpty() || attendee.firstname.isNullOrEmpty() || attendee.lastname.isNullOrEmpty()) {
message.value = "Please fill all the fields"
return
}

compositeDisposable.add(attendeeService.postAttendee(attendee)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
progress.value = true
}.doFinally {
progress.value = false
}.subscribe({
message.value = "Attendee created successfully!"
Timber.d("Success!")
}, {
message.value = "Unable to create Attendee!"
Timber.d(it, "Failed")
}))
}

 

This function sends a POST request to the server and stores the attendee details in the local database.

fun postAttendee(attendee: Attendee): Single<Attendee> {
return attendeeApi.postAttendee(attendee)
.map {
attendeeDao.insertAttendee(it)
it
}

 

This is how the attendee details are inserted into the local database. In case of a conflict the attendee object gets replaced.

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAttendee(attendee: Attendee)

 

Resources

  1. ReactiveX official documentation : http://reactivex.io/
  2. Vogella RxJava 2 – Tutorial : http://www.vogella.com/tutorials/RxJava/article.html
  3. Androidhive RxJava Tutorial : https://www.androidhive.info/RxJava/
Continue ReadingAttendee details in the Open Event Android App

Handling No internet cases in Open Event Android

It’s pretty common to face connectivity issues and when the user has no Internet connection he should be shown an appropriate response rather than allowing him to send requests to the server. Let’s have a look how we are handling such cases in Open Event Android

Firstly we need to add the required permission in the manifest. We need the permission to access the user’s WiFi state and network state.

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

 

We use this function to check if the user is connected to the Internet. This function return a Boolean which is true if the user is connected to the Internet otherwise it is false

private fun isNetworkConnected(): Boolean {
val connectivityManager = context?.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager

return connectivityManager?.activeNetworkInfo != null
}

 

This function is used to decide which screen should be shown to the user. If the user has an active Internet connection he will see events fragment but if there is no Internet he will see the no Internet card.

private fun showNoInternetScreen(show: Boolean) {
rootView.homeScreenLL.visibility = if (show) View.VISIBLE else View.GONE
rootView.noInternetCard.visibility = if (!show) View.VISIBLE else View.GONE
}

 

Let’s see how the above two functions are used in the events fragment. When the app starts we check if there is a need to show the no Internet screen. If the user is not connected to the Internet, the no Internet card will be shown. Then when the user clicks on retry, the events fragment is shown again if the user is connected to the Internet.

showNoInternetScreen(isNetworkConnected())

rootView.retry.setOnClickListener {
showNoInternetScreen(isNetworkConnected())
}

 

Let’s have a look a how the XML code looks, here we are only seeing a part of the code as the rest is pretty obvious. We have cardView and inside it all the views ie ImageView,TextView are inside a LinearLayout which has a vertical orientation so that all these views appear below each other.

<android.support.v7.widget.CardView
android:id="@+id/noInternetCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_margin_medium"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="@dimen/card_corner_radius"
app:cardElevation="@dimen/card_elevation">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_margin_extra_large"
android:orientation="vertical">

<ImageView
android:id="@+id/noInternetImageView"
android:layout_width="@dimen/item_image_view_large"
android:layout_height="@dimen/item_image_view_large"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/layout_margin_large"
android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_no_internet" />

<TextView
android:id="@+id/noInternetTextview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/layout_margin_large"
android:text="@string/no_internet_message"
android:textSize="@dimen/text_size_medium"
tools:text="No Internet" />

 

References

  1. AndroidHive tutorial – https://www.androidhive.info/2012/07/android-detect-internet-connection-status/
  2. Official Android Documentation – https://developer.android.com/training/monitoring-device-state/connectivity-monitoring
  3. StackOverflow – https://stackoverflow.com/questions/9570237/android-check-internet-connection
Continue ReadingHandling No internet cases in Open Event Android

Shift from Java to Kotlin in SUSI Android

Previously SUSI Android was written in JAVA. But recently Google announced that it will officially support Kotlin on Android as a first class language so we decided to shift from Java to Kotlin. Kotlin runs on Java Virtual Machine and hence we can use Kotlin for Android app development. Kotlin is new programming language developed by JetBrains, which also developed IntelliJ-the IDE that Android Studio is based on. So Android Studio has excellent support for Kotlin.

Advantages of Kotlin over Java

  • Kotlin is a null safe language. It changes all the instances used in the code to non nullable type thus it ensures that the developers don’t get any nullPointerException.
  • Good Android Studio support. Kotlin tool is included with Android Studio 3.0 by default. But for Android Studio 2.x we need to install the required plugin for Kotlin.
  • Kotlin also provides support for Lambda function and other high order functions.
  • One of Kotlin’s greatest strengths as a potential alternative to Java is interoperability between Java and Kotlin. You can even have Java and Kotlin code existing side by side in the same project.
  • Kotlin provides the way to declare Extension function similar to that of C# and Gosu. We can use this function in the same way as we use the member functions of our class.

After seeing the above points it is now clear that Kotlin is much more effective than Java. In this blog post, I will show you how ForgotPasswordActivity is shifted from JAVA to Kotlin in SUSI Android.

How to install Kotlin plugin in Android Studio 2.x

We are using Android Studio 2.3.0 in our android project. To use Kotlin in Android Studio 2.x we need to install Kotlin plugin.

Steps to include Kotlin plugin in Android Studio 2.x are:

  • Go to File
  • After that select Settings
  • Then select Plugin Option from the left sidebar.
  • After that click on Browse repositories.
  • Search for Kotlin and install it.

Once it is installed successfully, restart Android Studio. Now configure Kotlin in Android Studio.

Shift  SUSI Android from JAVA to Kotlin

Kotlin is compatible with JAVA. Thus it allows us to change the code bit by bit instead of all at once. In SUSI Android app we implemented MVP architecture with Kotlin. We converted the code by one activity each time from JAVA to Kotlin. I converted the ForgotPassword of SUSI Android from JAVA to Kotlin with MVP architecture. I will discuss only shifting of SUSI Android from JAVA to Kotlin and not about MVP architecture implementation.

First thing is how to extend parent class. In JAVA we need to use extend keyword

public class ForgotPasswordActivity extends AppCompatActivity

but in Kotlin parent class is extended like

class ForgotPasswordActivity : AppCompatActivity ()

Second is no need to bind view in Kotlin. In JAVA we bind view using Butter Knife.

@BindView(R.id.forgot_email)

protected TextInputLayout email;

and

email.setError(getString(R.string.email_invalid_title));

but in Kotlin we can directly use view using id.

forgot_email.error = getString(R.string.email_invalid_title)

Another important thing is that instead of using setError and getError we can use only error for both purposes.

forgot_email.error

It can be used to get error in TextInputLayout ‘forgot_email’ and

forgot_email.error = getString(R.string.email_invalid_title)

can be use to set error in TextInputLayout ‘forgot_email’.

Third one is in Kotlin we don’t need to define variable type always

val notSuccessAlertboxHelper = AlertboxHelper(this@ForgotPasswordActivity, title, message, null, null, button, null, color)

but in JAVA we need to define it always.

AlertboxHelper notSuccessAlertboxHelper = new AlertboxHelper(ForgotPasswordActivity.this, title, message, null, null, positiveButtonText, null, colour);

And one of the most important feature of Kotlin null safety. So you don’t need to worry about NullPointerException. In java we need to check for null otherwise our app crash

if (url.getEditText() == null)

      throw new IllegalArgumentException(“No Edittext hosted!”);

But in Kotlin there is no need to check for null. You need to use ’?’ and Kotlin will handle it itself.  

input_url.editText?.text.toString()

You can find the previous ForgotPasswordActivity here. You can compare it with new ForgotPaswordActivity.

Reference

Continue ReadingShift from Java to Kotlin in SUSI Android

Introduction To Kotlin in SUSI Android App

Lately, we wrote some of the code of SUSI Android App in Kotlin. Kotlin is a very similar language to Java but with much more advantages than Java. It is easy to adapt and learn. There is no doubt that Kotlin is better than Java but with the announcement of Kotlin Support in Google IO’17 for Android development, Kotlin seems a decent way to write code for an Android App.

Advantages of Kotlin over Java

    1. Reduce Boilerplate Code: It helps making development of app faster as it reduces more than 20 percent of boilerplate code. Writing long statements again and again is a headache for developers. Kotlin comes to rescue in that situation.
    2. Removes Null Pointer Exception: Once a large company faced millions of dollars of loss due to null pointer exception. It causes crashes of apps more often than anything else. Thus Kotlin helps in Null checks and makes app free from Null pointer Exceptions.
    3. Interoperable with Java: Kotlin code and Java code are interoperable. Which means you can write half your code in kotlin and half in Java and it will work like a charm. You can call java methods from Kotlin code and vice versa. So, you can simply move your existing Java based app to Kotlin slowly making your app always running.
    4. Lambda and Inline functions: Yes, Kotlin also has functionalities from functional programming languages. Mainly and most widely used feature of those languages is Lambda functions.
    5. Direct Reference of Views by Id: You do not need to write findViewById(R.id.view_name) or use any other library like Butterknife for view binding. You can simply use the view by its id.
    6. No semicolon:  Last but not the least, you do not need to add a semicolon after each statement. In fact, you do not need to add semicolon at all.

Setting up Android Studio to work with Kotlin

If you have latest Android Studio Canary Version, there is already a build support for Kotlin in it. You need not do anything in that case. But if you don’t have the Canary version, you can add Kotlin Plugin in your Android Studio. Follow the below steps to do that.

  1. Install the Kotlin Plugin:

Android Studio → Preferences… →Plugins → Browse Repository → type “Kotlin” in search box → install

  1. Restart your Android Studio and Rebuild the project. Everything else is already set up in SUSI Android App but if you want to do it for your other apps, follow this link.

Implementation in SUSI Android App

So, I am not going to give unnecessary code but will point out specific things where Kotlin helped a lot to reduce unnecessary code and made the code compact.

1. Listeners:

Earlier with Java

Button signup = (Button) findViewById(R.id.sign_up);

signup.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               startActivity(new Intent(LoginActivity.this, SignUpActivity.class));
            }
        });

Now, with Kotlin

fun signUp() {
   sign_up.setOnClickListener { startActivity(Intent(this@LoginActivity, SignUpActivity::class.java)) }
}

2. Models

With Java

public class MapData {

    private double latitude;
    private double longitude;
    private double zoom;

    public MapData(double latitude, double longitude, double zoom) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.zoom = zoom;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public double getZoom() {
        return zoom;
    }

    public void setZoom(double zoom) {
        this.zoom = zoom;
    }
}

With Kotlin

class MapData (var latitude: Double, var longitude: Double, var zoom: Double) 

3. Constructor

With Java

public class LoginPresenter {
    private LoginActivity loginActivity;
    public LoginPresenter(loginActivity: LoginActivity){
        this.loginActivity = loginActivity;
    }
}

With Kotlin

class LoginPresenter(loginActivity: LoginActivity) {
}

Summary

So, this blog was to give you an idea about Kotlin programming language, it’s advantages over java and information how you can set it up on your Android Studio so as to help you a little in understanding the codebase of SUSI Android App a little more.

Resources

  1. Official Kotlin Guide for Syntax Reference and further learning  https://kotlinlang.org/docs/reference/
  2. Blog by Elye on Setting up Kotlin on Android Studio https://android.jlelse.eu/setup-kotlin-for-android-studio-1bffdf1362e8
  3. Youtube Video tutorial by Derek Banas on Kotlin https://www.youtube.com/watch?v=H_oGi8uuDpA
Continue ReadingIntroduction To Kotlin in SUSI Android App

Shifting from Java to Kotlin in SUSI Android

SUSI Android (https://github.com/fossasia/susi_android) is written in Java. After the announcement of Google to officially support Kotlin as a first class language for Android development we decided to shift to Kotlin as it is more robust and code friendly than Java.

Advantages of Kotlin over Java

  1. Kotlin is a null safe language. It changes all the instances used in the code to non nullable type thus it ensures that the developer don’t get any nullPointerException.
  2. Kotlin provides the way to declare Extensive function similar to that of C#. We can use this function in the same way as we use the member functions of our class.
  3. Kotlin also provides support for Lambda function and other high order functions.

For more details refer to this link.

After seeing the above points it is now clear that Kotlin is much more effective than Java and there is harm in switching the code from Java to Kotlin. Lets now see the implementation in Susi Android.

Implementation in Susi Android

In the Susi Android App we are implementing the MVP design with Kotlin. We are converting the code by one activity each time from java to Kotlin. The advantage here with Kotlin is that it is totally compatible with java at any time. Thus allowing the developer to change the code bit by bit instead of all at once.Let’s now look at SignUp Activity implementation in Susi Android.

The SignUpView interface contains all the function related to the view.

interface ISignUpView {


  fun alertSuccess()

  fun alertFailure()

  fun alertError(message: String)

  fun setErrorEmail()

  fun setErrorPass()

  fun setErrorConpass(msg: String)

  fun setErrorUrl()

  fun enableSignUp(bool: Boolean)

  fun clearField()

  fun showProgress()

  fun hideProgress()

  fun passwordInvalid()

  fun emptyEmailError()

  fun emptyPasswordError()

  fun emptyConPassError()


}

The SignUpActivity implements the view interface in the following way. The view is responsible for all the interaction of user with the UI elements of the app. It does not contain any business logic related to the app.

class SignUpActivity : AppCompatActivity(), ISignUpView {


  var signUpPresenter: ISignUpPresenter? = null

  var progressDialog: ProgressDialog? = null


  override fun onCreate(savedInstanceState: Bundle?) {

      super.onCreate(savedInstanceState)

      setContentView(R.layout.activity_sign_up)

      addListeners()

      setupPasswordWatcher()


      progressDialog = ProgressDialog(this@SignUpActivity)

      progressDialog?.setCancelable(false)

      progressDialog?.setMessage(this.getString(R.string.signing_up))


      signUpPresenter = SignUpPresenter()

      signUpPresenter?.onAttach(this)

  }


  fun addListeners() {

      showURL()

      hideURL()

      signUp()

  }


  override fun onOptionsItemSelected(item: MenuItem): Boolean {

      if (item.itemId == android.R.id.home) {

          finish()

          return true

      }

      return super.onOptionsItemSelected(item)

  }

Now we will see the implementation of models in Susi Android in Kotlin and compare it with Java.

Lets First see the implementation in Java

public class WebSearchModel extends RealmObject {

  private String url;

  private String headline;

  private String body;

  private String imageURL;


  public WebSearchModel() {

  }


  public WebSearchModel(String url, String headline, String body, String imageUrl) {

      this.url = url;

      this.headline = headline;

      this.body = body;

      this.imageURL = imageUrl;

  }


  public void setUrl(String url) {

      this.url = url;

  }


  public void setHeadline(String headline) {

      this.headline = headline;

  }


  public void setBody(String body) {

      this.body = body;

  }


  public void setImageURL(String imageURL) {

      this.imageURL = imageURL;

  }


  public String getUrl() {

      return url;

  }


  public String getHeadline() {

      return headline;

  }


  public String getBody() {

      return body;

  }


  public String getImageURL() {

      return imageURL;

  }

}
open class WebSearchModel : RealmObject {


  var url: String? = null


  var headline: String? = null


  var body: String? = null


  var imageURL: String? = null


  constructor() {}


  constructor(url: String, headline: String, body: String, imageUrl: String) {

      this.url = url

      this.headline = headline

      this.body = body

      this.imageURL = imageUrl

  }

}

You can yourself see the difference and how easily with the help of Kotlin we can reduce the code drastically.

For diving more into the code, we can refer to the GitHub repo of Susi Android (https://github.com/fossasia/susi_android).

Resources

Continue ReadingShifting from Java to Kotlin in SUSI Android

Porting Phimpme Android to Kotlin

As we are going ahead in Phimpme Project we are now on verge to start our account manager which deals with sharing images to many platforms right from the app. The account manager will take care of logging In the user. Saving it’s important credentials such access token, username etc as required by the API.

Google IO ‘17 just passed, and we seen tons of new features, APIs and development tools. One of the them is official support for Kotlin in Android Studio.

As stated by the developers at the conference that one best way to work on Kotlin is add today in your project. Because it is compatible with Java, we can work together on both languages in the same project. It is not mandatory for you to shift your entire code to Kotlin to build a project. So starting with the account manager we decided to bring this to our code. It helps in reducing the boilerplate code for example in Phimpme, I created a model for Realm database.

open class AccountDatabase(

       @PrimaryKey var name: String = "",

       var username: String = "",

       var token: String = ""

) : RealmObject()

That’s all I need to create a model class, no need to create getter and setter property

This helps me to get details from getter methods and show on Account Manager Recycler View like below.

Step 1 : Upgrade to Android Studio Preview 3.0

Android Studio Preview 3.0 comes up with all new features and Kotlin support. We all upgraded to that. It has a great Android Profiler with advance features for debugging and logcat is now moved separately. This step is not mandatory, you can work on older version of Android Studio as well.

Step 2 : Configure Kotlin

It’s easy in Android Studio Preview 3.0 to configure Kotlin. Go to Tools → Kotlin → Configure Kotlin in project.

What in the configuration

  • It added a classpath dependency in project level build.gradle file
classpath"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  • Added Kotlin plugin
apply plugin: 'kotlin-android'
  • Added kotlin std lib in app level build.gradle
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

Step 3: How to add Kotlin files

Now your project is ready for Kotlin. In Android Studio Preview 3.0 you can create new Kotlin files from the file menu.

Also by using Activity template you can select the source language as Java or Kotlin as per your preference.

Step 4 : Work with Kotlin

There are a lot new features in Kotlin to explore. Some of them are

  • Null Safety : In Kotlin, the type system distinguishes between references that can hold null (nullable references) and those that can not (non-null references). For example, a regular variable of type String cannot hold null.
var a: String = "abc"
a = null // compilation error

To allow nulls, we can declare a variable as nullable string, written String?:

 var b: String? = "abc"
 b = null // ok
  • Val and Var are two keywords in Kotlin to declare variables. Val gives you read only variable which is same as final modifier in Java, it is not changing. In other words it is immutable Data variables. Var is mutable data variable
  • Semicolons (;) are optional
  • No switch it’s when block in Kotlin. No need to write break and case: below is snippet from phimpme app
override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.action_add_account -> {
                val fragmentManager = fragmentManager
                val accountsPicker = AccountPickerFragment().newInstance("Accounts Picker")
                accountsPicker.show(fragmentManager, "Accounts Picker")
            }
            else -> return super.onOptionsItemSelected(item)
        }
        return true
    }


Source:

Continue ReadingPorting Phimpme Android to Kotlin