When to use function vs property in Swift

Issue #687 Although I do Swift, I often follow Kotlin guideline https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties In some cases functions with no arguments might be interchangeable with read-only properties. Although the semantics are similar, there are some stylistic conventions on when to prefer one to another. Prefer a property over a function when the underlying algorithm: does not throw is cheap to calculate (or cached on the first run) returns the same result over invocations if the object state hasn鈥檛 changed Updated at 2020-10-27 09:56:38...

October 27, 2020 路 1 min 路 Khoa Pham

How to use synthetic property in Kotlin Android Extension

Issue #555 Synthetic properties generated by Kotlin Android Extensions plugin needs a view for Fragment/Activity to be set before hand. In your case, for Fragment, you need to use view.btn_K in onViewCreated override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) val view = inflater.inflate(R.layout.fragment_card_selector, container, false) view.btn_K.setOnClickListener{} // access with `view` return view } Or better, you should only access synthetic properties in onViewCreated...

December 23, 2019 路 1 min 路 Khoa Pham

How to access view in fragment in Kotlin

Issue #497 Synthetic properties generated by Kotlin Android Extensions plugin needs a view for Fragment/Activity to be set before hand. In your case, for Fragment, you need to use view.btn_K in onViewCreated override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) val view = inflater.inflate(R.layout.fragment_card_selector, container, false) view.btn_K.setOnClickListener{} // access with `view` return view } Or better, you should only access synthetic properties in onViewCreated...

November 10, 2019 路 1 min 路 Khoa Pham

How to add AdMob to Android app

Issue #431 Use AdMob with Firebase https://firebase.google.com/docs/admob/android/quick-start build.gradle buildscript { repositories { google() jcenter() } dependencies { classpath 'com.google.gms:google-services:4.3.2' } } app/build.gradle class Version { class Firebase { static def analytics = "17.2.0" static def ads = "18.2.0" } } dependencies { implementation "com.google.firebase:firebase-analytics:$Version.Firebase.analytics" implementation "com.google.firebase:firebase-ads:$Version.Firebase.ads" } apply plugin: 'com.google.gms.google-services' Manifest.xml <manifest> <application> <!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 --> <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="[ADMOB_APP_ID]"/> </application> </manifest> MyApplication.kt class MyApplication: Application() { override fun onCreate() { super....

September 27, 2019 路 1 min 路 Khoa Pham

How to use flow in Kotlin

Issue #388 Asynchronous Flow https://kotlinlang.org/docs/reference/coroutines/flow.html Using List result type we can only return all the values at once. To represent the stream of values that are being asynchronously computed we can use Flow type similarly to the Sequence type for synchronously computed values: ...

September 3, 2019 路 1 min 路 Khoa Pham

How to create bounce animation programmatically in Android

Issue #383 Right click res -> New -> Android Resource Directory, select anim and name it anim Right click res/anim -> New -> Android Resource file, name it bounce <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromYDelta="0" android:toYDelta="-100" android:repeatCount="infinite" /> </set> We have to set repeatCount in xml, setting in code does not work !! val bounce = AnimationUtils.loadAnimation(context, R.anim.bounce) bounce.repeatMode = Animation.REVERSE bounce.duration = (1000..2000).random().toLong() imageView.startAnimation(bounce) ...

August 28, 2019 路 1 min 路 Khoa Pham

How to use point in dp programmatically in Android

Issue #382 import android.content.Context fun Int.toDp(context: Context?): Int { if (context != null) { val scale = context.resources.displayMetrics.density return (this.toFloat() * scale + 0.5f).toInt() } else { return 0 } } val set = ConstraintSet() set.setMargin(imageView.id, ConstraintSet.RIGHT, rightMargin.toDp(150)) Read more https://stackoverflow.com/questions/5255184/android-and-setting-width-and-height-programmatically-in-dp-units ...

August 28, 2019 路 1 min 路 Khoa Pham

How to create constraints programmatically with ConstraintLayout in Android

Issue #381 From API < 17, there is ViewCompat.generateViewId() For API 17, there is View.generateViewId() Note that to use ConstraintSet, all views under ConstraintLayout inside xml must have unique id val imageView = ImageView(context) imageView.id = View.generateViewId() imageView.setImageResource(resId) constraintLayout.addView(imageView) val set = ConstraintSet() set.clone(constraintLayout) set.connect(imageView.id, ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT) set.applyTo(constraintLayout) ...

August 28, 2019 路 1 min 路 Khoa Pham

How to use custom font as resource in Android

Issue #380 Downloadable fonts https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts Android 8.0 (API level 26) and Android Support Library 26 introduce support for APIs to request fonts from a provider application instead of bundling files into the APK or letting the APK download fonts. The feature is available on devices running Android API versions 14 and higher through the Support Library 26 Before Select File -> New -> Folder -> Assets Folder to create src/main/assets/fonts al myTypeface = Typeface....

