{"id":15040849,"url":"https://github.com/droidkaigi/conference-app-2018","last_synced_at":"2025-04-08T09:06:34.739Z","repository":{"id":54028324,"uuid":"115203383","full_name":"DroidKaigi/conference-app-2018","owner":"DroidKaigi","description":"The Official Conference App for DroidKaigi 2018 Tokyo","archived":false,"fork":false,"pushed_at":"2018-09-27T08:57:38.000Z","size":5970,"stargazers_count":1345,"open_issues_count":30,"forks_count":331,"subscribers_count":156,"default_branch":"master","last_synced_at":"2025-04-08T09:06:29.357Z","etag":null,"topics":["android","android-app","conference","droidkaigi","kotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DroidKaigi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-23T15:13:21.000Z","updated_at":"2025-03-06T03:00:40.000Z","dependencies_parsed_at":"2022-08-13T06:10:27.208Z","dependency_job_id":null,"html_url":"https://github.com/DroidKaigi/conference-app-2018","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DroidKaigi%2Fconference-app-2018","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DroidKaigi%2Fconference-app-2018/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DroidKaigi%2Fconference-app-2018/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DroidKaigi%2Fconference-app-2018/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DroidKaigi","download_url":"https://codeload.github.com/DroidKaigi/conference-app-2018/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247809964,"owners_count":20999816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-app","conference","droidkaigi","kotlin"],"created_at":"2024-09-24T20:45:09.944Z","updated_at":"2025-04-08T09:06:34.720Z","avatar_url":"https://github.com/DroidKaigi.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![](app/src/main/res/mipmap-mdpi/ic_launcher.png) DroidKaigi 2018 official Android app\n[![CircleCI](https://circleci.com/gh/DroidKaigi/conference-app-2018.svg?style=svg\u0026circle-token=b8e6a12e76295c24c7be8f57807cf6ab4139288e)](https://circleci.com/gh/DroidKaigi/conference-app-2018)[![Waffle.io - Columns and their card count](https://badge.waffle.io/4004fb95e3835aef9cfea229c8443b4b62eacf957879565e813f405126decfae.svg?columns=all)](https://waffle.io/DroidKaigi/conference-app-2018)\n\n[DroidKaigi 2018](https://droidkaigi.jp/2018/en/) is a conference tailored for developers on 8th and 9th February 2018.\n\n[\u003cimg src=\"https://dply.me/xt08ja/button/large\" alt=\"Try it on your device via DeployGate\"\u003e](https://dply.me/xt08ja#install)\n\n# Features\n\u003cimg src=\"art/screenshot_sessions.png\" width=\"200\" /\u003e \u003cimg src=\"art/screenshot_search.png\" width=\"200\" /\u003e \u003cimg src=\"art/screenshot_session_detail.png\" width=\"200\" /\u003e\n\n* View conference schedule and details of each session\n* Set notification for upcoming sessions on your preference\n* Search sessions and speakers and topics\n* Show Information Feed\n\n\n# Contributing\nWe are always welcome your contribution!\n\n## How to find the tasks\nWe use [waffle.io](https://waffle.io/DroidKaigi/conference-app-2018) to manage the tasks.\nPlease find the issues you'd like to contribute in it.\n[welcome contribute](https://github.com/DroidKaigi/conference-app-2018/labels/welcome%20contribute) and [easy](https://github.com/DroidKaigi/conference-app-2018/labels/easy) are good for first contribution.\n\nOf course, it would be great to send PullRequest which has no issue!\n\n## How to contribute\nIf you find the tasks you want to contribute, please comment in the issue like [this](https://github.com/DroidKaigi/conference-app-2018/issues/73#issuecomment-357410022) to prevent to conflict contribution.\nWe'll reply as soon as possible, but it's unnecessary to wait our reaction. It's okay to start contribution and send PullRequest!\n\nWe'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`).\n\n\n# Development Environment\n\n## Kotlin\nThis app is full Kotlin!\n\n## RxJava2 \u0026 LiveData\nConverting RxJava2's publisher to AAC LiveData with [LiveDataReactiveStreams](https://developer.android.com/reference/android/arch/lifecycle/LiveDataReactiveStreams.html).\n\n\u003e AllSessionsViewModel.kt\n\n```kotlin\nrepository.sessions\n    .toResult(schedulerProvider)\n    .toLiveData()\n```\n\n\u003e LiveDataReactiveStreamsExt.kt\n\n```kotlin\nfun \u003cT\u003e Publisher\u003cT\u003e.toLiveData() = LiveDataReactiveStreams.fromPublisher(this)\n```\n\n## Groupie\n\nBy using Groupie you can simplify the implementation around RecyclerView.\n\n```kotlin\ndata class SpeakerItem(\n        val speaker: Speaker\n) : BindableItem\u003cItemSpeakerBinding\u003e(speaker.id.hashCode().toLong()) {\n\n    override fun bind(viewBinding: ItemSpeakerBinding, position: Int) {\n        viewBinding.speaker = speaker\n    }\n\n    override fun getLayout(): Int = R.layout.item_speaker\n}\n```\n\n# Architecture\nThis app uses an Android Architecture Components(AAC) based architecture using AAC(LiveData, ViewModel, Room), Kotlin, RxJava, DataBinding, dependency injection, Firebase.\n\n\u003cimage src=\"https://user-images.githubusercontent.com/1386930/34318268-f8b7eece-e806-11e7-8b18-d9fc64dcd24e.png\" width=\"500\" /\u003e\n\n## Fragment -\u003e ViewModel\n\n\u003cimage src=\"https://user-images.githubusercontent.com/1386930/34699651-ed4798b4-f521-11e7-84c9-11528a1c1f8c.png\" width=\"500\" /\u003e\n\nUse `LifecycleObserver` for telling lifecycle to ViewModel.\n\nSessionsFragment.kt\n\n```kotlin\nclass SessionsFragment : Fragment(), Injectable {\n\n    private lateinit var sessionsViewModel: SessionsViewModel\n\n...\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        ...\n        lifecycle.addObserver(sessionsViewModel)\n        ...\n```\n\nSessionsViewModel.kt\n\n```kotlin\nclass SessionsViewModel @Inject constructor(\n        private val repository: SessionRepository,\n        private val schedulerProvider: SchedulerProvider\n) : ViewModel(), LifecycleObserver {\n  ...\n    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)\n    fun onCreate() {\n    ...\n}\n```\n\n## ViewModel -\u003e Repository\n\u003cimage src=\"https://user-images.githubusercontent.com/1386930/34699666-0360c026-f522-11e7-935e-663006e72d01.png\" width=\"500\" /\u003e\n\nUse RxJava2(RxKotlin) and `ViewModel#onCleared()` for preventing leaking.\n\nSessionsViewModel.kt\n\n```kotlin\n    private val compositeDisposable: CompositeDisposable = CompositeDisposable()\n\n    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)\n    fun onCreate() {\n        repository\n                .refreshSessions()\n                .subscribeBy(onError = defaultErrorHandler())\n                .addTo(compositeDisposable)\n    }\n\n    override fun onCleared() {\n        super.onCleared()\n        compositeDisposable.clear()\n    }\n```\n\n## Repository -\u003e API, Repository -\u003e DB\n\n\u003cimage src=\"https://user-images.githubusercontent.com/1386930/34699678-156f4f3a-f522-11e7-92a7-bebf96a9ed4b.png\" width=\"500\" /\u003e\n\nUse Retrofit and save to the `Architecture Component Room`.\n\nSessionDataRepository.kt\n\n```kotlin\n    override fun refreshSessions(): Completable {\n        return api.getSessions()\n                .doOnSuccess { response -\u003e\n                    sessionDatabase.save(response)\n                }\n                .subscribeOn(schedulerProvider.computation())\n                .toCompletable()\n    }\n```\n\n## DB -\u003e Repository\n\n\u003cimage src=\"https://user-images.githubusercontent.com/1386930/34699688-1eccee0c-f522-11e7-9c2c-77870cccca5b.png\" width=\"500\" /\u003e\n\nUse `Room` with RxJava2 Flowable Support.\nAnd SessionDataRepository holds Flowable property.\n\nSessionDao.kt\n\n```kotlin\n    @Query(\"SELECT room_id, room_name FROM session GROUP BY room_id ORDER BY room_id\")\n    abstract fun getAllRoom(): Flowable\u003cList\u003cRoomEntity\u003e\u003e\n```\n\nSessionDataRepository.kt\n\n```kotlin\nclass SessionDataRepository @Inject constructor(\n        private val sessionDatabase: SessionDatabase,\n...\n) : SessionRepository {\n\n    override val rooms: Flowable\u003cList\u003cRoom\u003e\u003e =\n            sessionDatabase.getAllRoom().toRooms()\n```\n\n## Repository -\u003e ViewModel\n\n\u003cimage src=\"https://user-images.githubusercontent.com/1386930/34699694-29ddfdcc-f522-11e7-83df-aa872eebafbb.png\" width=\"500\" /\u003e\n\nWe create LiveData from a ReactiveStreams publisher with [LiveDataReactiveStreams](https://developer.android.com/reference/android/arch/lifecycle/LiveDataReactiveStreams.html)\n\n\nSessionsViewModel.kt\n\n```kotlin\n    val rooms: LiveData\u003cResult\u003cList\u003cRoom\u003e\u003e\u003e by lazy {\n        repository.rooms\n                .toResult(schedulerProvider)\n                .toLiveData()\n    }\n```\n\nLiveDataReactiveStreamsExt.kt\n\n```kotlin\nfun \u003cT\u003e Publisher\u003cT\u003e.toLiveData() = LiveDataReactiveStreams.fromPublisher(this) as LiveData\u003cT\u003e\n```\n\nAnd using `Result` class for error handling with Kotlin extension.\n\n```kotlin\nfun \u003cT\u003e Flowable\u003cT\u003e.toResult(schedulerProvider: SchedulerProvider): Flowable\u003cResult\u003cT\u003e\u003e =\n        compose { item -\u003e\n            item\n                    .map { Result.success(it) }\n                    .onErrorReturn { e -\u003e Result.failure(e.message ?: \"unknown\", e) }\n                    .observeOn(schedulerProvider.ui())\n                    .startWith(Result.inProgress())\n        }\n```\n\n```kotlin\nsealed class Result\u003cT\u003e(val inProgress: Boolean) {\n    class InProgress\u003cT\u003e : Result\u003cT\u003e(true)\n    data class Success\u003cT\u003e(var data: T) : Result\u003cT\u003e(false)\n    data class Failure\u003cT\u003e(val errorMessage: String?, val e: Throwable) : Result\u003cT\u003e(false)\n\n```\n\n## ViewModel -\u003e Fragment\n\n\u003cimage src=\"https://user-images.githubusercontent.com/1386930/34699699-320f9bb8-f522-11e7-9ce3-f02940f1343e.png\" width=\"500\" /\u003e\n\nFragment observe ViewModel's LiveData.\nWe can use the result with Kotlin `when` expression.\nIn `is Result.Success` block, you can access data with `result.data` by Kotlin Smart cast.\n\nSessionsFragment.kt\n\n```kotlin\n        sessionsViewModel.rooms.observe(this, { result -\u003e\n            when (result) {\n                is Result.InProgress -\u003e {\n                    binding.progress.show()\n                }\n                is Result.Success -\u003e {\n                    binding.progress.hide()\n                    sessionsViewPagerAdapter.setRooms(result.data)\n                }\n                is Result.Failure -\u003e {\n                    Timber.e(result.e)\n                    binding.progress.hide()\n                }\n            }\n        })\n```\n\n## Release\nThe release process is automated by using [gradle-play-publisher](https://github.com/Triple-T/gradle-play-publisher).\nWhen we add `git tag`, CI deploys the release apk to GooglePlay alpha. \nTo know more details, see [.circleci/config.yml](https://github.com/DroidKaigi/conference-app-2018/blob/master/.circleci/config.yml)\n\n```shell\nelif [[ \"${CIRCLE_TAG}\" =~ ^v[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n    echo \"Deploy to Google Play\"\n    openssl aes-256-cbc -k $PUBLISHER_KEYS_JSON_DECRYPT_PASSWORD -d -in encrypted-publisher-keys.json -out app/publisher-keys.json\n    ./gradlew publishApkRelease\nfi\n```\n\n## iOS App with Kotlin/Native and Kotlin Multiplatform Projects\nSome contributors are challenging to develop iOS app with [Kotlin/Native](https://kotlinlang.org/docs/reference/native-overview.html) and [Kotlin Multiplatform Projects](https://kotlinlang.org/docs/reference/multiplatform.html).  \nWe are watching this project. \n[DroidKaigi2018iOS](https://github.com/kikuchy/DroidKaigi2018iOS)\n\n## DroidKaigi 2018 Flutter App\nThe unofficial conference app for DroidKaigi 2018 Tokyo\nhttps://github.com/konifar/droidkaigi2018-flutter\n\n## Thanks\nThank you for contributing!\n\n* Contributors\n  * [GitHub : Contributors](https://github.com/DroidKaigi/conference-app-2018/graphs/contributors)\n* Designer  \n  * [okapi / Yuki Okamoto](https://www.instagram.com/okamoto.yuki/)\n  * [DroidKaigi 2018 Zeplin Scene](https://scene.zeplin.io/project/5a327a64f0b20176887c556c)\n\n## Credit\nThis project uses some modern Android libraries and source codes.\n\n* Android Architecture Components\n  * [Lifecycles](https://developer.android.com/topic/libraries/architecture/lifecycle.html)\n  * [LiveData](https://developer.android.com/topic/libraries/architecture/livedata.html)\n  * [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel.html)\n  * [Room](https://developer.android.com/topic/libraries/architecture/room.html)\n  * [ReactiveStreams](https://developer.android.com/reference/android/arch/lifecycle/LiveDataReactiveStreams.html)\n* Android Support Libraries\n  * Support v4\n  * AppCompat v7\n  * Design\n  * VectorDrawable\n  * CardView\n  * ConstraintLayout\n  * RecyclerView\n  * AOSP Codes\n* Firebase\n  * [Cloud Firestore](https://firebase.google.com/docs/firestore/)\n  * [Auth](https://firebase.google.com/docs/auth/)\n* [KTX](https://github.com/android/android-ktx/), [Dagger2](https://google.github.io/dagger/), Dagger Android Support, [Glide4](https://bumptech.github.io/glide/) - Google\n* [Kotlin](http://kotlinlang.org/) - JetBrains\n* [Retrofit2](http://square.github.io/retrofit/), [Moshi](https://github.com/square/moshi/) - Square\n* [RxJava2](https://github.com/ReactiveX/RxJava), [RxKotlin](https://github.com/ReactiveX/RxKotlin), RxAndroid  - ReactiveX\n* [OkHttp3](http://square.github.io/okhttp/)\n* [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP), [Timber](https://github.com/JakeWharton/timber) - JakeWharton\n* [Groupie](https://github.com/lisawray/groupie) - lisawray\n* [Stetho](http://facebook.github.io/stetho/) - Facebook\n* [Kotshi](https://github.com/ansman/kotshi) - ansman\n* [DownloadableCalligraphy](https://github.com/takahirom/DownloadableCalligraphy) - takahirom\n* [Kotpref](https://github.com/chibatching/Kotpref) - chibatching\n* [RxBroadcast](https://github.com/cantrowitz/RxBroadcast) - cantrowitz\n* [ProgressTimeLatch](https://github.com/chrisbanes/tivi/blob/96e7cae7560ffd358b8c58c47267ed1024df53f6/app/src/main/java/me/banes/chris/tivi/ui/ProgressTimeLatch.kt) - chrisbanes\n* [Google I/O 2017](https://github.com/google/iosched)\n* [DebouncingOnClickListener](https://github.com/JakeWharton/butterknife/blob/e78507711fe8a7c637ee61c44a7b09f1be8ff9f6/butterknife/src/main/java/butterknife/internal/DebouncingOnClickListener.java) - JakeWharton\n\n## License\n\n    Copyright 2018 DroidKaigi\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdroidkaigi%2Fconference-app-2018","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdroidkaigi%2Fconference-app-2018","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdroidkaigi%2Fconference-app-2018/lists"}