DroidKaigi 2018 official Android app
DroidKaigi 2018 is a conference tailored for developers on 8th and 9th February 2018.
Features
TBD
Contributing
We are always welcome your contribution!
How to find the tasks
We use waffle.io to manage the tasks. Please find the issues you'd like to contribute in it. welcome contribute and easy are good for first contribution.
Of course, it would be great to send PullRequest which has no issue!
How to contribute
If you find the tasks you want to contribute, please comment in the issue like this to prevent to conflict contribution. We'll reply as soon as possible, but it's unnecessary to wait our reaction. It's okay to start contribution and send PullRequest!
We've designated these issues as good candidates for easy contribution. You can always fork the repository and send a pull request (on a branch other than master
).
Development Environment
Kotlin
This app is full Kotlin!
RxJava2 & LiveData
Converting RxJava2's publisher to AAC LiveData with LiveDataReactiveStreams.
AllSessionsViewModel.kt
repository.sessions
.toResult(schedulerProvider)
.toLiveData()
LiveDataReactiveStreamsExt.kt
fun <T> Publisher<T>.toLiveData() = LiveDataReactiveStreams.fromPublisher(this)
Groupie
By using Groupie you can simplify the implementation around RecyclerView.
data class SpeakerItem(
val speaker: Speaker
) : BindableItem<ItemSpeakerBinding>(speaker.id.hashCode().toLong()) {
override fun bind(viewBinding: ItemSpeakerBinding, position: Int) {
viewBinding.speaker = speaker
}
override fun getLayout(): Int = R.layout.item_speaker
}
Architecture
This app uses an Android Architecture Components(AAC) based architecture using AAC(LiveData, ViewModel, Room), Kotlin, RxJava, DataBinding, dependency injection, Firebase.
Fragment -> ViewModel
Use LifecycleObserver
for telling lifecycle to ViewModel.
SessionsFragment.kt
class SessionsFragment : Fragment(), Injectable {
private lateinit var sessionsViewModel: SessionsViewModel
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
lifecycle.addObserver(sessionsViewModel)
...
SessionsViewModel.kt
class SessionsViewModel @Inject constructor(
private val repository: SessionRepository,
private val schedulerProvider: SchedulerProvider
) : ViewModel(), LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate() {
...
}
ViewModel -> Repository
Use RxJava2(RxKotlin) and ViewModel#onCleared()
for preventing leaking.
SessionsViewModel.kt
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate() {
repository
.refreshSessions()
.subscribeBy(onError = defaultErrorHandler())
.addTo(compositeDisposable)
}
override fun onCleared() {
super.onCleared()
compositeDisposable.clear()
}
Repository -> API, Repository -> DB
Use Retrofit and save to the Architecture Component Room
.
SessionDataRepository.kt
override fun refreshSessions(): Completable {
return api.getSessions()
.doOnSuccess { response ->
sessionDatabase.save(response)
}
.subscribeOn(schedulerProvider.computation())
.toCompletable()
}
DB -> Repository
Use Room
with RxJava2 Flowable Support.
And SessionDataRepository holds Flowable property.
SessionDao.kt
@Query("SELECT room_id, room_name FROM session GROUP BY room_id ORDER BY room_id")
abstract fun getAllRoom(): Flowable<List<RoomEntity>>
SessionDataRepository.kt
class SessionDataRepository @Inject constructor(
private val sessionDatabase: SessionDatabase,
...
) : SessionRepository {
override val rooms: Flowable<List<Room>> =
sessionDatabase.getAllRoom().toRooms()
Repository -> ViewModel
We create LiveData from a ReactiveStreams publisher with LiveDataReactiveStreams
SessionsViewModel.kt
val rooms: LiveData<Result<List<Room>>> by lazy {
repository.rooms
.toResult(schedulerProvider)
.toLiveData()
}
LiveDataReactiveStreamsExt.kt
fun <T> Publisher<T>.toLiveData() = LiveDataReactiveStreams.fromPublisher(this) as LiveData<T>
And using Result
class for error handling with Kotlin extension.
fun <T> Flowable<T>.toResult(schedulerProvider: SchedulerProvider): Flowable<Result<T>> =
compose { item ->
item
.map { Result.success(it) }
.onErrorReturn { e -> Result.failure(e.message ?: "unknown", e) }
.observeOn(schedulerProvider.ui())
.startWith(Result.inProgress())
}
sealed class Result<T>(val inProgress: Boolean) {
class InProgress<T> : Result<T>(true)
data class Success<T>(var data: T) : Result<T>(false)
data class Failure<T>(val errorMessage: String?, val e: Throwable) : Result<T>(false)
ViewModel -> Fragment
Fragment observe ViewModel's LiveData.
We can use the result with Kotlin when
expression.
In is Result.Success
block, you can access data with result.data
by Kotlin Smart cast.
SessionsFragment.kt
sessionsViewModel.rooms.observe(this, { result ->
when (result) {
is Result.InProgress -> {
binding.progress.show()
}
is Result.Success -> {
binding.progress.hide()
sessionsViewPagerAdapter.setRooms(result.data)
}
is Result.Failure -> {
Timber.e(result.e)
binding.progress.hide()
}
}
})
Thanks
Thank you for contributing!
- Contributors
- Designer
Credit
This project uses some modern Android libraries and source codes.
- Android Architecture Components
- Android Support Libraries
- Support v4
- AppCompat v7
- Design
- VectorDrawable
- CardView
- ConstraintLayout
- RecyclerView
- AOSP Codes
- Firebase
- Dagger2, Dagger Android Support, Glide4 - Google
- Kotlin - JetBrains
- Retrofit2, Moshi - Square
- RxJava2, RxKotlin, RxAndroid - ReactiveX
- OkHttp3
- ThreeTenABP, Timber - JakeWharton
- Groupie - lisawray
- Stetho - Facebook
- Kotshi - ansman
- DownloadableCalligraphy - takahirom
- EllipsizingTextView
- Google I/O 2017
License
Copyright 2018 DroidKaigi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.