August 28, 2019 路 1 min 路 Khoa Pham

How to get Hacker News top stories using parallel coroutine and Retrofit

Issue #379 interface Api { @GET("topstories.json?print=pretty") suspend fun getTopStories(): List<Int> @GET("item/{id}.json?print=pretty") suspend fun getStory(@Path("id") id: Int): Item } class Repo { fun api(): Api { return Retrofit.Builder() .baseUrl("https://hacker-news.firebaseio.com/v0/") .addConverterFactory(MoshiConverterFactory.create()) .build() .create(Api::class.java) } } class ViewModel(val repo: Repo): ViewModel() { val items = MutableLiveData<ArrayList<Item>>() suspend fun load() { try { val ids = repo.api() .getTopStories() .take(20) val items = ids.map { repo.api().getStory(it) } this.items.value = items.toCollection(ArrayList()) } catch (e: Exception) { this....

August 28, 2019 路 2 min 路 Khoa Pham

How to show generic list in Fragment in Android

Issue #378 After having a generic RecyclerView, if we want to show multiple kinds of data in Fragment, we can use generic. We may be tempted to use interface or protocol, but should prefer generic. class FeedFragment() : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val mainViewModel: MainViewModel = ViewModelProviders.of(activity!!).get(MainViewModel::class.java) mainViewModel.resId.observe(viewLifecycleOwner, Observer { when (it) { R.id.gitHub -> { handleGitHub() } R.id.hackerNews -> { handleHackerNews() } R.id.reddit -> { handleReddit() } R....

August 28, 2019 路 2 min 路 Khoa Pham

How to use Product Hunt GraphQL API with Retrofit

Issue #370 Define response model import com.squareup.moshi.Json data class Response( @field:Json(name="data") val data: ResponseData ) data class ResponseData( @field:Json(name="posts") val posts: Posts ) data class Posts( @field:Json(name="edges") val edges: List<Edge> ) data class Edge( @field:Json(name="node") val node: Item ) data class Item( @field:Json(name="id") val id: String, @field:Json(name="name") val name: String, @field:Json(name="url") val url: String, @field:Json(name="tagline") val tagline: String, @field:Json(name="featuredAt") val featuredAt: String, @field:Json(name="votesCount") val votesCount: Int, @field:Json(name="commentsCount") val commentsCount: Int, @field:Json(name="thumbnail") val thumbnail: Thumbnail ) data class Thumbnail( @field:Json(name="url") val ur: String ) Here is the query...

August 26, 2019 路 2 min 路 Khoa Pham

How to get trending repos on GitHub using Retrofit

Issue #367 https://api.github.com/search/repositories?sort=stars&order=desc&q=language:javascript,java,swift,kotlin&q=created:>2019-08-21 interface Api { @GET("https://api.github.com/search/repositories") suspend fun getTrendingRepos( @Query("sort") sort: String, @Query("order") order: String, @Query("q") qs: List<String> ): Response } class Repo { fun api(): Api { return Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(MoshiConverterFactory.create()) .build() .create(Api::class.java) } } class ViewModel(val repo: Repo, val dateProvider: DateProvider): ViewModel() { val items = MutableLiveData<ArrayList<Item>>() suspend fun load() { try { val order = "desc" val sort = "star" val formatter = SimpleDateFormat("YYYY-MM-dd") val qs = listOf( "language:javascript,java,swift,kotlin", "q=created:>${formatter....

August 22, 2019 路 1 min 路 Khoa Pham

How to use Retrofit in Android

Issue #366 Code uses Retrofit 2.6.0 which has Coroutine support app/build.gradle implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01" implementation "com.squareup.moshi:moshi:$Version.moshi" implementation "com.squareup.retrofit2:retrofit:$Version.retrofit" implementation "com.squareup.retrofit2:converter-moshi:$Version.retrofit" Api.kt import retrofit2.http.GET interface Api { @GET("api/articles") suspend fun getArticles(): List<Article> } Repo.kt import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory class Repo { fun get(): Api { return Retrofit.Builder() .baseUrl("https://dev.to") .addConverterFactory(MoshiConverterFactory.create()) .build() .create(Api::class.java) } } ViewModel.kt import androidx.lifecycle.ViewModel import androidx.lifecycle.liveData import kotlinx.coroutines.Dispatchers class ViewModel(val repo: Repo): ViewModel() { val articles = liveData(Dispatchers.Main) { emit(repo.get().getArticles().toCollection(ArrayList())) } } Article....

August 22, 2019 路 1 min 路 Khoa Pham

How to inject view model with Koin in Android

