mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-1803 App | Tech | Kotlin + Library updates (#704)
This commit is contained in:
parent
232b634e48
commit
53fd8e9061
31 changed files with 172 additions and 949 deletions
|
@ -1,14 +1,13 @@
|
|||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "kotlin-android-extensions"
|
||||
id "kotlin-kapt"
|
||||
id 'com.google.gms.google-services'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 34
|
||||
buildToolsVersion "32.0.0"
|
||||
buildToolsVersion "34.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.anytypeio.anytype.sample"
|
||||
|
@ -31,12 +30,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
|
@ -27,11 +27,6 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".search.SearchOnPageActivity"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize"/>
|
||||
<activity android:name=".DisabledAnimationActivity" />
|
||||
<activity android:name=".ScrollAndMove" />
|
||||
<activity android:name=".StyleActivity" />
|
||||
<activity android:name=".MainActivity" />
|
||||
<activity android:name=".StyleToolbarActivity"/>
|
||||
|
|
|
@ -1,266 +0,0 @@
|
|||
package com.anytypeio.anytype.sample
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_utils.ext.imm
|
||||
import com.anytypeio.anytype.core_utils.ui.ViewType
|
||||
import com.anytypeio.anytype.sample.adapter.AbstractAdapter
|
||||
import com.anytypeio.anytype.sample.adapter.AbstractHolder
|
||||
import kotlinx.android.synthetic.main.activity_disabled_animation.*
|
||||
import kotlinx.android.synthetic.main.item_editable.view.*
|
||||
import timber.log.Timber
|
||||
|
||||
class DisabledAnimationActivity : AppCompatActivity(R.layout.activity_disabled_animation) {
|
||||
|
||||
private val start: List<Mock>
|
||||
get() = mutableListOf(
|
||||
Mock(
|
||||
id = 0,
|
||||
text = "TITLE",
|
||||
type = 0,
|
||||
isFocused = false
|
||||
),
|
||||
Mock(
|
||||
id = 0,
|
||||
text = "BULLETED",
|
||||
type = 0,
|
||||
isFocused = true
|
||||
),
|
||||
Mock(
|
||||
id = 1,
|
||||
text = "PARAGRAPH 2",
|
||||
type = 0,
|
||||
isFocused = false
|
||||
)
|
||||
)
|
||||
|
||||
private val end: List<Mock>
|
||||
get() = listOf(
|
||||
Mock(
|
||||
id = 0,
|
||||
text = "TITLE",
|
||||
type = 0,
|
||||
isFocused = false
|
||||
),
|
||||
Mock(
|
||||
id = 0,
|
||||
text = "PARAGRAPH",
|
||||
type = 1,
|
||||
isFocused = true
|
||||
),
|
||||
Mock(
|
||||
id = 1,
|
||||
text = "PARAGRAPH 2",
|
||||
type = 0,
|
||||
isFocused = false
|
||||
)
|
||||
)
|
||||
|
||||
private val mockAdapter = MockAdapter(
|
||||
items = start.toMutableList()
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
recycler.apply {
|
||||
layoutManager = CustomLinearLayoutManager(context)
|
||||
adapter = mockAdapter
|
||||
itemAnimator = null
|
||||
}
|
||||
startButton.setOnClickListener {
|
||||
Timber.d("Start button clicked")
|
||||
mockAdapter.update(
|
||||
update = end
|
||||
)
|
||||
}
|
||||
|
||||
endButton.setOnClickListener {
|
||||
Timber.d("End button clicked")
|
||||
mockAdapter.update(
|
||||
update = start
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MockAdapter(val items: MutableList<Mock>) : AbstractAdapter<Mock>(items) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractHolder<Mock> {
|
||||
return when (viewType) {
|
||||
0 -> {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view = inflater.inflate(R.layout.item_editable, parent, false)
|
||||
MockHolder(view)
|
||||
}
|
||||
1 -> {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view = inflater.inflate(R.layout.item_editable, parent, false)
|
||||
MockHolder2(view)
|
||||
}
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return items[position].getViewType()
|
||||
}
|
||||
|
||||
override fun update(update: List<Mock>) {
|
||||
val old = ArrayList(items)
|
||||
val cb = Differ(old = old, new = update)
|
||||
val result = DiffUtil.calculateDiff(cb)
|
||||
items.clear()
|
||||
items.addAll(update)
|
||||
result.dispatchUpdatesTo(this)
|
||||
}
|
||||
}
|
||||
|
||||
class MockHolder(view: View) : AbstractHolder<Mock>(view) {
|
||||
|
||||
override fun bind(item: Mock) {
|
||||
Timber.d("Binding item: $item")
|
||||
itemView.input.setText(item.text)
|
||||
if (item.isFocused)
|
||||
focus()
|
||||
else
|
||||
itemView.input.clearFocus()
|
||||
}
|
||||
|
||||
private fun focus() {
|
||||
itemView.input.apply {
|
||||
post {
|
||||
if (!hasFocus()) {
|
||||
if (requestFocus()) {
|
||||
context.imm().showSoftInput(this, InputMethodManager.SHOW_FORCED)
|
||||
} else {
|
||||
Timber.d("Couldn't gain focus")
|
||||
}
|
||||
} else
|
||||
Timber.d("Already had focus")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MockHolder2(view: View) : AbstractHolder<Mock>(view) {
|
||||
|
||||
override fun bind(item: Mock) {
|
||||
Timber.d("Binding item: $item")
|
||||
itemView.input.setText(item.text)
|
||||
itemView.input.setTextColor(Color.GREEN)
|
||||
if (item.isFocused) focus()
|
||||
}
|
||||
|
||||
private fun focus() {
|
||||
itemView.input.apply {
|
||||
post {
|
||||
Timber.d("Focusing!")
|
||||
if (!hasFocus()) {
|
||||
if (requestFocus()) {
|
||||
context.imm().showSoftInput(this, InputMethodManager.SHOW_FORCED)
|
||||
} else {
|
||||
Timber.d("Couldn't gain focus")
|
||||
}
|
||||
} else
|
||||
Timber.d("Already had focus")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Mock(
|
||||
val id: Int,
|
||||
val text: String,
|
||||
val isFocused: Boolean,
|
||||
val type: Int
|
||||
) : ViewType {
|
||||
override fun getViewType(): Int = type
|
||||
}
|
||||
|
||||
class Differ(
|
||||
private val old: List<Mock>,
|
||||
private val new: List<Mock>
|
||||
) : DiffUtil.Callback() {
|
||||
|
||||
override fun getOldListSize(): Int = old.size
|
||||
override fun getNewListSize(): Int = new.size
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = old[oldItemPosition]
|
||||
val newItem = new[newItemPosition]
|
||||
Timber.d("areItemsTheSame for: $oldItem \n and \n $newItem")
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldItem = old[oldItemPosition]
|
||||
val newItem = new[newItemPosition]
|
||||
Timber.d("areContentsTheSame for: $oldItem \n and \n $newItem")
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
class CustomLinearLayoutManager(
|
||||
context: Context,
|
||||
//focus: View
|
||||
) : LinearLayoutManager(context) {
|
||||
|
||||
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
|
||||
Timber.d("OnInterceptFocusSearch view with text: ${(focused as EditText).text}")
|
||||
when (direction) {
|
||||
View.FOCUS_UP -> {
|
||||
Timber.d("OnInterceptFocusSearch direction: FOCUS_UP")
|
||||
}
|
||||
View.FOCUS_DOWN -> {
|
||||
Timber.d("OnInterceptFocusSearch direction: FOCUS_DOWN")
|
||||
}
|
||||
}
|
||||
//return super.onInterceptFocusSearch(focused, direction)
|
||||
|
||||
//val v = getChildAt(2)?.findViewById<EditText>(R.id.input)
|
||||
|
||||
//v?.requestFocus()
|
||||
|
||||
//Timber.d("At position 2 there is a view with text: ${(v as EditText).text}")
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun removeView(child: View?) {
|
||||
Timber.d("On remove view")
|
||||
super.removeView(child)
|
||||
}
|
||||
|
||||
override fun detachView(child: View) {
|
||||
Timber.d("On detach view")
|
||||
super.detachView(child)
|
||||
}
|
||||
|
||||
override fun onFocusSearchFailed(
|
||||
focused: View,
|
||||
focusDirection: Int,
|
||||
recycler: RecyclerView.Recycler,
|
||||
state: RecyclerView.State
|
||||
): View? {
|
||||
Timber.d("onFocusSearchFailed for view with text: ${(focused as EditText).text}")
|
||||
when (focusDirection) {
|
||||
View.FOCUS_UP -> {
|
||||
Timber.d("onFocusSearchFailed direction: FOCUS_UP")
|
||||
}
|
||||
View.FOCUS_DOWN -> {
|
||||
Timber.d("onFocusSearchFailed direction: FOCUS_DOWN")
|
||||
}
|
||||
}
|
||||
return super.onFocusSearchFailed(focused, focusDirection, recycler, state)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package com.anytypeio.anytype.sample
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.android.synthetic.main.activity_long_clicked.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Emulate long click on text block, with following
|
||||
* Selection and Focus events
|
||||
*
|
||||
*/
|
||||
class LongClickActivity: AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_long_clicked)
|
||||
|
||||
textInputWidget.enableEditMode()
|
||||
textInputWidget.setText("Hi, Konstantin")
|
||||
|
||||
textInputWidget.setOnClickListener {
|
||||
Timber.d("Single clicked!")
|
||||
}
|
||||
|
||||
textInputWidget.setOnLongClickListener {
|
||||
Timber.d("Long clicked!")
|
||||
lifecycleScope.launch {
|
||||
delay(500)
|
||||
Timber.d("Enabling read mode")
|
||||
textInputWidget.enableReadMode()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
textInputWidget.selectionWatcher = {intRange: IntRange ->
|
||||
Timber.d("Selection changed : $intRange")
|
||||
}
|
||||
|
||||
textInputWidget.setOnFocusChangeListener { v, hasFocus ->
|
||||
Timber.d("Focus changed:$hasFocus")
|
||||
}
|
||||
|
||||
button.setOnClickListener {
|
||||
textInputWidget.enableEditMode()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package com.anytypeio.anytype.sample
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Annotation
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.item_test.view.*
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val annotation = Annotation("key", "rounded")
|
||||
val list = mutableListOf<Item>()
|
||||
|
||||
val range = IntRange(0, 50)
|
||||
range.forEach {
|
||||
|
||||
var t = "I am block number $it"
|
||||
val spannable = SpannableString(t)
|
||||
if (it == 10) {
|
||||
spannable.setSpan(annotation, 0, t.length, Spannable.SPAN_INCLUSIVE_INCLUSIVE)
|
||||
list.add(Item(text = spannable))
|
||||
} else {
|
||||
list.add(Item(text = spannable))
|
||||
}
|
||||
}
|
||||
|
||||
with(recyclerView) {
|
||||
layoutManager = LinearLayoutManager(this@MainActivity)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
this@MainActivity,
|
||||
DividerItemDecoration.VERTICAL
|
||||
)
|
||||
)
|
||||
adapter = MarkupAdapter(list) { pos: Int ->
|
||||
(this.adapter as MarkupAdapter).setData(
|
||||
Item(SpannableString("I am block number $pos")),
|
||||
pos
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MarkupAdapter(private val data: MutableList<Item>, private val listener: (Int) -> Unit) :
|
||||
RecyclerView.Adapter<MarkupAdapter.MarkupViewHolder>() {
|
||||
|
||||
fun setData(item: Item, position: Int) {
|
||||
data[position] = item
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MarkupViewHolder {
|
||||
val view =
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.item_test, parent, false)
|
||||
return MarkupViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun onBindViewHolder(holder: MarkupViewHolder, position: Int) {
|
||||
holder.bind(data[position].text, listener)
|
||||
}
|
||||
|
||||
class MarkupViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind(text: SpannableString, listener: (Int) -> Unit) {
|
||||
itemView.item.text = text
|
||||
itemView.setOnClickListener {
|
||||
listener.invoke(adapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Item(val text: SpannableString)
|
|
@ -1,406 +0,0 @@
|
|||
package com.anytypeio.anytype.sample
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import kotlinx.android.synthetic.main.activity_scroll_and_move.*
|
||||
import kotlinx.android.synthetic.main.item_scroll_and_move.view.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.consumeAsFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class ScrollAndMove : AppCompatActivity() {
|
||||
|
||||
private val decoration = object : RecyclerView.ItemDecoration() {
|
||||
|
||||
var divider: Drawable? = null
|
||||
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
parent.adapter?.itemCount?.let { count ->
|
||||
if (count == 1) {
|
||||
outRect.bottom = screenHeight
|
||||
outRect.top = screenHeight
|
||||
} else {
|
||||
if (parent.getChildAdapterPosition(view) == count - 1) {
|
||||
outRect.bottom = screenHeight
|
||||
} else if (parent.getChildAdapterPosition(view) == 0) {
|
||||
outRect.top = screenHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
//c.save()
|
||||
|
||||
Timber.d("child count: ${parent.childCount}")
|
||||
|
||||
for (i in 0 until parent.childCount) {
|
||||
|
||||
Timber.d("idx: $i")
|
||||
|
||||
val child = parent.getChildAt(i)
|
||||
|
||||
val pos = parent.getChildAdapterPosition(child)
|
||||
|
||||
Timber.d("onDrawOver position: $pos")
|
||||
|
||||
var targeted = false
|
||||
var ratio: Float = 0.0f
|
||||
|
||||
(parent.adapter as? Adapter)?.let { adapter ->
|
||||
val item = adapter.models[pos]
|
||||
targeted = item.isTargeted
|
||||
ratio = item.ratio
|
||||
}
|
||||
|
||||
if (!targeted) continue
|
||||
|
||||
Timber.d("onDrawOver ratio: $ratio")
|
||||
|
||||
if (ratio in END_RANGE) {
|
||||
Timber.d("Drawing bottom decoration for: $pos")
|
||||
|
||||
val left = 0
|
||||
val right = parent.width
|
||||
val top = child.bottom
|
||||
val bottom = child.bottom + divider!!.intrinsicHeight
|
||||
|
||||
|
||||
divider?.setBounds(left, top, right, bottom)
|
||||
divider?.draw(c)
|
||||
} else if (ratio in START_RANGE) {
|
||||
Timber.d("Drawing bottom decoration for: $pos")
|
||||
|
||||
val left = 0
|
||||
val right = parent.width
|
||||
val top = child.top
|
||||
val bottom = child.top + divider!!.intrinsicHeight
|
||||
|
||||
divider?.setBounds(left, top, right, bottom)
|
||||
divider?.draw(c)
|
||||
}
|
||||
}
|
||||
|
||||
//c.restore()
|
||||
}
|
||||
}
|
||||
|
||||
private val channel = Channel<Unit>()
|
||||
|
||||
init {
|
||||
channel
|
||||
.consumeAsFlow()
|
||||
.onEach { Timber.d("event!") }
|
||||
.mapLatest {
|
||||
calculate()
|
||||
}
|
||||
.onEach { Timber.d("done") }
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
val screenHeight: Int
|
||||
get() {
|
||||
val wm = (getSystemService(Context.WINDOW_SERVICE) as WindowManager)
|
||||
val display = wm.defaultDisplay
|
||||
val p = Point()
|
||||
display.getSize(p)
|
||||
return p.y / 2
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_scroll_and_move)
|
||||
setup()
|
||||
}
|
||||
|
||||
fun toggleDecoration() {
|
||||
if (recycler.itemDecorationCount > 0) {
|
||||
recycler.removeItemDecoration(decoration)
|
||||
} else {
|
||||
recycler.addItemDecoration(decoration)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun setup() {
|
||||
|
||||
toogleDecoration.setOnClickListener {
|
||||
toggleDecoration()
|
||||
}
|
||||
|
||||
apply.setOnClickListener {
|
||||
move()
|
||||
}
|
||||
|
||||
val text = getString(R.string.placeholder_text)
|
||||
|
||||
val models = mutableListOf<Model>().apply {
|
||||
repeat(MODEL_COUNT) { count ->
|
||||
add(
|
||||
Model(
|
||||
text = "$count. $text",
|
||||
isSelected = count in 0..1
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
decoration.divider = getDrawable(R.drawable.scroll_and_move_divider)
|
||||
|
||||
recycler.apply {
|
||||
|
||||
addItemDecoration(decoration)
|
||||
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
|
||||
adapter = Adapter(
|
||||
models = models
|
||||
)
|
||||
|
||||
addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
//Timber.d("On scrolled dy: $dy")
|
||||
//Timber.d("Current scroll y: ${recycler.scrollY}")
|
||||
}
|
||||
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
lifecycleScope.launch {
|
||||
channel.send(Unit)
|
||||
}
|
||||
}
|
||||
//return
|
||||
|
||||
// Timber.d("Vertical scroll: ${recycler.computeVerticalScrollOffset()}")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun move() {
|
||||
|
||||
val adapter = (recycler.adapter as Adapter)
|
||||
val models = adapter.models
|
||||
val target = models.firstOrNull { it.isTargeted }
|
||||
|
||||
check(models.count { it.isTargeted } == 1) { "merde!" }
|
||||
|
||||
if (target != null) {
|
||||
when (target.ratio) {
|
||||
in START_RANGE -> {
|
||||
// top
|
||||
val result = mutableListOf<Model>()
|
||||
val selected = models.filter { it.isSelected }
|
||||
models.forEach { model ->
|
||||
if (!model.isSelected) {
|
||||
if (!model.isTargeted)
|
||||
result.add(model)
|
||||
if (model.isTargeted) {
|
||||
result.addAll(selected)
|
||||
result.add(
|
||||
model.copy(
|
||||
isTargeted = false,
|
||||
ratio = 0.0f
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.update(result)
|
||||
}
|
||||
in MIDDLE_RANDE -> {
|
||||
// inside
|
||||
val update =
|
||||
models.filter { !it.isSelected }.map { it.copy(isTargeted = false) }
|
||||
adapter.update(update)
|
||||
}
|
||||
in END_RANGE -> {
|
||||
// bottom
|
||||
val result = mutableListOf<Model>()
|
||||
val selected = models.filter { it.isSelected }
|
||||
models.forEach { model ->
|
||||
if (!model.isSelected) {
|
||||
if (!model.isTargeted)
|
||||
result.add(model)
|
||||
if (model.isTargeted) {
|
||||
result.add(
|
||||
model.copy(
|
||||
isTargeted = false,
|
||||
ratio = 0.0f
|
||||
)
|
||||
)
|
||||
result.addAll(selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.update(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toast("well done!")
|
||||
}
|
||||
|
||||
fun calculate() {
|
||||
|
||||
val lm = recycler.layoutManager as LinearLayoutManager
|
||||
|
||||
recycler.findChildViewUnder(0f, screenHeight.toFloat())?.let { view ->
|
||||
|
||||
Timber.d("Found view: $view")
|
||||
|
||||
val first = lm.findFirstVisibleItemPosition()
|
||||
val last = lm.findLastVisibleItemPosition()
|
||||
|
||||
val position = recycler.getChildAdapterPosition(view)
|
||||
|
||||
val center = screenHeight.toFloat()
|
||||
|
||||
val top = view.top
|
||||
val height = view.height
|
||||
|
||||
val ratio: Float
|
||||
|
||||
if (center < top) {
|
||||
Timber.d("top of the view is below center")
|
||||
val delta = top - center
|
||||
ratio = delta / height
|
||||
Timber.d("Ration = $ratio")
|
||||
} else {
|
||||
Timber.d("top ($top) of the view is above center ($center)")
|
||||
val delta = center - top
|
||||
ratio = delta / height
|
||||
Timber.d("Ration = $ratio")
|
||||
}
|
||||
|
||||
Timber.d("Target position: $position")
|
||||
Timber.d("First visible item position: $first")
|
||||
Timber.d("Last visible item position: $last")
|
||||
|
||||
(recycler.adapter as Adapter).let {
|
||||
val update = it.models.mapIndexed { index, model ->
|
||||
model.copy(
|
||||
isTargeted = index == position,
|
||||
ratio = ratio
|
||||
)
|
||||
}
|
||||
it.update(update)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MODEL_COUNT = 20
|
||||
val START_RANGE = 0.0..0.2
|
||||
val MIDDLE_RANDE = 0.2..0.8
|
||||
val END_RANGE = 0.8..1.0
|
||||
}
|
||||
}
|
||||
|
||||
data class Model(
|
||||
val text: String,
|
||||
val isTargeted: Boolean = false,
|
||||
val isSelected: Boolean = false,
|
||||
val ratio: Float = 0.0f
|
||||
)
|
||||
|
||||
class Adapter(var models: List<Model>) : RecyclerView.Adapter<Adapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup, viewType: Int
|
||||
): ViewHolder = ViewHolder(
|
||||
view = LayoutInflater.from(parent.context).inflate(
|
||||
R.layout.item_scroll_and_move,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
override fun getItemCount(): Int = models.size
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(models[position])
|
||||
}
|
||||
|
||||
fun update(update: List<Model>) {
|
||||
this.models = update
|
||||
notifyDataSetChanged()
|
||||
Timber.d("Updated data set: $update")
|
||||
}
|
||||
|
||||
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val top = itemView.topDivider
|
||||
private val bottom = itemView.bottomDivider
|
||||
private val paragraph = itemView.paragraph
|
||||
|
||||
fun bind(model: Model) {
|
||||
|
||||
if (model.isTargeted) {
|
||||
when (model.ratio) {
|
||||
in START_RANGE -> {
|
||||
itemView.setBackgroundColor(Color.WHITE)
|
||||
}
|
||||
in MIDDLE_RANDE -> {
|
||||
itemView.setBackgroundColor(Color.parseColor("#FFB522"))
|
||||
}
|
||||
in END_RANGE -> {
|
||||
itemView.setBackgroundColor(Color.WHITE)
|
||||
}
|
||||
}
|
||||
Timber.d("Binding selected model: $model")
|
||||
} else {
|
||||
itemView.setBackgroundColor(Color.WHITE)
|
||||
//bottom.invisible()
|
||||
//top.invisible()
|
||||
Timber.d("Binding simple model")
|
||||
}
|
||||
|
||||
if (model.isSelected) {
|
||||
paragraph.setTextColor(
|
||||
Color.parseColor("#3E58EB")
|
||||
)
|
||||
} else {
|
||||
paragraph.setTextColor(
|
||||
Color.parseColor("#2C2B27")
|
||||
)
|
||||
}
|
||||
|
||||
paragraph.text = model.text
|
||||
itemView.isSelected = false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val START_RANGE = 0.0..0.2
|
||||
val MIDDLE_RANDE = 0.2..0.8
|
||||
val END_RANGE = 0.8..1.0
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.anytypeio.anytype.sample.search
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.anytypeio.anytype.sample.R
|
||||
import kotlinx.android.synthetic.main.activity_search_on_page.*
|
||||
|
||||
class SearchOnPageActivity : AppCompatActivity(R.layout.activity_search_on_page) {
|
||||
|
||||
private val items = mutableListOf<SearchOnPageAdapter.Item>().apply {
|
||||
repeat(10) {
|
||||
add(
|
||||
SearchOnPageAdapter.Item(
|
||||
id = it,
|
||||
txt = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val mockAdapter = SearchOnPageAdapter(
|
||||
items = items
|
||||
)
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
recycler.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = mockAdapter
|
||||
}
|
||||
search.doOnTextChanged { text, start, before, count ->
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.anytypeio.anytype.sample.search
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.anytypeio.anytype.sample.R
|
||||
import com.anytypeio.anytype.sample.adapter.AbstractAdapter
|
||||
import com.anytypeio.anytype.sample.adapter.AbstractHolder
|
||||
import kotlinx.android.synthetic.main.item_editable.view.*
|
||||
|
||||
class SearchOnPageAdapter(
|
||||
private var items: List<Item>
|
||||
) : AbstractAdapter<SearchOnPageAdapter.Item>(items) {
|
||||
|
||||
override fun update(update: List<Item>) {
|
||||
this.items = update
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractHolder<Item> {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view = inflater.inflate(R.layout.item_editable, parent, false)
|
||||
return ItemViewHolder(view)
|
||||
}
|
||||
|
||||
data class Item(
|
||||
val id: Int,
|
||||
val txt: String
|
||||
)
|
||||
|
||||
class ItemViewHolder(view: View) : AbstractHolder<Item>(view) {
|
||||
override fun bind(item: Item) {
|
||||
itemView.input.setText(item.txt)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue