Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buildSrc/src/main/java/dependencies/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dependencies

private object Versions {
val androidCompileSdkVersion = 28
val androidTargetSdkVersion = 28
val androidMinSdkVersion = 21

private val versionMajor = 1
Expand Down
5 changes: 2 additions & 3 deletions corecomponent/androidcomponent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ dependencies {
api Dep.AndroidX.constraint
api Dep.AndroidX.design
api Dep.AndroidX.coreKtx
implementation Dep.Kotlin.stdlibJvm
api Dep.Kotlin.stdlibJvm

api Dep.Dagger.androidSupport
implementation Dep.Kotlin.stdlibJvm
api Dep.Kotlin.coroutines

api Dep.Klock.jvm
Expand All @@ -33,7 +32,7 @@ dependencies {
implementation Dep.Dagger.core
implementation Dep.Dagger.androidSupport

implementation Dep.Groupie.groupie
api Dep.Groupie.groupie

testImplementation Dep.Test.junit
testImplementation Dep.MockK.jvm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
-keepnames class io.github.droidkaigi.confsched2019.session.ui.TabularFormSessionPagesFragment
-keepnames class io.github.droidkaigi.confsched2019.settings.ui.SettingsFragment
-keepnames class io.github.droidkaigi.confsched2019.sponsor.ui.SponsorFragment
-keepnames class io.github.droidkaigi.confsched2019.staff.ui.StaffSearchFragment
-keepnames class io.github.droidkaigi.confsched2019.staff_dfm.ui.StaffSearchFragment
-keepnames class io.github.droidkaigi.confsched2019.survey.ui.SessionSurveyFragment
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
</activity>
<fragment
android:id="@+id/staff_search"
android:name="io.github.droidkaigi.confsched2019.staff.ui.StaffSearchFragment"
android:name="io.github.droidkaigi.confsched2019.staff_dfm.ui.StaffSearchFragment"
android:label="@string/staff_search_label"
/>
</navigation>
1 change: 1 addition & 0 deletions feature/staff_dfm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
56 changes: 56 additions & 0 deletions feature/staff_dfm/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import dependencies.Versions
import dependencies.Dep

apply plugin: 'com.android.dynamic-feature'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion Versions.androidCompileSdkVersion
defaultConfig {
minSdkVersion Versions.androidMinSdkVersion
targetSdkVersion Versions.androidTargetSdkVersion
}

signingConfigs {
debug {
keyAlias 'droidkaigi_debug'
keyPassword 'droidkaigi'
storeFile file("${project.rootDir}/frontend/android/debug.keystore")
storePassword 'droidkaigi'
}
}

dataBinding {
enabled true
}
}

dependencies {
implementation project(':frontend:android')

implementation project(':corecomponent:androidcomponent')
implementation project(":data:repository")
implementation project(":feature:system")
implementation project(":model")

implementation Dep.InjectedVmProvider.extension
implementation Dep.InjectedVmProvider.injectedVmProvider
implementation Dep.InjectedVmProvider.ktx

implementation Dep.Groupie.groupie
implementation Dep.Groupie.databinding

implementation Dep.Picasso.picasso
implementation Dep.Picasso.picassoTransformation

kapt Dep.Dagger.compiler
kapt Dep.Dagger.androidProcessor
compileOnly Dep.Dagger.assistedInjectAnnotations
kapt Dep.Dagger.assistedInjectProcessor

testImplementation Dep.Test.junit
testImplementation Dep.MockK.jvm
androidTestImplementation Dep.Test.testRunner
androidTestImplementation Dep.Test.espressoCore
}
14 changes: 14 additions & 0 deletions feature/staff_dfm/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="io.github.droidkaigi.confsched2019.staff_dfm"
>

<dist:module
dist:onDemand="false"
dist:title="@string/title_staff_dfm"
>
<dist:fusing dist:include="true"/>
</dist:module>
</manifest>

Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package io.github.droidkaigi.confsched2019.staff_dfm.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import com.xwray.groupie.GroupAdapter
import com.xwray.groupie.Item
import com.xwray.groupie.ViewHolder
import io.github.droidkaigi.confsched2019.App
import io.github.droidkaigi.confsched2019.ext.changed
import io.github.droidkaigi.confsched2019.ext.requireValue
import io.github.droidkaigi.confsched2019.staff_dfm.R
import io.github.droidkaigi.confsched2019.staff_dfm.databinding.FragmentStaffSearchBinding
import io.github.droidkaigi.confsched2019.staff_dfm.ui.actioncreator.StaffSearchActionCreator
import io.github.droidkaigi.confsched2019.staff_dfm.ui.di.DaggerStaffComponent
import io.github.droidkaigi.confsched2019.staff_dfm.ui.di.StaffModule
import io.github.droidkaigi.confsched2019.staff_dfm.ui.item.StaffItem
import io.github.droidkaigi.confsched2019.staff_dfm.ui.store.StaffSearchStore
import me.tatarka.injectedvmprovider.InjectedViewModelProviders
import javax.inject.Inject
import javax.inject.Provider

class StaffSearchFragment : Fragment() {
private lateinit var binding: FragmentStaffSearchBinding

@Inject lateinit var searchActionCreator: StaffSearchActionCreator
private var searchView: SearchView? = null
@Inject lateinit var searchStoreProvider: Provider<StaffSearchStore>
private val searchStore: StaffSearchStore by lazy {
InjectedViewModelProviders.of(requireActivity()).get(searchStoreProvider)
}

private val groupAdapter = GroupAdapter<ViewHolder>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
setHasOptionsMenu(true)
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_staff_search,
container,
false
)
return binding.root
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)

val appComponent = (requireContext().applicationContext as App).appCmponent
val component = DaggerStaffComponent.builder()
.appComponent(appComponent)
.staffModule(StaffModule(this))
.build()
component.inject(this)

binding.searchRecycler.adapter = groupAdapter

searchStore.loadingState.changed(viewLifecycleOwner) { loadingState ->
binding.progressBar.isVisible = loadingState.isLoading
}

searchStore.staffContents.changed(viewLifecycleOwner) { staffContents ->
searchActionCreator.search(
searchStore.query,
staffContents
)
}

searchStore.searchResult.changed(viewLifecycleOwner) { result ->
val items = mutableListOf<Item<*>>()
items += result.staffs.map { staff -> StaffItem(staff) }
groupAdapter.update(items)
}

if (!searchStore.loadingState.requireValue().isLoaded) {
searchActionCreator.load()
}
}

