Skip to content
The Official Conference App for DroidKaigi 2020 Tokyo
Kotlin Swift Shell Java Ruby HTML
Branch: master
Clone or download
Latest commit 1e53963 Jan 24, 2020
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.ci Merge pull request #562 from DroidKaigi/reportAllIssues Jan 24, 2020
.circleci Save ktlint artifacts Jan 23, 2020
.encrypted Create github action workflow for release branches for now Jan 20, 2020
.github Decrypt files as well even on test job Jan 20, 2020
.idea Fix KtLint all warnings Jan 22, 2020
android-base nit Jan 23, 2020
arts replace some drawer's icon and adjust padding between icon and title Jan 20, 2020
buildSrc Merge pull request #543 from TentaShion/add_deeplink_tester Jan 23, 2020
corecomponent Remove AutoClearedValue Jan 24, 2020
data Scroll to current session Jan 24, 2020
ext/log Implement error handling Sep 15, 2019
feature Merge pull request #565 from numa08/fix-link-text Jan 24, 2020
gradle Upgrade Gradle to 6.1 Jan 21, 2020
ios-base Merge branch 'master' of https://github.com/DroidKaigi/conference-app… Jan 24, 2020
ios-combined Delete unneeded println Jan 19, 2020
model When ended of all sessions, don't scroll Jan 24, 2020
.editorconfig Added editorconfig Jan 21, 2020
.envrc.example Remove key alias environment variable Jan 20, 2020
.gitignore enable r8 of release Jan 19, 2020
.ruby-version Added danger stuff Jan 11, 2020
CONTRIBUTING.ja.md Update CONTRIBUTING.ja.md Jan 13, 2020
CONTRIBUTING.md Add CONTRIBUTING.md Jan 12, 2020
LICENSE Fix organization name Jan 16, 2020
README.md Merge branch 'master' of https://github.com/DroidKaigi/conference-app… Jan 21, 2020
build.gradle Save ktlint artifacts Jan 23, 2020
bundletool-all.jar Try to create universal apk Jan 15, 2020
gradle.properties Fix Gradle Sync Issue(details in PR) Dec 6, 2019
gradlew Upgrade Gradle to 6.1 Jan 21, 2020
gradlew.bat Upgrade Gradle to 6.1 Jan 21, 2020
project.dot.png Add staff using dynamic feature module Aug 18, 2019
settings.gradle use sessings.gradle instead of echo Jan 20, 2020

README.md

DroidKaigi LogoDroidKaigi 2020 official Android app GitHub Actions

We are currently working on the event. We are looking for contributors!

DroidKaigi 2020 is a conference tailored for developers on 20th and 21st February 2020.

You can install the production app via Get it on Google Play. // TODO: Add link to Google Play

And also, you can try the binary under development built on master branch through Try it on your device via DeployGate

Try it on your device via DeployGate

Features

top drawer

Contributing

We always welcome any and all contributions! See CONTRIBUTING.md for more information

For Japanese speakers, please see CONTRIBUTING.ja.md

Requirements

Android Studio 3.6 and higher. You can download it from this page.

Development Environment

Multi module project

We separate the modules for each feature. We use the Dynamic feature modules for additional features.

Kotlin Multiplatform Project

// TODO: Add MultiPlatform

Architecture

This app uses an AndroidJetpack(AAC) based architecture using AAC(LiveData, ViewModel, Room), Kotlin, Kotlin Coroutines Flow, DataBinding, Dagger, Firebase.

It is designed to be a unidirectional data flow within the ViewModel.

Fragment

Just observe() the LiveData<UiModel> of the ViewModel.

@Inject lateinit var sessionDetailViewModelFactory: SessionDetailViewModel.Factory
private val sessionDetailViewModel by assistedViewModels {
    sessionDetailViewModelFactory.create(navArgs.sessionId)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    ...
    sessionDetailViewModel.uiModel
        .observe(viewLifecycleOwner) { uiModel: SessionDetailViewModel.UiModel ->
            ...
            progressTimeLatch.loading = uiModel.isLoading
            uiModel.session
                ?.let { session -> setupSessionViews(session) }
        }
    }

ViewModel

The LiveData Kotlin Coroutines builder runs when LiveData becomes active.
And observe the data of the Coroutiens Flow of the repository.

The LiveData becomes LoadState.Loading before the Coroutiens Flow is executed by Flow.toLoadingState(), and becomes LoadState.Loaded when finished.

class SessionsViewModel @Inject constructor(
    val sessionRepository: SessionRepository
) : ViewModel() {

...
    private val sessionLoadState: LiveData<LoadState<SessionContents>> = liveData {
        emitSource(
            sessionRepository.sessionContents()
                .toLoadingState()
                .asLiveData()
        )
        sessionRepository.refresh()
    }

Construct UiModel LiveData from some such LiveData.
The combine method works like RxJava's combineLatest.
You can make the loading state of the screen from multiple LiveData states like sessionLoadState.isLoading || favoriteState.isLoading.

class SessionDetailViewModel @AssistedInject constructor(
    @Assisted private val sessionId: SessionId,
    private val sessionRepository: SessionRepository
) : ViewModel() {
...
    val uiModel: LiveData<UiModel> = combine(
        initialValue = UiModel.EMPTY,
        liveData1 = sessionLoadStateLiveData,
        liveData2 = favoriteLoadingStateLiveData
    ) { current: UiModel,
        sessionLoadState: LoadState<Session>,
        favoriteState: LoadingState ->
        // You can create loading state by multiple LiveData
        val isLoading = sessionLoadState.isLoading || favoriteState.isLoading
        UiModel(
            isLoading = isLoading,
            error = sessionLoadState
                .getErrorIfExists()
                .toAppError()
                ?: favoriteState
                    .getErrorIfExists()
                    .toAppError()
            ,
            session = sessionLoadState.value
        )
    }

Run Coroutines with viewModelScope when data changes, such as adding a session to Favorites.
Because we do not want to end the process of adding a session to favorites with the back button, we use WorkManager to do the processing.

class SessionDetailViewModel @AssistedInject constructor(
    @Assisted private val sessionId: SessionId,
    private val sessionRepository: SessionRepository
) : ViewModel() {
..
    private var favoriteLoadingStateLiveData: MutableLiveData<LoadingState> = MutableLiveData(LoadingState.Loaded)
...
    fun favorite(session: Session) {
        viewModelScope.launch {
            favoriteLoadingStateLiveData.value = LoadingState.Loading
            try {
                sessionRepository.toggleFavoriteWithWorker(session.id)
                favoriteLoadingStateLiveData.value = LoadingState.Loaded
            } catch (e: Exception) {
                favoriteLoadingStateLiveData.value = LoadingState.Error(e)
            }
        }
    }

Design

Thanks

Thank you for contributing!

Credit

This project uses some modern Android libraries and source codes.

Android

iOS

You can’t perform that action at this time.