Issue #359 app/build.gradle implementation "org.koin:koin-core:$Version.koin" implementation "org.koin:koin-androidx-scope:$Version.koin" implementation "org.koin:koin-androidx-viewmodel:$Version.koin" MyApplication.kt import android.app.Application import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.core.context.startKoin import org.koin.dsl.module class MyApplication: Application() { var appModule = module { single { MyRepo() } viewModel { MyViewModel(get()) } } override fun onCreate() { super.onCreate() startKoin { androidLogger() androidContext(this@MyApplication) modules(appModule) } } } MyFragment.kt import org.koin.androidx.viewmodel.ext.android.viewModel val viewModel: MyViewModel by viewModel() ...

August 15, 2019 路 1 min 路 Khoa Pham

How to use coroutine LiveData in Android

Issue #358 app/build.gradle implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01" import androidx.lifecycle.ViewModel import androidx.lifecycle.liveData import kotlinx.coroutines.Dispatchers class MainViewModel : ViewModel() { val repository: TodoRepository = TodoRepository() val firstTodo = liveData(Dispatchers.IO) { val retrivedTodo = repository.getTodo(1) emit(retrivedTodo) } } Use coroutines with LiveData https://developer.android.com/topic/libraries/architecture/coroutines The liveData building block serves as a structured concurrency primitive between coroutines and LiveData. The code block starts executing when LiveData becomes active and is automatically canceled after a configurable timeout when the LiveData becomes inactive....

August 15, 2019 路 1 min 路 Khoa Pham

How to declare generic RecyclerView adapter in Android

Issue #357 generic/Adapter.kt package com.onmyway133.generic import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView abstract class Adapter<T>(var items: ArrayList<T>): RecyclerView.Adapter<RecyclerView.ViewHolder>() { abstract fun configure(item: T, holder: ViewHolder) fun update(items: ArrayList<T>) { this.items = items notifyDataSetChanged() } override fun getItemCount(): Int = items.count() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val view = LayoutInflater .from(parent.context) .inflate(viewType, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { configure(items[position], holder as ViewHolder) } } class ViewHolder(view: View): RecyclerView....

August 14, 2019 路 1 min 路 Khoa Pham

How to use Navigation component with DrawerLayout in Android

Issue #349 build.gradle dependencies { classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha05' } app/build.gradle apply plugin: 'androidx.navigation.safeargs' dependencies { def navigationVersion = "2.0.0" def drawerLayoutVersion = "1.0.0" implementation "androidx.drawerlayout:drawerlayout:$drawerLayoutVersion" implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" } main_activity.xml Use CoordinatorLayout and ToolBar Define layout_gravity for NavigationView <?xml version="1.0" encoding="utf-8"?> <androidx.drawerlayout.widget.DrawerLayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawerLayout" tools:context=".MainActivity"> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.appcompat.widget.Toolbar android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:id="@+id/toolbar"/> </com.google.android.material.appbar.AppBarLayout> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/hostFragment" android:name="androidx.navigation.fragment.NavHostFragment" app:defaultNavHost="true" app:navGraph="@navigation/navigation_graph"/> </androidx.coordinatorlayout.widget.CoordinatorLayout> <com....

August 7, 2019 路 2 min 路 Khoa Pham

How to organise test files

Issue #327 In terms of tests, we usually have files for unit test, UI test, integeration test and mock. Out of sight, out of mind. Unit tests are for checking specific functions and classes, it鈥檚 more convenient to browse them side by side with source file. For example in Javascript, Kotlin and Swift index.js index.test.js index.mock.js LocationManager.kt LocationManager.mock.kt LocationManager.test.kt BasketHandler.swift BasketHandler.mock.swift BasketHandler.test.swift Integration tests check features or sub features, and may cover many source files, it鈥檚 better to place them in feature folders...

June 25, 2019 路 1 min 路 Khoa Pham

How to use Gradle Kotlin DSL in Android

Issue #285 kts settings.gradle.kts include(":app") build.gradle.kts import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.kotlin import org.gradle.kotlin.dsl.* import org.jetbrains.kotlin.config.KotlinCompilerVersion plugins { id("com.android.application") kotlin("android") kotlin("android.extensions") } //apply { // from("$rootDir/tools/grgit.gradle") // from("$rootDir/buildSrc/quality.gradle.kts") // from("$rootDir/tools/ktlint.gradle") // from("$rootDir/tools/detekt.gradle") //} android { compileSdkVersion(28) flavorDimensions("default") defaultConfig { applicationId = "com.onmyway133.myapp" minSdkVersion(26) targetSdkVersion(28) // versionCode = ext.get("gitCommitCount") as? Int versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } signingConfigs { create("release") { keyAlias = "keyalias" keyPassword = "keypassword" storePassword = "storepassword" storeFile = file("/Users/khoa/Android/Key/keystore") } } buildTypes { getByName("debug") { signingConfig = signingConfigs....

May 24, 2019 路 2 min 路 Khoa Pham