1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-3601 App | Tech | Release 10 stabilisation (#2354)

This commit is contained in:
Evgenii Kozlov 2025-04-24 16:31:56 +02:00
parent df87f33b9d
commit d38ead4273
9 changed files with 191 additions and 76 deletions

View file

@ -215,7 +215,13 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
)
)
} else {
controller.navigate(R.id.actionOpenSpaceFromVault)
controller.navigate(
R.id.actionOpenSpaceFromVault,
HomeScreenFragment.args(
space = command.space,
deeplink = null
)
)
}
proceedWithOpenObjectNavigation(command.navigation)
}.onFailure {

View file

@ -150,7 +150,13 @@ class VaultFragment : BaseComposeFragment() {
private fun proceed(destination: Navigation) {
when (destination) {
is Navigation.OpenObject -> runCatching {
findNavController().navigate(R.id.actionOpenSpaceFromVault)
findNavController().navigate(
R.id.actionOpenSpaceFromVault,
HomeScreenFragment.args(
space = destination.space,
deeplink = null
)
)
navigation().openDocument(
target = destination.ctx,
space = destination.space
@ -159,7 +165,13 @@ class VaultFragment : BaseComposeFragment() {
Timber.e(it, "Error while opening object from vault")
}
is Navigation.OpenSet -> runCatching {
findNavController().navigate(R.id.actionOpenSpaceFromVault)
findNavController().navigate(
R.id.actionOpenSpaceFromVault,
HomeScreenFragment.args(
space = destination.space,
deeplink = null
)
)
navigation().openObjectSet(
target = destination.ctx,
space = destination.space,
@ -169,7 +181,13 @@ class VaultFragment : BaseComposeFragment() {
Timber.e(it, "Error while opening set or collection from vault")
}
is Navigation.OpenChat -> {
findNavController().navigate(R.id.actionOpenSpaceFromVault)
findNavController().navigate(
R.id.actionOpenSpaceFromVault,
HomeScreenFragment.args(
space = destination.space,
deeplink = null
)
)
navigation().openChat(
target = destination.ctx,
space = destination.space
@ -177,7 +195,13 @@ class VaultFragment : BaseComposeFragment() {
}
is Navigation.OpenDateObject -> {
runCatching {
findNavController().navigate(R.id.actionOpenSpaceFromVault)
findNavController().navigate(
R.id.actionOpenSpaceFromVault,
HomeScreenFragment.args(
space = destination.space,
deeplink = null
)
)
navigation().openDateObject(
objectId = destination.ctx,
space = destination.space
@ -189,7 +213,13 @@ class VaultFragment : BaseComposeFragment() {
is Navigation.OpenParticipant -> {
runCatching {
findNavController().navigate(R.id.actionOpenSpaceFromVault)
findNavController().navigate(
R.id.actionOpenSpaceFromVault,
HomeScreenFragment.args(
space = destination.space,
deeplink = null
)
)
navigation().openParticipantObject(
objectId = destination.ctx,
space = destination.space
@ -205,7 +235,7 @@ class VaultFragment : BaseComposeFragment() {
}
override fun onApplyWindowRootInsets(view: View) {
if (USE_EDGE_TO_EDGE && SDK_INT >= EDGE_TO_EDGE_MIN_SDK) {
if (SDK_INT >= EDGE_TO_EDGE_MIN_SDK) {
// Do nothing.
} else {
super.onApplyWindowRootInsets(view)

View file

@ -401,15 +401,6 @@
android:name="com.anytypeio.anytype.ui.splash.SplashFragment"
android:label="SplashFragment"
tools:layout="@layout/fragment_splash">
<action
android:id="@+id/action_splashScreen_to_homeScreen"
app:destination="@id/homeScreen"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@+id/splashScreen"
app:popUpToInclusive="true" />
<action
android:id="@+id/actionOpenVaultFromSplash"
app:destination="@id/vaultScreen"

View file

@ -36,14 +36,18 @@ abstract class BaseBottomSheetComposeFragment : BottomSheetDialogFragment() {
args: Bundle? = null
) {
jobs += this.lifecycleScope.launch {
throttleFlow.emit {
if (currentNavigationId == getNavigationId()) {
try {
findNavController().navigate(id, args)
} catch (e: Exception) {
Timber.e(e, "safeNavigateMethod is not safe!")
try {
throttleFlow.emit {
if (currentNavigationId == getNavigationId()) {
try {
findNavController().navigate(id, args)
} catch (e: Exception) {
Timber.e(e, "safeNavigateMethod is not safe!")
}
}
}
} catch (e: Exception) {
Timber.e(e, "Error during emit in safeNavigate")
}
}
}
@ -70,11 +74,16 @@ abstract class BaseBottomSheetComposeFragment : BottomSheetDialogFragment() {
protected fun DialogFragment.showChildFragment(tag: String? = null) {
jobs += this@BaseBottomSheetComposeFragment.lifecycleScope.launch {
throttleFlow.emit {
show(
this@BaseBottomSheetComposeFragment.childFragmentManager,
tag
)
try {
throttleFlow.emit {
try {
show(this@BaseBottomSheetComposeFragment.childFragmentManager, tag)
} catch (e: Exception) {
Timber.e(e, "Error while showing child dialog")
}
}
} catch (e: Exception) {
Timber.e(e, "Error during emit in showChildFragment")
}
}
}
@ -118,5 +127,14 @@ abstract class BaseBottomSheetComposeFragment : BottomSheetDialogFragment() {
fun Fragment.getNavigationId() = findNavController().currentDestination?.id
fun <T> BaseBottomSheetComposeFragment.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope)
jobs += flow
.cancellable()
.onEach {
try {
body(it)
} catch (e: Exception) {
Timber.e(e, "Unhandled exception in proceed flow")
}
}
.launchIn(lifecycleScope)
}

View file

@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
import com.google.android.material.R.id.design_bottom_sheet as BOTTOM_SHEET_ID
abstract class BaseBottomSheetFragment<T : ViewBinding>(
@ -33,13 +34,18 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
val sheet: FrameLayout? get() = dialog?.findViewById(BOTTOM_SHEET_ID)
private val throttleFlow = MutableSharedFlow<() -> Unit>(0)
val jobs = mutableListOf<Job>()
protected fun throttle(task: () -> Unit) {
jobs += this.lifecycleScope.launch { throttleFlow.emit { task() } }
jobs += this.lifecycleScope.launch {
try {
throttleFlow.emit { task() }
} catch (e: Exception) {
Timber.e(e, "Error during emit in throttle()")
}
}
}
val jobs = mutableListOf<Job>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -64,7 +70,13 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
expand()
}
proceed(throttleFlow.throttleFirst(LONG_THROTTLE_DURATION)) { it() }
proceed(throttleFlow.throttleFirst(LONG_THROTTLE_DURATION)) {
try {
it()
} catch (e: Exception) {
Timber.e(e, "Unhandled exception in throttled flow execution")
}
}
}
override fun onStop() {
@ -77,7 +89,17 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
protected fun DialogFragment.showChildFragment(tag: String? = null) {
jobs += this@BaseBottomSheetFragment.lifecycleScope.launch {
throttleFlow.emit { show(this@BaseBottomSheetFragment.childFragmentManager, tag) }
try {
throttleFlow.emit {
try {
show(this@BaseBottomSheetFragment.childFragmentManager, tag)
} catch (e: Exception) {
Timber.e(e, "Error while showing child dialog")
}
}
} catch (e: Exception) {
Timber.e(e, "Error during emit in showChildFragment")
}
}
}
@ -87,18 +109,14 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
}
fun skipCollapsed() {
sheet?.let { sheet ->
BottomSheetBehavior.from(sheet).apply {
skipCollapsed = true
}
sheet?.let {
BottomSheetBehavior.from(it).skipCollapsed = true
}
}
fun expand() {
sheet?.let { sheet ->
BottomSheetBehavior.from(sheet).apply {
state = BottomSheetBehavior.STATE_EXPANDED
}
sheet?.let {
BottomSheetBehavior.from(it).state = BottomSheetBehavior.STATE_EXPANDED
}
}
@ -106,17 +124,25 @@ abstract class BaseBottomSheetFragment<T : ViewBinding>(
sheet?.layoutParams?.height = ViewGroup.LayoutParams.MATCH_PARENT
}
abstract fun injectDependencies()
abstract fun releaseDependencies()
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
abstract fun injectDependencies()
abstract fun releaseDependencies()
protected abstract fun inflateBinding(inflater: LayoutInflater, container: ViewGroup?): T
}
fun <T> BaseBottomSheetFragment<*>.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope)
jobs += flow
.cancellable()
.onEach {
try {
body(it)
} catch (e: Exception) {
Timber.e(e, "Unhandled exception in proceed flow")
}
}
.launchIn(lifecycleScope)
}

View file

@ -22,7 +22,7 @@ abstract class BaseBottomSheetTextInputFragment<T : ViewBinding>(
}
private fun setupWindowInsetAnimation() {
if (BuildConfig.USE_NEW_WINDOW_INSET_API && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
textInput.syncFocusWithImeVisibility()
}
}

View file

@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
abstract class BaseComposeFragment : Fragment() {
@ -42,26 +43,37 @@ abstract class BaseComposeFragment : Fragment() {
}
open fun onApplyWindowRootInsets(view: View) {
if (BuildConfig.USE_NEW_WINDOW_INSET_API && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val deferringInsetsListener = RootViewDeferringInsetsCallback(
persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
deferredInsetTypes = WindowInsetsCompat.Type.ime()
)
ViewCompat.setWindowInsetsAnimationCallback(view, deferringInsetsListener)
ViewCompat.setOnApplyWindowInsetsListener(view, deferringInsetsListener)
}
}
protected fun DialogFragment.showChildFragment(tag: String? = null) {
show(this@BaseComposeFragment.childFragmentManager, tag)
try {
show(this@BaseComposeFragment.childFragmentManager, tag)
} catch (e: Exception) {
Timber.e(e, "Error while showing child fragment")
}
}
abstract fun injectDependencies()
abstract fun releaseDependencies()
}
fun <T> BaseComposeFragment.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope)
jobs += flow
.cancellable()
.onEach {
try {
body(it)
} catch (e: Exception) {
Timber.e(e, "Unhandled exception in proceed flow")
}
}
.launchIn(lifecycleScope)
}

View file

@ -90,7 +90,7 @@ abstract class BaseFragment<T : ViewBinding>(
}
open fun onApplyWindowRootInsets() {
if (BuildConfig.USE_NEW_WINDOW_INSET_API && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val deferringInsetsListener = RootViewDeferringInsetsCallback(
persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
deferredInsetTypes = WindowInsetsCompat.Type.ime()
@ -102,17 +102,21 @@ abstract class BaseFragment<T : ViewBinding>(
protected fun DialogFragment.showChildFragment(tag: String? = null) {
jobs += this@BaseFragment.lifecycleScope.launch {
throttleFlow.emit {
runCatching {
show(this@BaseFragment.childFragmentManager, tag)
}.fold(
onSuccess = {
// Do nothing.
},
onFailure = {
Timber.e(it, "Error while navigation")
}
)
try {
throttleFlow.emit {
runCatching {
show(this@BaseFragment.childFragmentManager, tag)
}.fold(
onSuccess = {
// Do nothing.
},
onFailure = {
Timber.e(it, "Error while navigation")
}
)
}
} catch (e: Exception) {
Timber.e(e, "Error while navigation")
}
}
}
@ -126,7 +130,16 @@ abstract class BaseFragment<T : ViewBinding>(
}
fun <T> BaseFragment<*>.proceed(flow: Flow<T>, body: suspend (T) -> Unit) {
jobs += flow.cancellable().onEach { body(it) }.launchIn(lifecycleScope)
jobs += flow
.cancellable()
.onEach {
try {
body(it)
} catch (e: Exception) {
Timber.e(e, "Unhandled exception in proceed flow")
}
}
.launchIn(lifecycleScope)
}
const val THROTTLE_DURATION = 300L

View file

@ -3334,11 +3334,16 @@ class EditorViewModel(
private fun onAddNewObjectClicked(
objectTypeView: ObjectTypeView
) {
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
val position: Position
val focused = blocks.first { it.id == orchestrator.stores.focus.current().targetOrNull() }
val focused = blocks.find { it.id == orchestrator.stores.focus.current().targetOrNull() }
if (focused == null) {
Timber.e("Error while trying to add new object: focused block is null, target is unknown.")
return
}
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
var target = focused.id
@ -3741,7 +3746,9 @@ class EditorViewModel(
if (target == context) {
position = Position.TOP
moveTarget = targetBlock.children.first()
moveTarget = targetBlock.children.firstOrNull().orEmpty().also {
Timber.e("Could not find move target in target block's children")
}
}
blocks.filter { selected.contains(it.id) }.forEach { block ->
@ -5076,7 +5083,11 @@ class EditorViewModel(
)
}
is SlashItem.Main.Color -> {
val block = blocks.first { it.id == targetId }
val block = blocks.find { it.id == targetId }
if (block == null) {
Timber.d("Could not find target block for slash item action: color")
return
}
val blockColor = block.content.asText().color
val color = if (blockColor != null) {
ThemeColor.valueOf(blockColor.toUpperCase())
@ -5092,7 +5103,11 @@ class EditorViewModel(
)
}
is SlashItem.Main.Background -> {
val block = blocks.first { it.id == targetId }
val block = blocks.find { it.id == targetId }
if (block == null) {
Timber.d("Could not find target block for slash item action: background")
return
}
val blockBackground = block.backgroundColor
val background = if (blockBackground == null) {
ThemeColor.DEFAULT
@ -5977,12 +5992,16 @@ class EditorViewModel(
private fun onMultiSelectAddBelow() {
mode = EditorMode.Edit
controlPanelInteractor.onEvent(ControlPanelMachine.Event.MultiSelect.OnExit)
val target = currentSelection().first()
clearSelections()
proceedWithCreatingNewTextBlock(
target = target,
style = Content.Text.Style.P
)
val target = currentSelection().firstOrNull()
if (target != null) {
clearSelections()
proceedWithCreatingNewTextBlock(
target = target,
style = Content.Text.Style.P
)
} else {
Timber.e("Could not define target in onMultiSelectAddBelow()")
}
}
fun onMultiSelectModeDeleteClicked() {