override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_toolbar_staff_search, menu)

val searchMenuItem: MenuItem = menu.findItem(R.id.menu_search)
searchView = (searchMenuItem.actionView as SearchView).apply {
setQuery(searchStore.query, false)
isIconified = false
clearFocus()
imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
queryHint = getString(R.string.staff_search_hint)
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(s: String): Boolean {
return false
}

override fun onQueryTextChange(query: String): Boolean {
searchActionCreator.search(
query,
searchStore.staffContents.requireValue()
)
return false
}
})
maxWidth = Int.MAX_VALUE
setOnCloseListener { false }
}
}

override fun onPause() {
super.onPause()
hideKeyboard()
}

private fun hideKeyboard() {
val imm = ContextCompat.getSystemService(requireContext(), InputMethodManager::class.java)
val view = activity?.currentFocus
imm?.hideSoftInputFromWindow(view?.windowToken, 0)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.github.droidkaigi.confsched2019.staff_dfm.ui.actioncreator

import androidx.lifecycle.Lifecycle
import io.github.droidkaigi.confsched2019.action.Action
import io.github.droidkaigi.confsched2019.data.repository.StaffRepository
import io.github.droidkaigi.confsched2019.di.PageScope
import io.github.droidkaigi.confsched2019.dispatcher.Dispatcher
import io.github.droidkaigi.confsched2019.ext.coroutineScope
import io.github.droidkaigi.confsched2019.model.LoadingState
import io.github.droidkaigi.confsched2019.model.StaffContents
import io.github.droidkaigi.confsched2019.model.StaffSearchResult
import io.github.droidkaigi.confsched2019.system.actioncreator.ErrorHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject

@PageScope
class StaffSearchActionCreator @Inject constructor(
override val dispatcher: Dispatcher,
private val staffRepository: StaffRepository,
@PageScope private val lifecycle: Lifecycle
) : CoroutineScope by lifecycle.coroutineScope, ErrorHandler {

fun load() = launch {
try {
dispatcher.dispatch(Action.StaffSearchLoadingStateChanged(LoadingState.LOADING))
// load cache data
dispatcher.dispatch(Action.StaffLoaded(staffRepository.staffContents()))
// fetch from api
staffRepository.refresh()
// load fetched data
dispatcher.dispatch(Action.StaffLoaded(staffRepository.staffContents()))
dispatcher.dispatch(Action.StaffSearchLoadingStateChanged(LoadingState.LOADED))
} catch (e: Exception) {
dispatcher.dispatch(Action.StaffSearchLoadingStateChanged(LoadingState.INITIALIZED))
}
}

fun search(query: String?, staffContents: StaffContents) {
// if we do not have query, we should show all staffs
if (query.isNullOrBlank()) {
dispatcher.launchAndDispatch(
Action.StaffSearchResultLoaded(
StaffSearchResult(
staffContents.staffs,
query
)
)
)
return
}
val searchResult = staffContents.search(query)
dispatcher.launchAndDispatch(Action.StaffSearchResultLoaded(searchResult))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.github.droidkaigi.confsched2019.staff_dfm.ui.di

import dagger.Component
import io.github.droidkaigi.confsched2019.di.AppComponent
import io.github.droidkaigi.confsched2019.di.PageScope
import io.github.droidkaigi.confsched2019.staff_dfm.ui.StaffSearchFragment

@PageScope
@Component(
modules = [
StaffModule::class
],
dependencies = [AppComponent::class]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝

)
interface StaffComponent {
@Component.Builder
interface Builder {
fun build(): StaffComponent
fun appComponent(appComponent: AppComponent): Builder
fun staffModule(staffModule: StaffModule): Builder
}

fun inject(fragment: StaffSearchFragment)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.github.droidkaigi.confsched2019.staff_dfm.ui.di

import androidx.lifecycle.Lifecycle
import dagger.Module
import dagger.Provides
import io.github.droidkaigi.confsched2019.di.PageScope
import io.github.droidkaigi.confsched2019.staff_dfm.ui.StaffSearchFragment

@Module
class StaffModule(private val fragment: StaffSearchFragment) {

@Provides
@PageScope
fun providesLifecycle(): Lifecycle {
return fragment.viewLifecycleOwner.lifecycle
}
}
Loading