lebonprix.apk/java/io/xdrm/lebonprix/HomeActivity.kt

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
}
}