240 lines
7.7 KiB
Kotlin
240 lines
7.7 KiB
Kotlin
package io.xdrm.lebonprix
|
|
|
|
import android.content.Intent
|
|
import android.support.v7.app.AppCompatActivity
|
|
import android.os.Bundle
|
|
import android.os.Handler
|
|
import android.text.Editable
|
|
import android.text.TextWatcher
|
|
import android.util.Log
|
|
import android.view.KeyEvent
|
|
import android.view.WindowManager
|
|
import android.widget.Toast
|
|
import io.xdrm.lebonprix.anim.HomeFilterAnimation
|
|
import io.xdrm.lebonprix.anim.UnderlineAnimation
|
|
import io.xdrm.lebonprix.api.CategoryFetcher
|
|
import io.xdrm.lebonprix.api.PricesFetcher
|
|
import io.xdrm.lebonprix.model.Category
|
|
import io.xdrm.lebonprix.model.CategoryItem
|
|
import io.xdrm.lebonprix.model.CategoryItemStore
|
|
import io.xdrm.todoleast.adapter.CategoryAdapter
|
|
import kotlinx.android.synthetic.main.activity_home.*
|
|
import kotlinx.coroutines.*
|
|
import okhttp3.OkHttpClient
|
|
import org.json.JSONArray
|
|
|
|
|
|
|
|
class HomeActivity : AppCompatActivity() {
|
|
|
|
companion object {
|
|
val SEARCH_KEY_TIMEOUT: Long = 500
|
|
}
|
|
|
|
private val categoryStore = CategoryItemStore
|
|
private val categoryAdapter = CategoryAdapter(CategoryItemStore.data)
|
|
|
|
private lateinit var underline_animator: UnderlineAnimation
|
|
private lateinit var load_animator: HomeFilterAnimation
|
|
|
|
private var last_search_time: Long = 0
|
|
private var last_search: String = ""
|
|
private var httpJob: Job? = null
|
|
|
|
private val httpClient = OkHttpClient()
|
|
private var ended = false
|
|
private var loader: Job? = null
|
|
|
|
private lateinit var apiCategories: CategoryFetcher
|
|
private lateinit var apiPrices: PricesFetcher
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
runOnUiThread { load_animator.stage(HomeFilterAnimation.CLEAR_ALL).during(0).start() }
|
|
}
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
setContentView(R.layout.activity_home)
|
|
|
|
// manage underline animation
|
|
underline_animator = UnderlineAnimation(search_underline)
|
|
load_animator = HomeFilterAnimation(applicationContext, filter)
|
|
apiCategories = CategoryFetcher(this, search_underline, underline_animator, httpClient)
|
|
apiPrices = PricesFetcher(this, httpClient)
|
|
|
|
// listen for focus changes
|
|
search.setOnFocusChangeListener{v, focus ->
|
|
if( focus ) underline_animator.animate(0F, 1F).during(30).start()
|
|
else underline_animator.animate(1F, 0F).during(30).start()
|
|
}
|
|
|
|
// listen for clicks
|
|
search.setOnClickListener {
|
|
underline_animator.animate(0F, 1F).during(30).start()
|
|
}
|
|
|
|
category_grid.adapter = categoryAdapter
|
|
|
|
// launch category fetch
|
|
search.addTextChangedListener(object : TextWatcher{
|
|
override fun afterTextChanged(s: Editable?) {}
|
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
|
|
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
categoryCoordinator(search.text.toString().trim())
|
|
|
|
// manage when key is pressed before the SEARCH_KEY_TIMEOUT is reached, so ignored
|
|
Handler().postDelayed({
|
|
categoryCoordinator(search.text.toString().trim())
|
|
}, HomeActivity.SEARCH_KEY_TIMEOUT)
|
|
}
|
|
})
|
|
|
|
|
|
// launch search
|
|
search.setOnKeyListener { v, keyCode, event ->
|
|
if( event.action != KeyEvent.ACTION_DOWN || keyCode != KeyEvent.KEYCODE_ENTER )
|
|
return@setOnKeyListener false
|
|
|
|
launch_search()
|
|
return@setOnKeyListener true
|
|
}
|
|
search_button.setOnClickListener { launch_search() }
|
|
|
|
updateCategories(JSONArray("[]"))
|
|
|
|
}
|
|
|
|
private fun categoryCoordinator(keywords: String){
|
|
val now = System.currentTimeMillis()
|
|
if( last_search_time > 0 && now-last_search_time < HomeActivity.SEARCH_KEY_TIMEOUT )
|
|
return
|
|
|
|
if( keywords == last_search )
|
|
return
|
|
|
|
last_search_time = now
|
|
last_search = keywords
|
|
|
|
if( httpJob != null && httpJob!!.isActive ) {
|
|
httpJob!!.cancel()
|
|
underline_animator.animateContinue(1F).during(0).start()
|
|
}
|
|
|
|
httpJob = GlobalScope.launch {
|
|
updateCategories( apiCategories.fetch(last_search) )
|
|
}
|
|
}
|
|
|
|
private fun launch_search(){
|
|
val selectedCategories = getCategories()
|
|
|
|
// (1) Loader
|
|
if( loader != null && loader!!.isActive )
|
|
loader!!.cancel()
|
|
|
|
loader = GlobalScope.launch(Dispatchers.Main) {
|
|
// launch animation
|
|
load_animator.stage(HomeFilterAnimation.STAGE_CLEAN).during(300).start()
|
|
delay(300)
|
|
load_animator.stage(HomeFilterAnimation.STAGE_CENTER).during(300).start()
|
|
delay(300)
|
|
while(true){
|
|
load_animator.stage(HomeFilterAnimation.STAGE_LOAD).during(5000).start()
|
|
delay(5000)
|
|
}
|
|
}
|
|
|
|
if( httpJob != null && httpJob!!.isActive )
|
|
httpJob!!.cancel()
|
|
|
|
httpJob = GlobalScope.launch {
|
|
|
|
// exec request
|
|
val prices = apiPrices.fetch(search.text.toString(), selectedCategories.toTypedArray())
|
|
loader?.cancel()
|
|
|
|
// stop all if no result
|
|
if( prices.isNullOrEmpty() ) {
|
|
runOnUiThread {
|
|
load_animator.stage(HomeFilterAnimation.CLEAR_ALL).during(0).start()
|
|
}
|
|
return@launch
|
|
}
|
|
|
|
// transition
|
|
runOnUiThread {
|
|
load_animator.stage(HomeFilterAnimation.STAGE_END).during(500).start()
|
|
}
|
|
delay(500)
|
|
|
|
val int = Intent(this@HomeActivity, ResultActivity::class.java)
|
|
int.putExtra("prices", prices)
|
|
startActivity(int)
|
|
overridePendingTransition(0, 0)
|
|
}
|
|
}
|
|
|
|
private fun updateCategories(categories: JSONArray){
|
|
// 1. Update categories
|
|
categoryStore.data.clear()
|
|
categoryStore.data.add(CategoryItem(Category.ALL, false, 1F)) // preselected
|
|
var total = 0
|
|
|
|
for( i in 0..categories.length() ){
|
|
// extract features
|
|
var label: String = ""
|
|
var count: Int = 0
|
|
try {
|
|
label = categories.getJSONArray(i).getString(0)
|
|
count = categories.getJSONArray(i).getInt(1)
|
|
}catch(e: Exception){ continue }
|
|
|
|
total += count
|
|
|
|
// try to parse
|
|
val cat: Category = Category.fromLabel(label) ?: continue
|
|
|
|
// add category
|
|
categoryStore.data.add(CategoryItem(cat, false, count.toFloat()))
|
|
|
|
// progress feedback
|
|
val progress = 0.5F + 0.5*i/categories.length()
|
|
runOnUiThread{ underline_animator.animateContinue(progress.toFloat()).during(200).start() }
|
|
}
|
|
|
|
for( i in 1..categories.length() ){
|
|
categoryStore.data[i].count /= total
|
|
}
|
|
|
|
// 3. update adapter
|
|
runOnUiThread{
|
|
categoryAdapter.notifyDataSetChanged()
|
|
underline_animator.animateContinue(1F).during(10).start()
|
|
}
|
|
}
|
|
|
|
|
|
private fun getCategories() : MutableList<String> {
|
|
|
|
val list = mutableListOf<String>()
|
|
|
|
for( item in categoryStore.data ){
|
|
// not selected -> ignore
|
|
if( !item.selected )
|
|
continue;
|
|
|
|
// select all -> return empty
|
|
if( item.category == Category.ALL )
|
|
return mutableListOf()
|
|
|
|
// else, add each one
|
|
list.add(item.category.label)
|
|
}
|
|
|
|
return list
|
|
}
|
|
}
|