diff --git a/common/build.gradle.kts b/common/build.gradle.kts
index bfcbfaf2..1430970b 100644
--- a/common/build.gradle.kts
+++ b/common/build.gradle.kts
@@ -44,23 +44,37 @@ android {
}
dependencies {
- api(kotlin("stdlib"))
+ api(androidx.activity.compose)
+ api(androidx.compose.animation)
+ api(androidx.compose.foundation)
+ api(androidx.compose.material)
+ api(androidx.compose.runtime.livedata)
+ api(androidx.compose.ui)
+ api(androidx.compose.ui.tooling)
+ api(androidx.compose.ui.tooling.preview)
+ api(androidx.compose.ui.viewbinding)
+ api(androidx.constraintlayout.compose)
+ api(androidx.core)
+ api(androidx.fragment)
+ api(androidx.lifecycle.viewmodel.compose)
+ api(androidx.room)
+ api(androidx.room.runtime)
+ api(rmr.flipper)
+ api(rmr.itemsadapter.viewbinding)
+ api(stack.accompanist.themeadapter.core)
+ api(stack.accompanist.themeadapter.material)
+ api(stack.kotlinx.coroutines.android)
api(stack.okhttp)
+ api(stack.timber)
+ kapt(androidx.room.compiler)
+ // legacy
api(androidx.appcompat)
- api(stack.material)
api(androidx.constraintlayout)
- api(stack.kotlinx.coroutines.android)
- api(androidx.lifecycle.runtime)
api(androidx.lifecycle.livedata)
api(androidx.lifecycle.livedata.core)
+ api(androidx.lifecycle.runtime)
api(androidx.lifecycle.viewmodel)
- api(androidx.room.runtime)
- api(androidx.room)
- api(androidx.core)
- api(rmr.itemsadapter.viewbinding)
- api(rmr.flipper)
- api(stack.timber)
- kapt(androidx.room.compiler)
+ api(stack.material)
}
tasks.register("prepareKotlinBuildScriptModel") {}
diff --git a/common/src/main/kotlin/com/redmadrobot/debug/common/base/PluginFragment.kt b/common/src/main/kotlin/com/redmadrobot/debug/common/base/PluginFragment.kt
deleted file mode 100644
index a9a046cc..00000000
--- a/common/src/main/kotlin/com/redmadrobot/debug/common/base/PluginFragment.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.redmadrobot.debug.common.base
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.view.ContextThemeWrapper
-import androidx.fragment.app.Fragment
-import com.redmadrobot.debug.panel.common.R
-
-public open class PluginFragment(private val layoutId: Int) : Fragment() {
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- val contextThemeWrapper = ContextThemeWrapper(activity, R.style.DebugPanelTheme)
- val localInflater = inflater.cloneInContext(contextThemeWrapper)
- return localInflater.inflate(layoutId, container, false)
- }
-}
diff --git a/common/src/main/kotlin/com/redmadrobot/debug/common/base/PluginViewModel.kt b/common/src/main/kotlin/com/redmadrobot/debug/common/base/PluginViewModel.kt
deleted file mode 100644
index b0a8e2a0..00000000
--- a/common/src/main/kotlin/com/redmadrobot/debug/common/base/PluginViewModel.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.redmadrobot.debug.common.base
-
-import androidx.lifecycle.ViewModel
-
-public open class PluginViewModel : ViewModel()
diff --git a/common/src/main/res/layout/item_section_header.xml b/common/src/main/res/layout/item_section_header.xml
deleted file mode 100644
index 8e32a980..00000000
--- a/common/src/main/res/layout/item_section_header.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/common/src/main/res/values/dimens.xml b/common/src/main/res/values/dimens.xml
deleted file mode 100644
index 166e2e12..00000000
--- a/common/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- 56dp
-
diff --git a/common/src/main/res/values/styles.xml b/common/src/main/res/values/styles.xml
deleted file mode 100644
index 7b624ca6..00000000
--- a/common/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 89a50fc6..8d8d51e7 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -39,6 +39,11 @@ android {
buildFeatures {
viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
namespace = "com.redmadrobot.debug.core"
}
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml
index d5c6ef5d..eb87ba58 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core/src/main/AndroidManifest.xml
@@ -9,6 +9,13 @@
android:taskAffinity="com.redmadrobot.debug_panel.DebugActivity"
android:theme="@style/DebugPanelTheme" />
+
+
) {
instance = DebugPanelInstance(application, plugins)
}
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/extension/ComposeExt.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/ComposeExt.kt
new file mode 100644
index 00000000..4a812474
--- /dev/null
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/ComposeExt.kt
@@ -0,0 +1,46 @@
+package com.redmadrobot.debug.core.extension
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.redmadrobot.debug.core.annotation.DebugPanelInternal
+
+@DebugPanelInternal
+@Composable
+public inline fun provideViewModel(crossinline block: () -> T): T {
+ return viewModel(
+ factory = object : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ @Suppress("UNCHECKED_CAST")
+ return block() as T
+ }
+ }
+ )
+}
+
+
+@DebugPanelInternal
+@Composable
+public fun OnLifecycleEvent(onEvent: (event: Lifecycle.Event) -> Unit) {
+ val eventHandler by rememberUpdatedState(onEvent)
+ val lifecycleOwner by rememberUpdatedState(LocalLifecycleOwner.current)
+
+ DisposableEffect(lifecycleOwner) {
+ val lifecycle = lifecycleOwner.lifecycle
+ val observer = LifecycleEventObserver { _, event ->
+ eventHandler(event)
+ }
+
+ lifecycle.addObserver(observer)
+ onDispose {
+ lifecycle.removeObserver(observer)
+ }
+ }
+}
diff --git a/common/src/main/kotlin/com/redmadrobot/debug/common/extension/CoroutinesExtension.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/CoroutinesExtension.kt
similarity index 94%
rename from common/src/main/kotlin/com/redmadrobot/debug/common/extension/CoroutinesExtension.kt
rename to core/src/main/kotlin/com/redmadrobot/debug/core/extension/CoroutinesExtension.kt
index a4a4b86f..1f419d82 100644
--- a/common/src/main/kotlin/com/redmadrobot/debug/common/extension/CoroutinesExtension.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/CoroutinesExtension.kt
@@ -1,4 +1,4 @@
-package com.redmadrobot.debug.common.extension
+package com.redmadrobot.debug.core.extension
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
diff --git a/common/src/main/kotlin/com/redmadrobot/debug/common/extension/LifecicleExt.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/LifecicleExt.kt
similarity index 96%
rename from common/src/main/kotlin/com/redmadrobot/debug/common/extension/LifecicleExt.kt
rename to core/src/main/kotlin/com/redmadrobot/debug/core/extension/LifecicleExt.kt
index 843b5390..bb674f91 100644
--- a/common/src/main/kotlin/com/redmadrobot/debug/common/extension/LifecicleExt.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/LifecicleExt.kt
@@ -1,4 +1,4 @@
-package com.redmadrobot.debug.common.extension
+package com.redmadrobot.debug.core.extension
import androidx.annotation.MainThread
import androidx.fragment.app.Fragment
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/extension/PluginsExt.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/PluginsExt.kt
index f6573243..10e7a398 100644
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/extension/PluginsExt.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/extension/PluginsExt.kt
@@ -1,8 +1,10 @@
package com.redmadrobot.debug.core.extension
+import androidx.fragment.app.Fragment
import com.redmadrobot.debug.core.DebugPanelInstance
import com.redmadrobot.debug.core.annotation.DebugPanelInternal
import com.redmadrobot.debug.core.plugin.Plugin
+import com.redmadrobot.debug.core.ui.debugpanel.DebugActivity
@PublishedApi
internal fun getPlugin(pluginName: String): Plugin {
@@ -20,3 +22,7 @@ public inline fun getPlugin(): T {
return plugin as T
}
+@DebugPanelInternal
+public fun Fragment.isSettingMode(): Boolean {
+ return activity?.javaClass == DebugActivity::class.java
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/DebugBottomSheet.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/DebugBottomSheet.kt
deleted file mode 100644
index 8978ce42..00000000
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/DebugBottomSheet.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-package com.redmadrobot.debug.core.inapp
-
-import android.app.Dialog
-import android.os.Bundle
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.appcompat.view.ContextThemeWrapper
-import androidx.fragment.app.FragmentManager
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.google.android.material.tabs.TabLayoutMediator
-import com.redmadrobot.debug.core.extension.getAllPlugins
-import com.redmadrobot.debug.core.databinding.BottomSheetDebugPanelBinding
-import com.redmadrobot.debug.panel.common.R as CommonR
-
-internal class DebugBottomSheet : BottomSheetDialogFragment() {
-
- companion object {
- private const val TAG = "DebugBottomSheet"
-
- fun show(fragmentManager: FragmentManager) {
- if (fragmentManager.findFragmentByTag(TAG) == null) {
- DebugBottomSheet().show(fragmentManager, TAG)
- }
- }
- }
-
- private var _binding: BottomSheetDebugPanelBinding? = null
- private val binding get() = checkNotNull(_binding)
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- /*set DebugPanelTheme*/
- val dialog = super.onCreateDialog(savedInstanceState)
- val contextThemeWrapper = ContextThemeWrapper(activity, CommonR.style.DebugPanelTheme)
- val localInflater = dialog.layoutInflater.cloneInContext(contextThemeWrapper)
-
- _binding = BottomSheetDebugPanelBinding.inflate(localInflater)
- dialog.setOnShowListener {
- setBottomSheetSize()
- }
- dialog.setContentView(binding.root)
- setViews(binding)
- return dialog
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- private fun setViews(binding: BottomSheetDebugPanelBinding) {
- val plugins = getAllPlugins()
- /*Only Plugins with Fragment*/
- .filter { it.getFragment() != null }
-
- binding.debugSheetViewpager.adapter = DebugSheetViewPagerAdapter(
- requireActivity(),
- plugins
- )
-
- val tabConfigurationStrategy = TabLayoutMediator.TabConfigurationStrategy { tab, position ->
- tab.text = plugins[position].getName()
- }
-
- TabLayoutMediator(
- binding.debugSheetTabLayout,
- binding.debugSheetViewpager,
- tabConfigurationStrategy
- ).attach()
- }
-
- private fun setBottomSheetSize() {
- val dialogContainer = binding.root.parent as? FrameLayout
- dialogContainer?.apply {
- layoutParams?.height = ViewGroup.LayoutParams.MATCH_PARENT
- requestLayout()
- }
-
- with(getBehavior()) {
- peekHeight = resources.displayMetrics.heightPixels / 2
- state = BottomSheetBehavior.STATE_HALF_EXPANDED
- }
- }
-
- private fun getBehavior(): BottomSheetBehavior {
- return (dialog as BottomSheetDialog).behavior
- }
-}
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/DebugSheetViewPagerAdapter.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/DebugSheetViewPagerAdapter.kt
deleted file mode 100644
index 59736bf7..00000000
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/DebugSheetViewPagerAdapter.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.redmadrobot.debug.core.inapp
-
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
-import androidx.viewpager2.adapter.FragmentStateAdapter
-import com.redmadrobot.debug.core.plugin.Plugin
-
-internal class DebugSheetViewPagerAdapter(
- fragmentActivity: FragmentActivity,
- private val plugins: List
-) : FragmentStateAdapter(fragmentActivity) {
-
-
- override fun getItemCount() = plugins.size
-
- override fun createFragment(position: Int): Fragment {
- return requireNotNull(plugins[position].getFragment())
- }
-}
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/ModalLayout.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/ModalLayout.kt
deleted file mode 100644
index e5cdbdb6..00000000
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/ModalLayout.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.redmadrobot.debug.core.inapp
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Paint
-import android.graphics.RectF
-import android.util.AttributeSet
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.content.ContextCompat
-import com.redmadrobot.debug.core.R
-import com.redmadrobot.debug.panel.common.R as CommonR
-
-internal class ModalLayout @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : CoordinatorLayout(context, attrs, defStyleAttr) {
-
- /**
- * Paint для отрисовки "ручки" модального окна
- */
- private val handlePaint = Paint().apply {
- color = ContextCompat.getColor(context, CommonR.color.gray)
- style = Paint.Style.FILL
- }
-
- /**
- * Координаты для отрисовки "ручки" модального окна
- */
- private val handleRect by lazy {
- val height = resources.getDimensionPixelSize(R.dimen.debug_panel_handle_height).toFloat()
- val width = resources.getDimensionPixelSize(R.dimen.debug_panel_handle_width).toFloat()
- val displayWidthCenter = resources.displayMetrics.widthPixels / 2
- val topMargin = resources.getDimensionPixelSize(R.dimen.debug_panel_handle_top_margin).toFloat()
-
- RectF(
- (displayWidthCenter - width / 2),
- topMargin,
- (displayWidthCenter + width / 2),
- height + topMargin
- )
- }
-
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- if (parent is CoordinatorLayout) {
- background = ContextCompat.getDrawable(context, R.drawable.shape_scrollable_sheet)
- }
- }
-
- override fun dispatchDraw(canvas: Canvas) {
- val radius = resources.getDimensionPixelSize(R.dimen.debug_panel_handle_height) / 2
- canvas.drawRoundRect(
- handleRect,
- radius.toFloat(),
- radius.toFloat(),
- handlePaint
- )
- super.dispatchDraw(canvas)
- }
-}
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/compose/DebugBottomSheet.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/compose/DebugBottomSheet.kt
new file mode 100644
index 00000000..85ad128c
--- /dev/null
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/compose/DebugBottomSheet.kt
@@ -0,0 +1,131 @@
+@file:OptIn(ExperimentalFoundationApi::class)
+
+package com.redmadrobot.debug.core.inapp.compose
+
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.ModalBottomSheetLayout
+import androidx.compose.material.ModalBottomSheetValue
+import androidx.compose.material.ScrollableTabRow
+import androidx.compose.material.Tab
+import androidx.compose.material.TabRowDefaults
+import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material.Text
+import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import com.google.accompanist.themeadapter.material.MdcTheme
+import com.redmadrobot.debug.core.R
+import com.redmadrobot.debug.core.extension.getAllPlugins
+import com.redmadrobot.debug.core.plugin.Plugin
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+public fun DebugBottomSheet(onClose: () -> Unit) {
+ val state = rememberModalBottomSheetState(
+ initialValue = ModalBottomSheetValue.HalfExpanded,
+ confirmValueChange = { sheetState ->
+ if (sheetState == ModalBottomSheetValue.Hidden) onClose()
+ true
+ },
+ skipHalfExpanded = false
+ )
+ val context = LocalContext.current
+ val themeWrapper by remember {
+ mutableStateOf(
+ value = ContextThemeWrapper(context, R.style.DebugPanelTheme)
+ )
+ }
+
+ MdcTheme(context = themeWrapper) {
+ ModalBottomSheetLayout(
+ sheetContent = { BottomSheetContent() },
+ sheetState = state,
+ scrimColor = Color.Transparent,
+ content = {}
+ )
+ }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun BottomSheetContent() {
+ val plugins = remember { getAllPlugins() }
+ val pluginsName = remember { plugins.map { it.getName() } }
+ val pagerState = rememberPagerState(initialPage = 0, pageCount = { plugins.size })
+ Column(modifier = Modifier.fillMaxSize()) {
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Box(
+ modifier = Modifier
+ .height(4.dp)
+ .width(50.dp)
+ .clip(CircleShape)
+ .background(Color.Gray)
+ .align(Alignment.CenterHorizontally)
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ PluginsTabLayout(pluginsName, pagerState = pagerState)
+
+ PluginsPager(plugins, pagerState = pagerState)
+ }
+}
+
+@Composable
+private fun PluginsTabLayout(pluginsName: List, pagerState: PagerState) {
+ val scope = rememberCoroutineScope()
+ ScrollableTabRow(
+ selectedTabIndex = pagerState.currentPage,
+ backgroundColor = MaterialTheme.colors.background,
+ indicator = { tabPositions ->
+ TabRowDefaults.Indicator(
+ modifier = Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
+ height = 2.dp,
+ color = MaterialTheme.colors.secondary
+ )
+ }
+ ) {
+ pluginsName.forEachIndexed { index, _ ->
+ Tab(
+ text = { Text(pluginsName[index]) },
+ selected = pagerState.currentPage == index,
+ onClick = {
+ scope.launch { pagerState.animateScrollToPage(index) }
+ }
+ )
+ }
+ }
+}
+
+@Composable
+private fun PluginsPager(plugins: List, pagerState: PagerState) {
+ plugins[pagerState.currentPage].content()
+ HorizontalPager(state = pagerState) {
+ // no impl. Temporary solution
+ }
+}
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/shake/ShakeController.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/shake/ShakeController.kt
index 95fe40fd..051dba4f 100644
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/shake/ShakeController.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/inapp/shake/ShakeController.kt
@@ -1,10 +1,10 @@
package com.redmadrobot.debug.core.inapp.shake
+import android.app.Activity
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorManager
-import androidx.fragment.app.FragmentManager
-import com.redmadrobot.debug.core.inapp.DebugBottomSheet
+import com.redmadrobot.debug.core.DebugPanel
internal class ShakeController(context: Context) {
private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
@@ -13,9 +13,9 @@ internal class ShakeController(context: Context) {
private var openDebugPanelAction: (() -> Unit)? = null
- fun register(fragmentManager: FragmentManager) {
+ fun register(activity: Activity) {
unregister()
- openDebugPanelAction = { DebugBottomSheet.show(fragmentManager) }
+ openDebugPanelAction = { DebugPanel.showPanel(activity) }
sensorManager.registerListener(shakeDetector, accelerometer, SensorManager.SENSOR_DELAY_UI)
}
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/internal/PluginViewModel.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/internal/PluginViewModel.kt
new file mode 100644
index 00000000..880556e4
--- /dev/null
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/internal/PluginViewModel.kt
@@ -0,0 +1,7 @@
+package com.redmadrobot.debug.core.internal
+
+import androidx.lifecycle.ViewModel
+import com.redmadrobot.debug.core.annotation.DebugPanelInternal
+
+@DebugPanelInternal
+public open class PluginViewModel : ViewModel()
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/plugin/Plugin.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/plugin/Plugin.kt
index 7949ea2f..9e1c4eba 100644
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/plugin/Plugin.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/plugin/Plugin.kt
@@ -1,5 +1,6 @@
package com.redmadrobot.debug.core.plugin
+import androidx.compose.runtime.Composable
import androidx.fragment.app.Fragment
import com.redmadrobot.debug.core.DebugEvent
import com.redmadrobot.debug.core.DebugPanelInstance
@@ -21,10 +22,21 @@ public abstract class Plugin {
public fun getContainer(): T = pluginContainer as T
- public open fun getFragment(): Fragment? = null
-
+ @Deprecated(
+ message = "You shouldn't use fragments for you plugins. Please use Jetpack Compose",
+ replaceWith = ReplaceWith("content()", "com.redmadrobot.debug.core.plugin.Plugin"),
+ level = DeprecationLevel.WARNING,
+ )
public open fun getSettingFragment(): Fragment? = null
+ @Composable
+ public open fun content() {
+ }
+
+ @Composable
+ public open fun settingsContent() {
+ }
+
public abstract fun getPluginContainer(commonContainer: CommonContainer): PluginDependencyContainer
public abstract fun getName(): String
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/ui/debugpanel/DebugActivity.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/ui/debugpanel/DebugActivity.kt
index d27e61af..ab4ef4a4 100644
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/ui/debugpanel/DebugActivity.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/ui/debugpanel/DebugActivity.kt
@@ -4,12 +4,12 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
-import com.redmadrobot.debug.core.extension.getAllPlugins
-import com.redmadrobot.itemsadapter.bind
-import com.redmadrobot.itemsadapter.itemsAdapter
import com.redmadrobot.debug.core.R
import com.redmadrobot.debug.core.databinding.ActivityDebugBinding
import com.redmadrobot.debug.core.databinding.ItemPluginSettingBinding
+import com.redmadrobot.debug.core.extension.getAllPlugins
+import com.redmadrobot.itemsadapter.bind
+import com.redmadrobot.itemsadapter.itemsAdapter
internal class DebugActivity : AppCompatActivity() {
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/ui/debugpanel/DebugBottomSheetActivity.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/ui/debugpanel/DebugBottomSheetActivity.kt
new file mode 100644
index 00000000..5bd42712
--- /dev/null
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/ui/debugpanel/DebugBottomSheetActivity.kt
@@ -0,0 +1,30 @@
+package com.redmadrobot.debug.core.ui.debugpanel
+
+import android.os.Bundle
+import android.view.WindowManager
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material.MaterialTheme
+import androidx.core.view.WindowCompat
+import com.redmadrobot.debug.core.inapp.compose.DebugBottomSheet
+
+internal class DebugBottomSheetActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ configureWindow()
+
+ setContent {
+ MaterialTheme {
+ DebugBottomSheet(onClose = { this.finish() })
+ }
+ }
+ }
+
+ private fun configureWindow() = with(window) {
+ WindowCompat.setDecorFitsSystemWindows(this, false)
+ this.setFlags(
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ )
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/util/ApplicationLifecycleHandler.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/util/ApplicationLifecycleHandler.kt
index e9444742..a383ffc7 100644
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/util/ApplicationLifecycleHandler.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/util/ApplicationLifecycleHandler.kt
@@ -35,24 +35,18 @@ internal class ApplicationLifecycleHandler(
if (openActivityCount == 0) onAppResumed()
++openActivityCount
- (activity as? FragmentActivity)?.let { fragmentActivity ->
- shakeController?.register(fragmentActivity.supportFragmentManager)
+ shakeController?.register(activity)
- /*register BroadcastReceiver for debug panel inner actions*/
- debugPanelBroadcastReceiver = DebugPanelBroadcastReceiver(
- fragmentActivity.supportFragmentManager
- )
- val filter = IntentFilter(
- DebugPanelBroadcastReceiver.ACTION_OPEN_DEBUG_PANEL
- )
+ /*register BroadcastReceiver for debug panel inner actions*/
+ debugPanelBroadcastReceiver = DebugPanelBroadcastReceiver(activity)
+ val filter = IntentFilter(DebugPanelBroadcastReceiver.ACTION_OPEN_DEBUG_PANEL)
- ContextCompat.registerReceiver(
- activity,
- debugPanelBroadcastReceiver,
- filter,
- ContextCompat.RECEIVER_NOT_EXPORTED
- )
- }
+ ContextCompat.registerReceiver(
+ activity,
+ debugPanelBroadcastReceiver,
+ filter,
+ ContextCompat.RECEIVER_NOT_EXPORTED
+ )
}
override fun onActivityPaused(activity: Activity) {
diff --git a/core/src/main/kotlin/com/redmadrobot/debug/core/util/DebugPanelBroadcastReceiver.kt b/core/src/main/kotlin/com/redmadrobot/debug/core/util/DebugPanelBroadcastReceiver.kt
index 5bb5c22b..51b2a631 100644
--- a/core/src/main/kotlin/com/redmadrobot/debug/core/util/DebugPanelBroadcastReceiver.kt
+++ b/core/src/main/kotlin/com/redmadrobot/debug/core/util/DebugPanelBroadcastReceiver.kt
@@ -1,23 +1,22 @@
package com.redmadrobot.debug.core.util
+import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import androidx.fragment.app.FragmentManager
import com.redmadrobot.debug.core.DebugPanel
internal class DebugPanelBroadcastReceiver(
- private val supportFragmentManager: FragmentManager
+ private val activity: Activity
) : BroadcastReceiver() {
companion object {
- const val ACTION_OPEN_DEBUG_PANEL =
- "com.redmadrobot.debug_panel_core.ACTION_OPEN_DEBUG_PANEL"
+ const val ACTION_OPEN_DEBUG_PANEL = "com.redmadrobot.debug.core.ACTION_OPEN_DEBUG_PANEL"
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ACTION_OPEN_DEBUG_PANEL) {
- DebugPanel.showPanel(supportFragmentManager)
+ DebugPanel.showPanel(activity)
}
}
}
diff --git a/core/src/main/res/layout/bottom_sheet_debug_panel.xml b/core/src/main/res/layout/bottom_sheet_debug_panel.xml
deleted file mode 100644
index ee1d02d0..00000000
--- a/core/src/main/res/layout/bottom_sheet_debug_panel.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/fragment_stub.xml b/core/src/main/res/layout/fragment_stub.xml
deleted file mode 100644
index 8724baa7..00000000
--- a/core/src/main/res/layout/fragment_stub.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
diff --git a/common/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml
similarity index 91%
rename from common/src/main/res/values/colors.xml
rename to core/src/main/res/values/colors.xml
index a7e6499e..10c3b0e8 100644
--- a/common/src/main/res/values/colors.xml
+++ b/core/src/main/res/values/colors.xml
@@ -11,5 +11,5 @@
#000000
#EF0000
#009688
- #E1E5EA
+ #E1E5EA
diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml
index 400f019e..c63ae9ac 100644
--- a/core/src/main/res/values/dimens.xml
+++ b/core/src/main/res/values/dimens.xml
@@ -3,4 +3,5 @@
8dp
2dp
48dp
+ 56dp
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
new file mode 100644
index 00000000..68809c4c
--- /dev/null
+++ b/core/src/main/res/values/styles.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/core/DebugPanel.kt b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/core/DebugPanel.kt
index bd684124..500ceb95 100644
--- a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/core/DebugPanel.kt
+++ b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/core/DebugPanel.kt
@@ -1,5 +1,6 @@
package com.redmadrobot.debug.core
+import android.app.Activity
import android.app.Application
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
@@ -16,7 +17,9 @@ object DebugPanel {
fun subscribeToEvents(lifecycleOwner: LifecycleOwner, onEvent: (DebugEvent) -> Unit) = Unit
- fun observeEvents(): Flow? = emptyFlow()
+ fun observeEvents(): Flow = emptyFlow()
fun showPanel(fragmentManager: FragmentManager) = Unit
+
+ fun showPanel(activity: Activity) = Unit
}
diff --git a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugServer.kt b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugServer.kt
index c4e77d68..91d54293 100644
--- a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugServer.kt
+++ b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugServer.kt
@@ -3,5 +3,6 @@ package com.redmadrobot.debug.plugin.servers.data.model
data class DebugServer(
val id: Int = 0,
val name: String,
- val url: String
+ val url: String,
+ val isDefault: Boolean = false
)
diff --git a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugStage.kt b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugStage.kt
new file mode 100644
index 00000000..118038e7
--- /dev/null
+++ b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugStage.kt
@@ -0,0 +1,8 @@
+package com.redmadrobot.debug.plugin.servers.data.model
+
+data class DebugStage(
+ val id: Int = 0,
+ val name: String,
+ val hosts: Map,
+ val isDefault: Boolean = false
+)
diff --git a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugServerInterceptor.kt b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/interceptor/DebugServerInterceptor.kt
similarity index 88%
rename from no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugServerInterceptor.kt
rename to no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/interceptor/DebugServerInterceptor.kt
index c8670bed..761ac6d2 100644
--- a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/DebugServerInterceptor.kt
+++ b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/interceptor/DebugServerInterceptor.kt
@@ -1,4 +1,4 @@
-package com.redmadrobot.debug.plugin.servers.util
+package com.redmadrobot.debug.plugin.servers.interceptor
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
import okhttp3.Interceptor
diff --git a/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/interceptor/DebugStageInterceptor.kt b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/interceptor/DebugStageInterceptor.kt
new file mode 100644
index 00000000..2c225b07
--- /dev/null
+++ b/no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/servers/interceptor/DebugStageInterceptor.kt
@@ -0,0 +1,17 @@
+package com.redmadrobot.debug.plugin.servers.interceptor
+
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
+import okhttp3.Interceptor
+import okhttp3.Request
+import okhttp3.Response
+
+public class DebugStageInterceptor(private val tag: String) : Interceptor {
+
+ public fun modifyRequest(block: (Request, DebugStage) -> Request): DebugStageInterceptor {
+ return this
+ }
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ return chain.proceed(chain.request())
+ }
+}
diff --git a/plugins/accounts/build.gradle.kts b/plugins/accounts/build.gradle.kts
index 79d1457d..a63aafc5 100644
--- a/plugins/accounts/build.gradle.kts
+++ b/plugins/accounts/build.gradle.kts
@@ -43,7 +43,11 @@ android {
}
buildFeatures {
- viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
namespace = "com.redmadrobot.debug.plugin.accounts"
}
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPlugin.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPlugin.kt
index 35717d8f..d3d4e9ff 100644
--- a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPlugin.kt
+++ b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPlugin.kt
@@ -1,15 +1,14 @@
package com.redmadrobot.debug.plugin.accounts
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
+import androidx.compose.runtime.Composable
+import com.redmadrobot.debug.core.data.DebugDataProvider
+import com.redmadrobot.debug.core.internal.CommonContainer
+import com.redmadrobot.debug.core.internal.PluginDependencyContainer
+import com.redmadrobot.debug.core.plugin.Plugin
import com.redmadrobot.debug.plugin.accounts.authenticator.DebugAuthenticator
import com.redmadrobot.debug.plugin.accounts.authenticator.DefaultAuthenticator
import com.redmadrobot.debug.plugin.accounts.data.model.DebugAccount
-import com.redmadrobot.debug.plugin.accounts.ui.AccountsFragment
-import com.redmadrobot.debug.core.internal.CommonContainer
-import com.redmadrobot.debug.core.data.DebugDataProvider
-import com.redmadrobot.debug.core.plugin.Plugin
-import com.redmadrobot.debug.core.internal.PluginDependencyContainer
+import com.redmadrobot.debug.plugin.accounts.ui.AccountsScreen
public class AccountsPlugin(
private val preInstalledAccounts: List = emptyList(),
@@ -34,15 +33,13 @@ public class AccountsPlugin(
return AccountsPluginContainer(preInstalledAccounts, commonContainer)
}
- override fun getFragment(): Fragment {
- return AccountsFragment().apply {
- arguments = bundleOf(AccountsFragment.IS_EDIT_MODE_KEY to false)
- }
+ @Composable
+ override fun content() {
+ AccountsScreen(isEditMode = false)
}
- override fun getSettingFragment(): Fragment {
- return AccountsFragment().apply {
- arguments = bundleOf(AccountsFragment.IS_EDIT_MODE_KEY to true)
- }
+ @Composable
+ override fun settingsContent() {
+ AccountsScreen(isEditMode = true)
}
}
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPluginContainer.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPluginContainer.kt
index 2c66eb01..56c7bf24 100644
--- a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPluginContainer.kt
+++ b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/AccountsPluginContainer.kt
@@ -1,11 +1,11 @@
package com.redmadrobot.debug.plugin.accounts
+import com.redmadrobot.debug.core.internal.CommonContainer
+import com.redmadrobot.debug.core.internal.PluginDependencyContainer
import com.redmadrobot.debug.plugin.accounts.data.LocalDebugAccountRepository
import com.redmadrobot.debug.plugin.accounts.data.model.DebugAccount
import com.redmadrobot.debug.plugin.accounts.data.storage.AccountsPluginDatabase
import com.redmadrobot.debug.plugin.accounts.ui.AccountsViewModel
-import com.redmadrobot.debug.core.internal.CommonContainer
-import com.redmadrobot.debug.core.internal.PluginDependencyContainer
internal class AccountsPluginContainer(
private val preInstalledAccounts: List,
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsFragment.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsFragment.kt
deleted file mode 100644
index d4efbbd2..00000000
--- a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsFragment.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.redmadrobot.debug.plugin.accounts.ui
-
-import android.os.Bundle
-import android.view.View
-import android.widget.Toast
-import androidx.core.view.isVisible
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.redmadrobot.debug.plugin.accounts.ui.add.AddAccountDialog
-import com.redmadrobot.debug.plugin.accounts.ui.item.DebugAccountItems
-import com.redmadrobot.debug.plugin.accounts.R
-import com.redmadrobot.debug.plugin.accounts.data.model.DebugAccount
-import com.redmadrobot.debug.plugin.accounts.databinding.FragmentAccountsBinding
-import com.redmadrobot.debug.plugin.accounts.databinding.ItemAccountBinding
-import com.redmadrobot.debug.plugin.accounts.AccountSelectedEvent
-import com.redmadrobot.debug.plugin.accounts.AccountsPlugin
-import com.redmadrobot.debug.plugin.accounts.AccountsPluginContainer
-import com.redmadrobot.debug.common.base.PluginFragment
-import com.redmadrobot.debug.common.extension.observe
-import com.redmadrobot.debug.common.extension.obtainShareViewModel
-import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.debug.panel.common.databinding.ItemSectionHeaderBinding
-import com.redmadrobot.itemsadapter.ItemsAdapter
-import com.redmadrobot.itemsadapter.bind
-import com.redmadrobot.itemsadapter.itemsAdapter
-import com.redmadrobot.debug.panel.common.R as CommonR
-
-internal class AccountsFragment : PluginFragment(R.layout.fragment_accounts) {
-
- companion object {
- const val IS_EDIT_MODE_KEY = "IS_EDIT_MODE_KEY"
- }
-
- private var _binding: FragmentAccountsBinding? = null
- private val binding get() = checkNotNull(_binding)
-
- private val isEditMode by lazy {
- requireNotNull(arguments).getBoolean(IS_EDIT_MODE_KEY)
- }
-
- private val viewModel by lazy {
- obtainShareViewModel {
- getPlugin()
- .getContainer()
- .createAccountsViewModel()
- }
- }
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
- observe(viewModel.state, ::render)
- viewModel.loadAccounts()
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- _binding = FragmentAccountsBinding.bind(view)
- binding.setView()
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- private fun FragmentAccountsBinding.setView() {
- accountList.layoutManager = LinearLayoutManager(requireContext())
- addAccount.setOnClickListener {
- AddAccountDialog.show(
- requireActivity().supportFragmentManager
- )
- }
- addAccount.isVisible = isEditMode
- }
-
- private fun render(state: AccountsViewState) {
- val adapter = createAdapterByState(state)
- binding.accountList.adapter = adapter
- }
-
- private fun createAdapterByState(state: AccountsViewState): ItemsAdapter {
- return itemsAdapter(state.preInstalledAccounts.plus(state.addedAccounts)) { item ->
- when (item) {
- is DebugAccountItems.Header -> {
- bind(CommonR.layout.item_section_header) {
- itemSectionTitle.text = item.header
- }
- }
- is DebugAccountItems.PreinstalledAccount -> {
- bind(R.layout.item_account) {
- accountLogin.text = item.account.login
- if (!isEditMode) {
- root.setOnClickListener { setAccountAsCurrent(item.account) }
- }
- }
- }
- is DebugAccountItems.AddedAccount -> {
- bind(R.layout.item_account) {
- accountLogin.text = item.account.login
- accountDelete.isVisible = isEditMode
- accountDelete.setOnClickListener { viewModel.removeAccount(item.account) }
- root.setOnClickListener { onAddedAccountClicked(item.account) }
- }
- }
- }
- }
- }
-
- private fun onAddedAccountClicked(account: DebugAccount) {
- if (isEditMode) openAccountDialog(account) else setAccountAsCurrent(account)
- }
-
- private fun openAccountDialog(account: DebugAccount) {
- AddAccountDialog.show(
- fragmentManager = requireActivity().supportFragmentManager,
- account = account
- )
- }
-
- private fun setAccountAsCurrent(account: DebugAccount) {
- getPlugin().debugAuthenticator.onAccountSelected(account)
- pushEvent(account)
- }
-
- private fun pushEvent(account: DebugAccount) {
- Toast.makeText(
- requireActivity(),
- "Account ${account.login} selected",
- Toast.LENGTH_SHORT
- ).show()
- getPlugin().pushEvent(AccountSelectedEvent(account))
- }
-}
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsScreen.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsScreen.kt
new file mode 100644
index 00000000..c5cb44d8
--- /dev/null
+++ b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsScreen.kt
@@ -0,0 +1,270 @@
+package com.redmadrobot.debug.plugin.accounts.ui
+
+import android.annotation.SuppressLint
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Button
+import androidx.compose.material.Card
+import androidx.compose.material.ExtendedFloatingActionButton
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Scaffold
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
+import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.core.extension.provideViewModel
+import com.redmadrobot.debug.plugin.accounts.AccountsPlugin
+import com.redmadrobot.debug.plugin.accounts.AccountsPluginContainer
+import com.redmadrobot.debug.plugin.accounts.R
+import com.redmadrobot.debug.plugin.accounts.data.model.DebugAccount
+
+@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
+@Composable
+internal fun AccountsScreen(
+ viewModel: AccountsViewModel = provideViewModel {
+ getPlugin()
+ .getContainer()
+ .createAccountsViewModel()
+ },
+ isEditMode: Boolean
+) {
+ val state by viewModel.state.collectAsState()
+ var showDialog by remember { mutableStateOf(false) }
+ var editableAccount by remember { mutableStateOf(null) }
+
+ Scaffold(
+ floatingActionButton = {
+ if (isEditMode) {
+ ExtendedFloatingActionButton(
+ onClick = { showDialog = true },
+ text = { Text("Add") },
+ icon = {
+ Icon(
+ painterResource(R.drawable.icon_add_account),
+ contentDescription = null
+ )
+ }
+ )
+ }
+ }
+ ) {
+ AccountScreenLayout(
+ state = state,
+ isEditMode = isEditMode,
+ onSelectedClick = viewModel::setAccountAsCurrent,
+ onOpenDialogClick = {
+ showDialog = true
+ editableAccount = it
+ },
+ onDeleteClick = viewModel::removeAccount,
+ )
+ if (showDialog) {
+ AccountDetailsDialog(
+ onDismiss = {
+ showDialog = false
+ editableAccount = null
+ },
+ onSaveClick = viewModel::saveAccount,
+ account = editableAccount
+ )
+ }
+ }
+
+ LaunchedEffect(viewModel) {
+ viewModel.loadAccounts()
+ }
+}
+
+@Composable
+private fun AccountScreenLayout(
+ state: AccountsViewState,
+ isEditMode: Boolean,
+ onSelectedClick: (DebugAccount) -> Unit,
+ onDeleteClick: (DebugAccount) -> Unit,
+ onOpenDialogClick: (DebugAccount?) -> Unit,
+) {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 16.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ state.allItems.forEach { item ->
+ item {
+ when (item) {
+ is DebugAccountItem.Header -> {
+ Text(
+ item.header.uppercase(),
+ modifier = Modifier
+ .fillParentMaxWidth()
+ .padding(vertical = 16.dp),
+ fontSize = 16.sp
+ )
+ }
+
+ is DebugAccountItem.PreinstalledAccount -> {
+ AccountItem(
+ item.account,
+ false,
+ onItemClick = { if (!isEditMode) onSelectedClick(it) }
+ )
+ }
+
+ is DebugAccountItem.AddedAccount -> {
+ AccountItem(
+ account = item.account,
+ showDelete = isEditMode,
+ onItemClick = {
+ if (isEditMode) onOpenDialogClick(it) else onSelectedClick(it)
+ },
+ onDeleteClick = onDeleteClick,
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun AccountItem(
+ account: DebugAccount,
+ showDelete: Boolean,
+ onItemClick: (DebugAccount) -> Unit,
+ onDeleteClick: (DebugAccount) -> Unit = {},
+) {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(56.dp)
+ .clickable { onItemClick(account) }
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 16.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ horizontalArrangement = Arrangement.spacedBy(32.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.icon_account),
+ contentDescription = null,
+ tint = MaterialTheme.colors.primary
+ )
+ Text(account.login)
+ }
+ if (showDelete) {
+ IconButton(
+ modifier = Modifier.align(Alignment.CenterEnd),
+ onClick = { onDeleteClick(account) },
+ ) {
+ Icon(
+ painterResource(R.drawable.icon_delete),
+ contentDescription = null,
+ tint = Color.Red
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun AccountDetailsDialog(
+ onDismiss: () -> Unit,
+ onSaveClick: (DebugAccount) -> Unit,
+ account: DebugAccount? = null,
+) {
+ var login by remember { mutableStateOf(account?.login.orEmpty()) }
+ var password by remember { mutableStateOf(account?.password.orEmpty()) }
+ var pin by remember { mutableStateOf(account?.pin.orEmpty()) }
+
+ Dialog(onDismissRequest = onDismiss) {
+ Surface(
+ shape = RoundedCornerShape(16.dp),
+ color = Color.White
+ ) {
+ Box(modifier = Modifier.padding(16.dp)) {
+ Column {
+ OutlinedTextField(
+ value = login,
+ onValueChange = { login = it },
+ label = { Text(stringResource(R.string.login_hint)) }
+ )
+ Spacer(modifier = Modifier.height(24.dp))
+ OutlinedTextField(
+ value = password,
+ onValueChange = { password = it },
+ label = { Text(stringResource(R.string.password_hint)) }
+ )
+ Spacer(modifier = Modifier.height(24.dp))
+ OutlinedTextField(
+ value = pin,
+ onValueChange = { pin = it },
+ label = { Text(stringResource(R.string.pin)) }
+ )
+ Spacer(modifier = Modifier.height(24.dp))
+ Button(
+ onClick = {
+ val debugAccount = DebugAccount(
+ id = account?.id ?: 0,
+ login = login,
+ password = password,
+ pin = pin
+ )
+ onSaveClick(debugAccount)
+ onDismiss()
+ },
+ modifier = Modifier.align(Alignment.End)
+ ) {
+ Text(stringResource(R.string.save_account_text).uppercase())
+ }
+ }
+ }
+ }
+ }
+}
+
+internal sealed class DebugAccountItem {
+ internal data class Header(val header: String) : DebugAccountItem()
+ internal data class PreinstalledAccount(val account: DebugAccount) : DebugAccountItem()
+ internal data class AddedAccount(var account: DebugAccount) : DebugAccountItem()
+}
+
+
+@Preview
+@Composable
+private fun DialogPreview() {
+ AccountDetailsDialog({}, {})
+}
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewModel.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewModel.kt
index 8614d8ca..2a580578 100644
--- a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewModel.kt
+++ b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewModel.kt
@@ -1,27 +1,29 @@
package com.redmadrobot.debug.plugin.accounts.ui
import android.content.Context
-import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.redmadrobot.debug.plugin.accounts.ui.item.DebugAccountItems
+import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.core.extension.safeLaunch
+import com.redmadrobot.debug.core.internal.PluginViewModel
+import com.redmadrobot.debug.plugin.accounts.AccountSelectedEvent
+import com.redmadrobot.debug.plugin.accounts.AccountsPlugin
import com.redmadrobot.debug.plugin.accounts.R
import com.redmadrobot.debug.plugin.accounts.data.DebugAccountRepository
import com.redmadrobot.debug.plugin.accounts.data.model.DebugAccount
-import com.redmadrobot.debug.common.base.PluginViewModel
-import com.redmadrobot.debug.common.extension.safeLaunch
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.update
internal class AccountsViewModel(
private val context: Context,
private val debugAccountsRepository: DebugAccountRepository
) : PluginViewModel() {
- val state = MutableLiveData().apply {
- /*Default state*/
+ val state = MutableStateFlow(
value = AccountsViewState(
preInstalledAccounts = emptyList(),
addedAccounts = emptyList()
)
- }
+ )
fun loadAccounts() {
viewModelScope.safeLaunch {
@@ -30,14 +32,13 @@ internal class AccountsViewModel(
}
}
- fun saveAccount(login: String, password: String, pin: String) {
- val account = DebugAccount(
- login = login,
- password = password,
- pin = pin
- )
+ fun saveAccount(debugAccount: DebugAccount) {
viewModelScope.safeLaunch {
- debugAccountsRepository.addAccount(account)
+ if (debugAccount.id != 0) {
+ debugAccountsRepository.updateAccount(debugAccount)
+ } else {
+ debugAccountsRepository.addAccount(debugAccount)
+ }
loadAddedAccounts()
}
}
@@ -68,31 +69,40 @@ internal class AccountsViewModel(
}
}
+ fun setAccountAsCurrent(account: DebugAccount) {
+ getPlugin().apply {
+ debugAuthenticator.onAccountSelected(account)
+ pushEvent(AccountSelectedEvent(account))
+ }
+ }
+
private suspend fun loadPreInstalledAccounts() {
val accounts = debugAccountsRepository.getPreInstalledAccounts()
val preInstalledAccounts = if (accounts.isNotEmpty()) {
val items = accounts.map { account ->
- DebugAccountItems.PreinstalledAccount(account)
+ DebugAccountItem.PreinstalledAccount(account)
}
val header = context.getString(R.string.pre_installed_accounts)
- listOf(/*Header item*/DebugAccountItems.Header(header)).plus(items)
+ listOf(/*Header item*/DebugAccountItem.Header(header)).plus(items)
} else {
emptyList()
}
- state.value = state.value?.copy(preInstalledAccounts = preInstalledAccounts)
+
+ state.update { state.value.copy(preInstalledAccounts = preInstalledAccounts) }
}
private suspend fun loadAddedAccounts() {
val accounts = debugAccountsRepository.getAccounts()
val addedAccountItems = if (accounts.isNotEmpty()) {
val items = accounts.map { account ->
- DebugAccountItems.AddedAccount(account)
+ DebugAccountItem.AddedAccount(account)
}
val header = context.getString(R.string.added_accounts)
- listOf(/*Header item*/DebugAccountItems.Header(header)).plus(items)
+ listOf(/*Header item*/DebugAccountItem.Header(header)).plus(items)
} else {
emptyList()
}
- state.value = state.value?.copy(addedAccounts = addedAccountItems)
+
+ state.update { state.value.copy(addedAccounts = addedAccountItems) }
}
}
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewState.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewState.kt
index aca9c13a..58af1d20 100644
--- a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewState.kt
+++ b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/AccountsViewState.kt
@@ -1,8 +1,11 @@
package com.redmadrobot.debug.plugin.accounts.ui
-import com.redmadrobot.debug.plugin.accounts.ui.item.DebugAccountItems
+import androidx.compose.runtime.Stable
+@Stable
internal data class AccountsViewState(
- val preInstalledAccounts: List,
- val addedAccounts: List
-)
+ val preInstalledAccounts: List = emptyList(),
+ val addedAccounts: List = emptyList(),
+) {
+ val allItems = preInstalledAccounts.plus(addedAccounts)
+}
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/add/AddAccountDialog.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/add/AddAccountDialog.kt
deleted file mode 100644
index eb6ce92d..00000000
--- a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/add/AddAccountDialog.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.redmadrobot.debug.plugin.accounts.ui.add
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.LinearLayout
-import androidx.core.os.bundleOf
-import androidx.fragment.app.DialogFragment
-import androidx.fragment.app.FragmentManager
-import com.redmadrobot.debug.plugin.accounts.data.model.DebugAccount
-import com.redmadrobot.debug.plugin.accounts.databinding.DialogAddAccountBinding
-import com.redmadrobot.debug.plugin.accounts.AccountsPlugin
-import com.redmadrobot.debug.plugin.accounts.AccountsPluginContainer
-import com.redmadrobot.debug.common.extension.obtainShareViewModel
-import com.redmadrobot.debug.core.extension.getPlugin
-
-internal class AddAccountDialog : DialogFragment() {
-
- companion object {
- private const val KEY_ID = "ID"
- private const val KEY_LOGIN = "LOGIN"
- private const val KEY_PASSWORD = "PASSWORD"
- private const val KEY_PIN = "PIN"
-
- private const val TAG = "AddAccountDialog"
-
- fun show(
- fragmentManager: FragmentManager,
- account: DebugAccount? = null
- ) {
- AddAccountDialog().apply {
- if (account != null) {
- this.arguments = bundleOf(
- KEY_ID to account.id,
- KEY_LOGIN to account.login,
- KEY_PASSWORD to account.password,
- KEY_PIN to account.pin
- )
- }
- }.show(fragmentManager, TAG)
- }
- }
-
- private var _binding: DialogAddAccountBinding? = null
- private val binding get() = checkNotNull(_binding)
-
- private val id by lazy { arguments?.getInt(KEY_ID) }
- private val login by lazy { arguments?.getString(KEY_LOGIN) }
- private val password by lazy { arguments?.getString(KEY_PASSWORD) }
- private val pin by lazy { arguments?.getString(KEY_PIN) }
-
- private val isEditMode: Boolean
- get() = login != null && password != null
-
- private val sharedViewModel by lazy {
- obtainShareViewModel {
- getPlugin()
- .getContainer()
- .createAccountsViewModel()
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- _binding = DialogAddAccountBinding.inflate(inflater, container, false)
- return binding.root
- }
-
- override fun onStart() {
- super.onStart()
- dialog?.window?.setLayout(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- )
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- binding.setView()
- binding.setData()
- binding.accountLogin.requestFocus()
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- private fun DialogAddAccountBinding.setData() {
- if (!login.isNullOrEmpty() && password != null) {
- accountLogin.setText(login)
- accountPassword.setText(password)
- accountPin.setText(pin)
- }
- }
-
- private fun DialogAddAccountBinding.setView() {
- saveAccountButton.setOnClickListener {
- if (dataIsValid()) save()
- }
- }
-
- private fun save() {
- val login = binding.accountLogin.text.toString()
- val password = binding.accountPassword.text.toString()
- val pin = binding.accountPin.text.toString()
- if (isEditMode) {
- id?.let { id ->
- sharedViewModel.updateAccount(id, login, password, pin)
- }
- } else {
- sharedViewModel.saveAccount(login, password, pin)
- }
- dialog?.dismiss()
- }
-
-
- private fun dataIsValid(): Boolean {
- //TODO Добавить валидацию данных
- return true
- }
-}
diff --git a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/item/DebugAccountItems.kt b/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/item/DebugAccountItems.kt
deleted file mode 100644
index 47e10ce3..00000000
--- a/plugins/accounts/src/main/kotlin/com/redmadrobot/debug/plugin/accounts/ui/item/DebugAccountItems.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.redmadrobot.debug.plugin.accounts.ui.item
-
-import com.redmadrobot.debug.plugin.accounts.data.model.DebugAccount
-
-internal sealed class DebugAccountItems {
- internal data class Header(val header: String) : DebugAccountItems()
- internal data class PreinstalledAccount(val account: DebugAccount) : DebugAccountItems()
- internal data class AddedAccount(var account: DebugAccount) : DebugAccountItems()
-}
-
diff --git a/plugins/accounts/src/main/res/drawable/icon_account.xml b/plugins/accounts/src/main/res/drawable/icon_account.xml
index 7fe80c7b..4649894e 100644
--- a/plugins/accounts/src/main/res/drawable/icon_account.xml
+++ b/plugins/accounts/src/main/res/drawable/icon_account.xml
@@ -1,7 +1,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/accounts/src/main/res/layout/dialog_add_pin.xml b/plugins/accounts/src/main/res/layout/dialog_add_pin.xml
deleted file mode 100644
index f0a63dcb..00000000
--- a/plugins/accounts/src/main/res/layout/dialog_add_pin.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/accounts/src/main/res/layout/fragment_accounts.xml b/plugins/accounts/src/main/res/layout/fragment_accounts.xml
deleted file mode 100644
index 587a7085..00000000
--- a/plugins/accounts/src/main/res/layout/fragment_accounts.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/plugins/accounts/src/main/res/layout/item_account.xml b/plugins/accounts/src/main/res/layout/item_account.xml
deleted file mode 100644
index a0db2d43..00000000
--- a/plugins/accounts/src/main/res/layout/item_account.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/app-settings/build.gradle.kts b/plugins/app-settings/build.gradle.kts
index 209530a4..01bb9174 100644
--- a/plugins/app-settings/build.gradle.kts
+++ b/plugins/app-settings/build.gradle.kts
@@ -37,7 +37,11 @@ android {
}
buildFeatures {
- viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
namespace = "com.redmadrobot.debug.plugin.appsettings"
}
diff --git a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/AppSettingsPlugin.kt b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/AppSettingsPlugin.kt
index 184b7398..43a5ea98 100644
--- a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/AppSettingsPlugin.kt
+++ b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/AppSettingsPlugin.kt
@@ -1,12 +1,12 @@
package com.redmadrobot.debug.plugin.appsettings
import android.content.SharedPreferences
-import androidx.fragment.app.Fragment
-import com.redmadrobot.debug.plugin.appsettings.data.DefaultSharedPreferences
-import com.redmadrobot.debug.plugin.appsettings.ui.ApplicationSettingsFragment
-import com.redmadrobot.debug.core.internal.CommonContainer
+import androidx.compose.runtime.Composable
+import com.redmadrobot.debug.plugin.appsettings.ui.ApplicationSettingsScreen
import com.redmadrobot.debug.core.plugin.Plugin
+import com.redmadrobot.debug.core.internal.CommonContainer
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
+import com.redmadrobot.debug.plugin.appsettings.data.DefaultSharedPreferences
public class AppSettingsPlugin(
private val sharedPreferences: List = listOf(DefaultSharedPreferences())
@@ -22,7 +22,8 @@ public class AppSettingsPlugin(
return AppSettingsPluginContainer(sharedPreferences)
}
- override fun getFragment(): Fragment? {
- return ApplicationSettingsFragment()
+ @Composable
+ override fun content() {
+ ApplicationSettingsScreen()
}
}
diff --git a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/data/AppSettingsRepositoryImpl.kt b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/data/AppSettingsRepositoryImpl.kt
index 19ab546a..4ace4fbd 100644
--- a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/data/AppSettingsRepositoryImpl.kt
+++ b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/data/AppSettingsRepositoryImpl.kt
@@ -1,6 +1,7 @@
package com.redmadrobot.debug.plugin.appsettings.data
import android.content.SharedPreferences
+import androidx.core.content.edit
internal class AppSettingsRepositoryImpl(
private val sharedPreferencesList: List
@@ -12,7 +13,7 @@ internal class AppSettingsRepositoryImpl(
override fun updateSetting(key: String, value: Any) {
val sharedPreferences = sharedPreferencesList.find { it.contains(key) }
- sharedPreferences?.edit()?.apply {
+ sharedPreferences?.edit {
when (value) {
is Boolean -> putBoolean(key, value)
is Int -> putInt(key, value)
@@ -20,6 +21,6 @@ internal class AppSettingsRepositoryImpl(
is Float -> putFloat(key, value)
is String -> putString(key, value)
}
- }?.apply()
+ }
}
}
diff --git a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsFragment.kt b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsFragment.kt
deleted file mode 100644
index d466c04d..00000000
--- a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsFragment.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.redmadrobot.debug.plugin.appsettings.ui
-
-import android.os.Bundle
-import android.view.View
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.redmadrobot.debug.plugin.appsettings.R
-import com.redmadrobot.debug.plugin.appsettings.databinding.FragmentAppSettingsBinding
-import com.redmadrobot.debug.plugin.appsettings.AppSettingsPlugin
-import com.redmadrobot.debug.plugin.appsettings.AppSettingsPluginContainer
-import com.redmadrobot.debug.plugin.appsettings.ui.item.AppSettingItems
-import com.redmadrobot.debug.common.base.PluginFragment
-import com.redmadrobot.debug.common.extension.observe
-import com.redmadrobot.debug.common.extension.obtainViewModel
-import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.itemsadapter.itemsAdapter
-
-internal class ApplicationSettingsFragment : PluginFragment(R.layout.fragment_app_settings) {
-
- private var binding: FragmentAppSettingsBinding? = null
-
- private val settingsViewModel by lazy {
- obtainViewModel {
- getPlugin()
- .getContainer()
- .createApplicationSettingsViewModel()
- }
- }
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
- observe(settingsViewModel.settingsLiveData, ::setSettingList)
- settingsViewModel.loadSettings()
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- binding = FragmentAppSettingsBinding.bind(view).also {
- it.setViews()
- }
- }
-
- private fun FragmentAppSettingsBinding.setViews() {
- appSettings.layoutManager = LinearLayoutManager(context)
- }
-
- private fun setSettingList(settings: List) {
- val binding = binding ?: return
- binding.appSettings.adapter = itemsAdapter(settings) { item ->
- item.getItem(this)
- }
- }
-}
diff --git a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsScreen.kt b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsScreen.kt
new file mode 100644
index 00000000..83543d7e
--- /dev/null
+++ b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsScreen.kt
@@ -0,0 +1,175 @@
+package com.redmadrobot.debug.plugin.appsettings.ui
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.Button
+import androidx.compose.material.Card
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Switch
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.Lifecycle
+import com.redmadrobot.debug.core.extension.OnLifecycleEvent
+import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.core.extension.provideViewModel
+import com.redmadrobot.debug.plugin.appsettings.AppSettingsPlugin
+import com.redmadrobot.debug.plugin.appsettings.AppSettingsPluginContainer
+import com.redmadrobot.debug.plugin.appsettings.R
+
+@Composable
+internal fun ApplicationSettingsScreen(
+ viewModel: ApplicationSettingsViewModel = provideViewModel {
+ getPlugin()
+ .getContainer()
+ .createApplicationSettingsViewModel()
+ }
+) {
+ val state by viewModel.state.collectAsState()
+ ApplicationSettingsLayout(state, onValueChanged = viewModel::updateSetting)
+ OnLifecycleEvent { event ->
+ if (event == Lifecycle.Event.ON_RESUME) {
+ viewModel.loadSettings()
+ }
+ }
+}
+
+@Composable
+private fun ApplicationSettingsLayout(
+ state: List,
+ onValueChanged: (String, Any) -> Unit
+) {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ items(state) { itemUiModel ->
+ when (itemUiModel) {
+ is SettingItemUiModel.Header -> {
+ Text(
+ text = itemUiModel.header,
+ modifier = Modifier.padding(vertical = 16.dp)
+ )
+ }
+
+ is SettingItemUiModel.ValueItem -> {
+ ValueItem(itemUiModel, onValueChanged)
+ }
+
+ is SettingItemUiModel.BooleanItem -> {
+ BooleanItem(itemUiModel, onValueChanged)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ValueItem(
+ itemUiModel: SettingItemUiModel.ValueItem,
+ onValueChanged: (String, Any) -> Unit
+) {
+ var value by remember { mutableStateOf(itemUiModel.value.toString()) }
+ var hasFocus by remember { mutableStateOf(false) }
+ var error by remember(value) { mutableStateOf("") }
+ val focusManager = LocalFocusManager.current
+
+ Card(modifier = Modifier.fillMaxWidth()) {
+ Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
+ Row(horizontalArrangement = Arrangement.SpaceBetween) {
+ Text(
+ text = itemUiModel.key,
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f),
+ fontWeight = FontWeight.SemiBold,
+ )
+ if (hasFocus) {
+ Button(
+ onClick = {
+ try {
+ val newValue = itemUiModel.castToNeededType(value)
+ onValueChanged(itemUiModel.key, newValue)
+ focusManager.clearFocus(force = true)
+ } catch (e: Throwable) {
+ error = "Wrong data type"
+ }
+ }
+ ) {
+ Text(text = stringResource(id = R.string.save))
+ }
+ }
+ }
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = itemUiModel.value?.let { it::class.java.simpleName }
+ ?: stringResource(id = R.string.unknown_type),
+ color = Color.Gray,
+ fontSize = 12.sp
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .onFocusChanged { hasFocus = it.hasFocus },
+ value = value,
+ onValueChange = { value = it },
+ isError = error.isNotEmpty(),
+ label = { Text(text = stringResource(id = R.string.value)) },
+ )
+ if (error.isNotEmpty()) {
+ Text(text = error, color = Color.Red)
+ }
+ }
+ }
+}
+
+@Composable
+private fun BooleanItem(
+ itemUiModel: SettingItemUiModel.BooleanItem,
+ onValueChanged: (String, Boolean) -> Unit
+) {
+ Card(modifier = Modifier.fillMaxWidth()) {
+ Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
+ Text(
+ text = itemUiModel.key,
+ modifier = Modifier
+ .fillMaxWidth(),
+ fontWeight = FontWeight.SemiBold,
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = "Boolean",
+ color = Color.Gray,
+ fontSize = 12.sp
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ Switch(
+ checked = itemUiModel.value,
+ onCheckedChange = { onValueChanged(itemUiModel.key, it) }
+ )
+ }
+ }
+}
diff --git a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsViewModel.kt b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsViewModel.kt
index 140b0e26..9660831e 100644
--- a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsViewModel.kt
+++ b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/ApplicationSettingsViewModel.kt
@@ -1,50 +1,46 @@
package com.redmadrobot.debug.plugin.appsettings.ui
import android.content.SharedPreferences
-import androidx.lifecycle.MutableLiveData
import com.redmadrobot.debug.plugin.appsettings.data.AppSettingsRepository
-import com.redmadrobot.debug.plugin.appsettings.ui.item.AppSettingItems
-import com.redmadrobot.debug.common.base.PluginViewModel
+import com.redmadrobot.debug.core.internal.PluginViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.update
internal class ApplicationSettingsViewModel(
private val appSettingsRepository: AppSettingsRepository
) : PluginViewModel() {
- val settingsLiveData = MutableLiveData>()
+ val state = MutableStateFlow>(emptyList())
fun loadSettings() {
val settings = appSettingsRepository.getSettings()
- val settingItems = mapToItems(settings)
- settingsLiveData.value = settingItems
+ val newSettingItems = mapToItemsNew(settings)
+ state.update { newSettingItems }
}
- @Suppress("NewApi")
- private fun mapToItems(settings: List): List {
- val items = mutableListOf()
+ fun updateSetting(settingKey: String, newValue: Any) {
+ appSettingsRepository.updateSetting(settingKey, newValue)
+ loadSettings()
+ }
+
+ private fun mapToItemsNew(settings: List): MutableList {
+ val items = mutableListOf()
settings.forEach { sharedPreferences ->
/*Settings header*/
items.add(
- AppSettingItems.Header(sharedPreferences.toString())
+ SettingItemUiModel.Header(sharedPreferences.toString())
)
/*Map SharedPreferences to Items*/
sharedPreferences.all.forEach { (key, value) ->
val item = if (value is Boolean) {
- AppSettingItems.BooleanValueItem(key, value) { settingKey, newValue ->
- updateSetting(settingKey, newValue)
- }
+ SettingItemUiModel.BooleanItem(key, value)
} else {
- AppSettingItems.ValueItem(key, value) { settingKey, newValue ->
- updateSetting(settingKey, newValue)
- }
+ SettingItemUiModel.ValueItem(key, value)
}
items.add(item)
}
}
return items
}
-
- private fun updateSetting(settingKey: String, newValue: Any) {
- appSettingsRepository.updateSetting(settingKey, newValue)
- }
}
diff --git a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/SettingItemUiModel.kt b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/SettingItemUiModel.kt
new file mode 100644
index 00000000..9cf87d28
--- /dev/null
+++ b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/SettingItemUiModel.kt
@@ -0,0 +1,21 @@
+package com.redmadrobot.debug.plugin.appsettings.ui
+
+internal sealed class SettingItemUiModel {
+ data class Header(val header: String) : SettingItemUiModel()
+ data class ValueItem(
+ val key: String,
+ var value: Any?,
+ ) : SettingItemUiModel() {
+ fun castToNeededType(newValue: String): Any {
+ return when (value) {
+ is Long -> newValue.toLong()
+ is String -> newValue
+ is Float -> newValue.toFloat()
+ is Int -> newValue.toInt()
+ else -> error("Unexpected type")
+ }
+ }
+ }
+
+ data class BooleanItem(val key: String, var value: Boolean) : SettingItemUiModel()
+}
diff --git a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/item/AppSettingItems.kt b/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/item/AppSettingItems.kt
deleted file mode 100644
index 4746f8e0..00000000
--- a/plugins/app-settings/src/main/kotlin/com/redmadrobot/debug/plugin/appsettings/ui/item/AppSettingItems.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.redmadrobot.debug.plugin.appsettings.ui.item
-
-import androidx.core.view.isVisible
-import com.redmadrobot.debug.plugin.appsettings.R
-import com.redmadrobot.debug.plugin.appsettings.databinding.ItemHeaderBinding
-import com.redmadrobot.debug.plugin.appsettings.databinding.ItemPreferenceBooleanBinding
-import com.redmadrobot.debug.plugin.appsettings.databinding.ItemPreferenceValueBinding
-import com.redmadrobot.itemsadapter.ItemsAdapter
-import com.redmadrobot.itemsadapter.bind
-import timber.log.Timber
-
-internal sealed class AppSettingItems {
-
- abstract fun getItem(bindingContext: ItemsAdapter.BindingContext): ItemsAdapter.Item
-
- internal class Header(private val header: String) : AppSettingItems() {
- override fun getItem(bindingContext: ItemsAdapter.BindingContext): ItemsAdapter.Item {
- return bindingContext.bind(R.layout.item_header) {
- itemHeaderText.text = header
- }
- }
- }
-
- class ValueItem(
- private val key: String,
- var value: Any?,
- private val onChanged: (key: String, newValue: Any) -> Unit
- ) : AppSettingItems() {
- override fun getItem(bindingContext: ItemsAdapter.BindingContext): ItemsAdapter.Item {
- return bindingContext.bind(R.layout.item_preference_value) {
- settingLabel.text = key
- settingType.text = value?.let { it::class.java.simpleName }
- settingType.isVisible = value != null
-
- setSettingValue()
- saveValue.setOnClickListener { saveNewValue() }
- }
- }
-
- private fun ItemPreferenceValueBinding.setSettingValue() = with(settingValue) {
- setText(value.toString())
- clearFocus()
- settingValueContainer.error = null
-
- setOnFocusChangeListener { _, hasFocus ->
- saveValue.isVisible = hasFocus && value != null
- }
- }
-
- private fun ItemPreferenceValueBinding.saveNewValue() {
- val fieldValue = settingValue.text.toString()
- try {
- val newValue = castToNeededType(fieldValue)
- onChanged.invoke(key, newValue)
-
- value = newValue
- setSettingValue()
- } catch (e: Exception) {
- Timber.e(e)
- settingValueContainer.error = root.context.getString(R.string.wrong_type)
- }
- }
-
- private fun castToNeededType(newValue: String): Any {
- return when (value) {
- is Long -> newValue.toLong()
- is String -> newValue
- is Float -> newValue.toFloat()
- is Int -> newValue.toInt()
- else -> throw Throwable("Unexpected type")
- }
- }
- }
-
- class BooleanValueItem(
- private val key: String,
- var value: Boolean,
- private val onChanged: (key: String, newValue: Any) -> Unit
- ) : AppSettingItems() {
-
- override fun getItem(bindingContext: ItemsAdapter.BindingContext): ItemsAdapter.Item {
- return bindingContext.bind(R.layout.item_preference_boolean) {
- settingLabel.text = key
- settingType.text = value::class.java.simpleName
-
- with(settingSwitch) {
- setOnCheckedChangeListener(null)
- isChecked = value
- setOnCheckedChangeListener { _, isChecked ->
- onChanged.invoke(key, isChecked)
- value = isChecked
- }
- }
- }
- }
- }
-}
diff --git a/plugins/app-settings/src/main/res/layout/fragment_app_settings.xml b/plugins/app-settings/src/main/res/layout/fragment_app_settings.xml
deleted file mode 100644
index 7dc0e7a7..00000000
--- a/plugins/app-settings/src/main/res/layout/fragment_app_settings.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
diff --git a/plugins/app-settings/src/main/res/layout/item_header.xml b/plugins/app-settings/src/main/res/layout/item_header.xml
deleted file mode 100644
index 0c14cf96..00000000
--- a/plugins/app-settings/src/main/res/layout/item_header.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
diff --git a/plugins/app-settings/src/main/res/layout/item_preference_boolean.xml b/plugins/app-settings/src/main/res/layout/item_preference_boolean.xml
deleted file mode 100644
index def246fc..00000000
--- a/plugins/app-settings/src/main/res/layout/item_preference_boolean.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/app-settings/src/main/res/layout/item_preference_unsupported.xml b/plugins/app-settings/src/main/res/layout/item_preference_unsupported.xml
deleted file mode 100644
index 74ed1890..00000000
--- a/plugins/app-settings/src/main/res/layout/item_preference_unsupported.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/app-settings/src/main/res/layout/item_preference_value.xml b/plugins/app-settings/src/main/res/layout/item_preference_value.xml
deleted file mode 100644
index a85965a4..00000000
--- a/plugins/app-settings/src/main/res/layout/item_preference_value.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/app-settings/src/main/res/values/strings.xml b/plugins/app-settings/src/main/res/values/strings.xml
index 6a25ae78..3128050a 100644
--- a/plugins/app-settings/src/main/res/values/strings.xml
+++ b/plugins/app-settings/src/main/res/values/strings.xml
@@ -4,4 +4,5 @@
save
Wrong type
value
+ Unknown type
diff --git a/plugins/flipper/build.gradle.kts b/plugins/flipper/build.gradle.kts
index 35ff6cad..385067b5 100644
--- a/plugins/flipper/build.gradle.kts
+++ b/plugins/flipper/build.gradle.kts
@@ -38,7 +38,11 @@ android {
}
buildFeatures {
- viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
namespace = "com.redmadrobot.debug.plugin.flipper"
}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPlugin.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPlugin.kt
index b0a86c63..70d16783 100644
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPlugin.kt
+++ b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPlugin.kt
@@ -1,14 +1,16 @@
package com.redmadrobot.debug.plugin.flipper
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.fragment.app.Fragment
import com.redmadrobot.debug.core.internal.CommonContainer
import com.redmadrobot.debug.core.extension.getPlugin
import com.redmadrobot.debug.core.plugin.Plugin
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
+import com.redmadrobot.debug.plugin.flipper.ui.FlipperFeatureScreen
import com.redmadrobot.flipper.config.FlipperValue
-import com.redmadrobot.debug.plugin.flipper.ui.FlipperFeaturesFragment
import kotlinx.coroutines.flow.Flow
-import java.util.*
+import java.util.Collections
public class FlipperPlugin(
private val toggles: List,
@@ -53,8 +55,9 @@ public class FlipperPlugin(
)
}
- override fun getFragment(): Fragment {
- return FlipperFeaturesFragment()
+ @Composable
+ override fun content() {
+ FlipperFeatureScreen()
}
}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPluginContainer.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPluginContainer.kt
index c3bec71b..638b9270 100644
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPluginContainer.kt
+++ b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/FlipperPluginContainer.kt
@@ -4,7 +4,7 @@ import android.content.Context
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
import com.redmadrobot.debug.plugin.flipper.data.FeatureTogglesRepository
import com.redmadrobot.debug.plugin.flipper.ui.FlipperFeaturesViewModel
-import com.redmadrobot.debug.plugin.flipper.ui.dialog.SourceSelectionViewModel
+import com.redmadrobot.debug.plugin.flipper.ui.SourceSelectionViewModel
internal class FlipperPluginContainer(
private val context: Context,
@@ -20,6 +20,6 @@ internal class FlipperPluginContainer(
}
fun createFlipperSourceSelectionViewModel(): SourceSelectionViewModel {
- return SourceSelectionViewModel(context, featureTogglesRepository)
+ return SourceSelectionViewModel(featureTogglesRepository)
}
}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/data/FeatureTogglesRepository.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/data/FeatureTogglesRepository.kt
index c76772ec..0bebeef5 100644
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/data/FeatureTogglesRepository.kt
+++ b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/data/FeatureTogglesRepository.kt
@@ -28,7 +28,8 @@ internal class FeatureTogglesRepository(
private val defaultSource = FeatureTogglesSource("Default")
private val defaultFeatureToggles = defaultToggles.associate { it.id to it.value }
- private val sources = MutableStateFlow(emptyMap>>())
+ private val sources =
+ MutableStateFlow(emptyMap>>())
private val selectedSource = MutableStateFlow(defaultSource)
init {
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeatureScreen.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeatureScreen.kt
new file mode 100644
index 00000000..4fa21675
--- /dev/null
+++ b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeatureScreen.kt
@@ -0,0 +1,403 @@
+package com.redmadrobot.debug.plugin.flipper.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.Checkbox
+import androidx.compose.material.Divider
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.ModalBottomSheetLayout
+import androidx.compose.material.ModalBottomSheetState
+import androidx.compose.material.ModalBottomSheetValue
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Switch
+import androidx.compose.material.SwitchDefaults
+import androidx.compose.material.Text
+import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.core.extension.provideViewModel
+import com.redmadrobot.debug.plugin.flipper.FlipperPlugin
+import com.redmadrobot.debug.plugin.flipper.FlipperPluginContainer
+import com.redmadrobot.debug.plugin.flipper.R
+import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem
+import com.redmadrobot.flipper.config.FlipperValue
+import kotlinx.coroutines.launch
+import com.redmadrobot.debug.core.R as CoreR
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+internal fun FlipperFeatureScreen(
+ viewModel: FlipperFeaturesViewModel = provideViewModel {
+ getPlugin()
+ .getContainer()
+ .createFlipperFeaturesViewModel()
+ },
+) {
+ val state by viewModel.state.collectAsState()
+ val bottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
+ val coroutineScope = rememberCoroutineScope()
+ var showResetConfirmationDialog by remember { mutableStateOf(false) }
+
+ SourceSelectionDialog(
+ state = bottomSheetState,
+ sourceSelectionViewModel = provideViewModel {
+ getPlugin()
+ .getContainer()
+ .createFlipperSourceSelectionViewModel()
+ },
+ content = {
+ FlipperFeatureLayout(
+ state,
+ onQueryChange = viewModel::onQueryChanged,
+ onValueChange = viewModel::onFeatureValueChanged,
+ onGroupToggleStateChange = viewModel::onGroupToggleStateChanged,
+ onGroupClick = viewModel::onGroupClick,
+ onChangeSourceClick = { coroutineScope.launch { bottomSheetState.show() } },
+ onResetClick = { showResetConfirmationDialog = true }
+ )
+ }
+ )
+ if (showResetConfirmationDialog) {
+ AlertDialog(
+ onDismissRequest = { showResetConfirmationDialog = false },
+ confirmButton = {
+ Button(onClick = {
+ viewModel.onResetClicked()
+ showResetConfirmationDialog = false
+ }) {
+ Text(text = stringResource(id = R.string.flipper_plugin_clear_changes))
+ }
+ },
+ dismissButton = {
+ Button(onClick = { showResetConfirmationDialog = false }) {
+ Text(text = stringResource(id = R.string.flipper_plugin_cancel))
+ }
+ },
+ text = { Text(text = stringResource(id = R.string.flipper_plugin_dialog_title_feature_toggles_reset)) }
+ )
+ }
+}
+
+@Composable
+internal fun FlipperFeatureLayout(
+ state: FlipperFeaturesViewState,
+ onQueryChange: (String) -> Unit,
+ onValueChange: (String, FlipperValue) -> Unit,
+ onGroupToggleStateChange: (String, Boolean) -> Unit,
+ onGroupClick: (String) -> Unit,
+ onChangeSourceClick: () -> Unit,
+ onResetClick: () -> Unit
+) {
+ Box(modifier = Modifier.fillMaxSize()) {
+ Column(modifier = Modifier.fillMaxSize()) {
+ Filter(onQueryChanged = onQueryChange)
+ LazyColumn(contentPadding = PaddingValues(bottom = 60.dp)) {
+ items(state.items) { item ->
+ when {
+ item is FlipperItem.Group -> {
+ GroupItem(
+ group = item,
+ onGroupToggleStateChange = onGroupToggleStateChange,
+ onGroupClick = onGroupClick
+ )
+ }
+
+ item is FlipperItem.Feature && item.value is FlipperValue.BooleanValue -> {
+ BooleanValueItem(feature = item, onValueChange = onValueChange)
+ }
+
+ item is FlipperItem.Feature && item.value is FlipperValue.StringValue -> {
+ StringValueItem(feature = item, onValueChange = onValueChange)
+ }
+ }
+ Divider()
+ }
+ }
+ }
+ Box(
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxWidth()
+ .align(Alignment.BottomCenter)
+ ) {
+ Button(
+ modifier = Modifier.height(44.dp),
+ onClick = onChangeSourceClick
+ ) {
+ Text(text = stringResource(id = R.string.flipper_plugin_button_switch_source).uppercase())
+ }
+
+ Button(
+ modifier = Modifier
+ .height(44.dp)
+ .align(Alignment.CenterEnd),
+ onClick = onResetClick
+ ) {
+ Text(text = stringResource(id = R.string.flipper_plugin_clear_changes).uppercase())
+ }
+ }
+ }
+}
+
+@Composable
+private fun Filter(onQueryChanged: (String) -> Unit) {
+ var filterValue by remember { mutableStateOf("") }
+ val focusManager = LocalFocusManager.current
+ val focusRequester = remember { FocusRequester() }
+ var hasFocus by remember { mutableStateOf(false) }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .focusRequester(focusRequester)
+ .onFocusChanged { hasFocus = it.hasFocus },
+ value = filterValue,
+ onValueChange = {
+ filterValue = it
+ onQueryChanged(it)
+ },
+ placeholder = { Text(text = stringResource(id = R.string.flipper_plugin_filter)) },
+ trailingIcon = {
+ if (!hasFocus) {
+ IconButton(onClick = { focusRequester.requestFocus() }) {
+ Icon(
+ painter = painterResource(id = R.drawable.icon_search),
+ contentDescription = null
+ )
+ }
+ } else {
+ IconButton(
+ onClick = {
+ filterValue = ""
+ onQueryChanged("")
+ focusManager.clearFocus(true)
+ }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.icon_clear),
+ contentDescription = null
+ )
+ }
+ }
+ }
+ )
+}
+
+@Composable
+private fun GroupItem(
+ group: FlipperItem.Group,
+ onGroupToggleStateChange: (String, Boolean) -> Unit,
+ onGroupClick: (String) -> Unit,
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(colorResource(id = CoreR.color.super_light_gray))
+ .clickable { onGroupClick(group.name) }
+ .padding(8.dp)
+ ) {
+ Text(
+ modifier = Modifier.align(Alignment.CenterStart),
+ text = group.name,
+ color = if (group.editable) Color.Black else Color.Gray,
+ fontWeight = FontWeight.Bold
+ )
+ Switch(
+ modifier = Modifier.align(Alignment.CenterEnd),
+ checked = group.allEnabled,
+ onCheckedChange = { onGroupToggleStateChange(group.name, it) },
+ enabled = group.editable,
+ colors = SwitchDefaults.colors(checkedThumbColor = MaterialTheme.colors.secondary)
+ )
+ }
+}
+
+
+@Composable
+private fun StringValueItem(
+ feature: FlipperItem.Feature,
+ onValueChange: (String, FlipperValue) -> Unit
+) {
+ var value by remember { mutableStateOf((feature.value as FlipperValue.StringValue).value) }
+ var hasFocus by remember { mutableStateOf(false) }
+ val focusManager = LocalFocusManager.current
+
+ Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
+
+ Row(horizontalArrangement = Arrangement.SpaceBetween) {
+ Text(
+ text = feature.description,
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f),
+ color = if (feature.editable) Color.Black else Color.Gray
+ )
+ if (hasFocus) {
+ Button(
+ onClick = {
+ onValueChange(feature.id, FlipperValue.StringValue(value))
+ focusManager.clearFocus(force = true)
+ }
+ ) {
+ Text(text = stringResource(id = R.string.flipper_plugin_update))
+ }
+ }
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .onFocusChanged { hasFocus = it.hasFocus },
+ value = value,
+ onValueChange = { value = it },
+ label = { Text(text = stringResource(id = R.string.flipper_plugin_value)) },
+ enabled = feature.editable,
+ )
+ }
+}
+
+@Composable
+private fun BooleanValueItem(
+ feature: FlipperItem.Feature,
+ onValueChange: (String, FlipperValue) -> Unit
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Checkbox(
+ checked = (feature.value as FlipperValue.BooleanValue).value,
+ onCheckedChange = { onValueChange(feature.id, FlipperValue.BooleanValue(it)) },
+ enabled = feature.editable
+ )
+ Text(
+ text = feature.description,
+ color = if (feature.editable) Color.Black else Color.Gray
+ )
+ }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+private fun SourceSelectionDialog(
+ content: @Composable () -> Unit,
+ state: ModalBottomSheetState,
+ sourceSelectionViewModel: SourceSelectionViewModel
+) {
+ val data by sourceSelectionViewModel.sources.collectAsState()
+ ModalBottomSheetLayout(
+ sheetState = state,
+ sheetShape = MaterialTheme.shapes.small,
+ sheetContent = {
+ Spacer(modifier = Modifier.height(1.dp))
+
+ LazyColumn {
+ items(data) { source ->
+ Column {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 16.dp, end = 8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(modifier = Modifier.weight(1F), text = source.name)
+ Checkbox(
+ checked = source.selected,
+ onCheckedChange = { sourceSelectionViewModel.onSelectSource(source.name) })
+ }
+ Divider()
+ }
+ }
+ }
+ },
+ content = content
+ )
+}
+
+@Preview
+@Composable
+private fun GroupItemPreview() {
+ GroupItem(
+ group = FlipperItem.Group(
+ "Name",
+ allEnabled = true,
+ editable = false
+ ),
+ onGroupToggleStateChange = { _: String, _: Boolean -> },
+ onGroupClick = {},
+ )
+}
+
+@Preview
+@Composable
+private fun BooleanItemPreview() {
+ BooleanValueItem(
+ feature = FlipperItem.Feature(
+ "id",
+ FlipperValue.BooleanValue(true),
+ description = "Boolean",
+ editable = false
+ ),
+ onValueChange = { _: String, _: FlipperValue -> }
+ )
+}
+
+@Preview
+@Composable
+private fun StringItemPreview() {
+ StringValueItem(
+ feature = FlipperItem.Feature(
+ "id",
+ FlipperValue.StringValue("Value"),
+ description = "StringToggle",
+ editable = false,
+ ),
+ onValueChange = { _: String, _: FlipperValue -> }
+ )
+}
+
+@Preview
+@Composable
+private fun FilterPreview() {
+ Filter(onQueryChanged = {})
+}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesFragment.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesFragment.kt
deleted file mode 100644
index 2f4d0e51..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesFragment.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui
-
-import android.os.Bundle
-import android.view.View
-import androidx.core.widget.addTextChangedListener
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.flowWithLifecycle
-import androidx.lifecycle.lifecycleScope
-import com.redmadrobot.debug.common.base.PluginFragment
-import com.redmadrobot.debug.common.extension.obtainShareViewModel
-import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.debug.plugin.flipper.R
-import com.redmadrobot.debug.plugin.flipper.databinding.FragmentFlipperFeaturesBinding
-import com.redmadrobot.debug.plugin.flipper.FlipperPlugin
-import com.redmadrobot.debug.plugin.flipper.FlipperPluginContainer
-import com.redmadrobot.debug.plugin.flipper.ui.dialog.SourceSelectionDialogFragment
-import com.redmadrobot.debug.plugin.flipper.ui.recycler.FlipperFeaturesAdapter
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-
-internal class FlipperFeaturesFragment : PluginFragment(R.layout.fragment_flipper_features) {
-
- private var _binding: FragmentFlipperFeaturesBinding? = null
- private val binding get() = _binding!!
-
- private val viewModel: FlipperFeaturesViewModel by lazy {
- obtainShareViewModel {
- getPlugin()
- .getContainer()
- .createFlipperFeaturesViewModel()
- }
- }
-
- private lateinit var featuresAdapter: FlipperFeaturesAdapter
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- _binding = FragmentFlipperFeaturesBinding.bind(view)
-
- this.featuresAdapter = FlipperFeaturesAdapter(
- onGroupClick = viewModel::onGroupClick,
- onFeatureValueChanged = viewModel::onFeatureValueChanged,
- onGroupToggleStateChanged = viewModel::onGroupToggleStateChanged,
- )
-
- binding.featureList.adapter = this.featuresAdapter
- binding.query.addTextChangedListener { text ->
- viewModel.onQueryChanged(text?.toString().orEmpty())
- }
- binding.resetToDefault.setOnClickListener {
- SourceSelectionDialogFragment()
- .show(childFragmentManager, SourceSelectionDialogFragment::class.simpleName)
- }
-
- viewModel.state
- .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.CREATED)
- .onEach { state ->
- featuresAdapter.submitList(state.items)
- }
- .launchIn(viewLifecycleOwner.lifecycleScope)
- }
-
- override fun onDestroyView() {
- _binding = null
-
- super.onDestroyView()
- }
-
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesViewModel.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesViewModel.kt
index b2bc97ee..435183ec 100644
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesViewModel.kt
+++ b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesViewModel.kt
@@ -1,13 +1,22 @@
package com.redmadrobot.debug.plugin.flipper.ui
import androidx.lifecycle.viewModelScope
-import com.redmadrobot.debug.common.base.PluginViewModel
-import com.redmadrobot.flipper.config.FlipperValue
-import com.redmadrobot.debug.plugin.flipper.data.FeatureTogglesRepository
+import com.redmadrobot.debug.core.internal.PluginViewModel
import com.redmadrobot.debug.plugin.flipper.PluginToggle
+import com.redmadrobot.debug.plugin.flipper.data.FeatureTogglesRepository
import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem
+import com.redmadrobot.flipper.config.FlipperValue
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
internal class FlipperFeaturesViewModel(
@@ -174,3 +183,5 @@ internal class FlipperFeaturesViewModel(
private const val QUERY_DEBOUNCE_MS = 450L
}
}
+
+internal data class FlipperFeaturesViewState(val items: List = emptyList())
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesViewState.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesViewState.kt
deleted file mode 100644
index fea3f12b..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/FlipperFeaturesViewState.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui
-
-import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem
-
-internal data class FlipperFeaturesViewState(
- val items: List = emptyList(),
-)
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/SourceSelectionViewModel.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/SourceSelectionViewModel.kt
similarity index 59%
rename from plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/SourceSelectionViewModel.kt
rename to plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/SourceSelectionViewModel.kt
index f469ca33..102107b6 100644
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/SourceSelectionViewModel.kt
+++ b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/SourceSelectionViewModel.kt
@@ -1,19 +1,22 @@
-package com.redmadrobot.debug.plugin.flipper.ui.dialog
+package com.redmadrobot.debug.plugin.flipper.ui
-import android.content.Context
import androidx.lifecycle.viewModelScope
-import com.redmadrobot.debug.common.base.PluginViewModel
-import com.redmadrobot.debug.plugin.flipper.R
+import com.redmadrobot.debug.core.internal.PluginViewModel
import com.redmadrobot.debug.plugin.flipper.data.FeatureTogglesRepository
import com.redmadrobot.debug.plugin.flipper.data.FeatureTogglesSource
-import com.redmadrobot.debug.plugin.flipper.ui.dialog.data.SelectableSource
+import com.redmadrobot.debug.plugin.flipper.ui.data.SelectableSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.*
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
internal class SourceSelectionViewModel(
- private val context: Context,
private val repository: FeatureTogglesRepository,
) : PluginViewModel() {
@@ -26,11 +29,6 @@ internal class SourceSelectionViewModel(
)
val clearingRequest = _clearingRequest.asSharedFlow()
- private val clearingSource = SelectableSource(
- name = context.getString(R.string.flipper_plugin_clear_changes),
- selected = false,
- )
-
init {
combine(
repository.getSources(),
@@ -46,25 +44,13 @@ internal class SourceSelectionViewModel(
)
}
- _sources.emit(listOf(clearingSource) + selectableSources)
+ _sources.emit(selectableSources)
}
.flowOn(Dispatchers.Default)
.launchIn(viewModelScope)
}
fun onSelectSource(sourceName: String) {
- if (clearingSource.name == sourceName) {
- _clearingRequest.tryEmit(Unit)
- return
- }
- repository.setSelectedSource(
- FeatureTogglesSource(sourceName)
- )
- }
-
- fun onClearTogglesConfirm() {
- viewModelScope.launch {
- repository.resetAllToDefault()
- }
+ repository.setSelectedSource(FeatureTogglesSource(sourceName))
}
}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/data/SelectableSource.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/data/SelectableSource.kt
similarity index 60%
rename from plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/data/SelectableSource.kt
rename to plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/data/SelectableSource.kt
index 89761414..bca4b74f 100644
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/data/SelectableSource.kt
+++ b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/data/SelectableSource.kt
@@ -1,4 +1,4 @@
-package com.redmadrobot.debug.plugin.flipper.ui.dialog.data
+package com.redmadrobot.debug.plugin.flipper.ui.data
internal data class SelectableSource(
val name: String,
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/SourceSelectionDialogFragment.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/SourceSelectionDialogFragment.kt
deleted file mode 100644
index d19bffc7..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/SourceSelectionDialogFragment.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.dialog
-
-import android.app.AlertDialog
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.flowWithLifecycle
-import androidx.lifecycle.lifecycleScope
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.redmadrobot.debug.common.extension.obtainShareViewModel
-import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.debug.plugin.flipper.R
-import com.redmadrobot.debug.plugin.flipper.databinding.DialogFlipperSourceBinding
-import com.redmadrobot.debug.plugin.flipper.FlipperPlugin
-import com.redmadrobot.debug.plugin.flipper.FlipperPluginContainer
-import com.redmadrobot.debug.plugin.flipper.ui.dialog.recycler.SourceSelectionAdapter
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-
-internal class SourceSelectionDialogFragment : BottomSheetDialogFragment() {
-
- private val viewModel: SourceSelectionViewModel by lazy {
- obtainShareViewModel {
- getPlugin()
- .getContainer()
- .createFlipperSourceSelectionViewModel()
- }
- }
-
- private lateinit var sourcesAdapter: SourceSelectionAdapter
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return DialogFlipperSourceBinding.inflate(
- LayoutInflater.from(context),
- null,
- false
- ).root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val binding = DialogFlipperSourceBinding.bind(view)
-
- sourcesAdapter = SourceSelectionAdapter(
- onSourceClick = { sourceName ->
- viewModel.onSelectSource(sourceName)
- },
- )
-
- binding.root.adapter = sourcesAdapter
-
- viewModel.sources
- .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.CREATED)
- .onEach(sourcesAdapter::submitList)
- .launchIn(viewLifecycleOwner.lifecycleScope)
-
- viewModel.clearingRequest
- .onEach {
- showResetConfirmationDialog()
- }
- .launchIn(viewLifecycleOwner.lifecycleScope)
- }
-
- private fun showResetConfirmationDialog() {
- AlertDialog.Builder(requireContext())
- .setMessage(R.string.flipper_plugin_dialog_title_feature_toggles_reset)
- .setPositiveButton(android.R.string.ok) { dialog, _ ->
- viewModel.onClearTogglesConfirm()
-
- dialog.dismiss()
- }
- .setNegativeButton(android.R.string.cancel) { dialog, _ ->
- dialog.dismiss()
- }
- .create()
- .show()
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceDiffCallback.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceDiffCallback.kt
deleted file mode 100644
index a1294790..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceDiffCallback.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.dialog.recycler
-
-import androidx.recyclerview.widget.DiffUtil
-import com.redmadrobot.debug.plugin.flipper.ui.dialog.data.SelectableSource
-
-internal class SourceDiffCallback : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(oldItem: SelectableSource, newItem: SelectableSource): Boolean {
- return oldItem.name == newItem.name
- }
-
- override fun areContentsTheSame(oldItem: SelectableSource, newItem: SelectableSource): Boolean {
- return oldItem == newItem
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceSelectionAdapter.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceSelectionAdapter.kt
deleted file mode 100644
index 92c57934..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceSelectionAdapter.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.dialog.recycler
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.recyclerview.widget.ListAdapter
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperSourceBinding
-import com.redmadrobot.debug.plugin.flipper.ui.dialog.data.SelectableSource
-
-internal class SourceSelectionAdapter(
- private val onSourceClick: (name: String) -> Unit,
-) : ListAdapter(
- SourceDiffCallback()
-) {
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SourceViewHolder {
- return SourceViewHolder(
- ItemFlipperSourceBinding.inflate(LayoutInflater.from(parent.context)).root,
- onSourceClick::invoke,
- )
- }
-
- override fun onBindViewHolder(holder: SourceViewHolder, position: Int) {
- holder.bind(getItem(position))
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceViewHolder.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceViewHolder.kt
deleted file mode 100644
index 4e47cf87..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/dialog/recycler/SourceViewHolder.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.dialog.recycler
-
-import android.view.View
-import androidx.core.view.isVisible
-import androidx.recyclerview.widget.RecyclerView
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperSourceBinding
-import com.redmadrobot.debug.plugin.flipper.ui.dialog.data.SelectableSource
-
-internal class SourceViewHolder(
- view: View,
- private val onSourceClick: (name: String) -> Unit,
-) : RecyclerView.ViewHolder(view) {
-
- private val binding = ItemFlipperSourceBinding.bind(itemView)
-
- fun bind(source: SelectableSource) {
- binding.sourceName.apply {
- text = source.name
- setOnClickListener {
- onSourceClick.invoke(source.name)
- }
- }
-
- binding.sourceSelected.isVisible = source.selected
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/BooleanFeatureViewHolder.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/BooleanFeatureViewHolder.kt
deleted file mode 100644
index e4d6d61a..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/BooleanFeatureViewHolder.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.recycler
-
-import android.view.View
-import androidx.recyclerview.widget.RecyclerView
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperFeatureBooleanBinding
-import com.redmadrobot.flipper.config.FlipperValue
-
-internal class BooleanFeatureViewHolder(
- itemView: View,
- private val onFeatureValueChanged: (feature: String, value: FlipperValue) -> Unit,
-) : RecyclerView.ViewHolder(itemView) {
-
- private val binding = ItemFlipperFeatureBooleanBinding.bind(itemView)
-
- fun bind(
- featureId: String,
- value: FlipperValue.BooleanValue,
- description: String,
- editable: Boolean,
- ) {
- binding.checkbox.apply {
- setOnCheckedChangeListener(null)
-
- text = description
- isEnabled = editable
- isChecked = value.value
-
- setOnCheckedChangeListener { _, isChecked ->
- onFeatureValueChanged.invoke(featureId, FlipperValue.BooleanValue(isChecked))
- }
- }
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/FlipperFeaturesAdapter.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/FlipperFeaturesAdapter.kt
deleted file mode 100644
index 89d45282..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/FlipperFeaturesAdapter.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.recycler
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperFeatureBooleanBinding
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperFeatureGroupBinding
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperFeatureStringBinding
-import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem
-import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem.Feature
-import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem.Group
-import com.redmadrobot.flipper.config.FlipperValue
-
-internal class FlipperFeaturesAdapter(
- private val onGroupClick: (groupName: String) -> Unit,
- private val onFeatureValueChanged: (featureId: String, value: FlipperValue) -> Unit,
- private val onGroupToggleStateChanged: (groupName: String, checked: Boolean) -> Unit,
-) : ListAdapter(
- FlipperFeaturesDiffCallback(),
-) {
-
- override fun getItemViewType(position: Int): Int {
- return when (val item = getItem(position)) {
- is Group -> ViewType.GROUP.ordinal
-
- is Feature -> {
- when (item.value) {
- is FlipperValue.BooleanValue -> ViewType.BOOLEAN.ordinal
- is FlipperValue.StringValue -> ViewType.STRING.ordinal
- else -> error("FlipperValue ${item.value::class} not supported")
- }
- }
- }
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- return when (ViewType.values()[viewType]) {
- ViewType.BOOLEAN -> {
- val binding = ItemFlipperFeatureBooleanBinding.inflate(
- LayoutInflater.from(parent.context),
- parent,
- false
- )
-
- BooleanFeatureViewHolder(
- itemView = binding.root,
- onFeatureValueChanged = onFeatureValueChanged::invoke,
- )
- }
-
- ViewType.GROUP -> {
- val binding = ItemFlipperFeatureGroupBinding.inflate(
- LayoutInflater.from(parent.context),
- parent,
- false
- )
-
- GroupViewHolder(
- itemView = binding.root,
- onGroupClick = onGroupClick::invoke,
- onTogglesStateChanged = onGroupToggleStateChanged::invoke,
- )
- }
-
- ViewType.STRING -> {
- val binding = ItemFlipperFeatureStringBinding.inflate(
- LayoutInflater.from(parent.context),
- parent,
- false
- )
-
- StringFeatureViewHolder(
- itemView = binding.root,
- onFeatureValueChanged = onFeatureValueChanged::invoke,
- )
- }
-
- else -> throw IllegalStateException("Can't create viewHolder for given viewType")
- }
- }
-
- override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
- val item = getItem(position)
-
- when (ViewType.values()[getItemViewType(position)]) {
- ViewType.GROUP -> {
- val group = item as Group
- val groupHolder = holder as GroupViewHolder
-
- groupHolder.bind(group.name, group.editable, group.allEnabled)
- }
-
- ViewType.BOOLEAN -> {
- val booleanItem = item as Feature
- val booleanHolder = holder as BooleanFeatureViewHolder
-
- booleanHolder.bind(
- featureId = booleanItem.id,
- value = booleanItem.value as FlipperValue.BooleanValue,
- editable = booleanItem.editable,
- description = booleanItem.description,
- )
- }
-
- ViewType.STRING -> {
- val booleanItem = item as Feature
- val stringHolder = holder as StringFeatureViewHolder
-
- stringHolder.bind(
- featureId = booleanItem.id,
- value = booleanItem.value as FlipperValue.StringValue,
- editable = booleanItem.editable,
- description = booleanItem.description,
- )
- }
- }
- }
-
- private enum class ViewType {
- GROUP,
- BOOLEAN,
- STRING
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/FlipperFeaturesDiffCallback.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/FlipperFeaturesDiffCallback.kt
deleted file mode 100644
index 4894dae8..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/FlipperFeaturesDiffCallback.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.recycler
-
-import androidx.recyclerview.widget.DiffUtil
-import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem
-import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem.Feature
-import com.redmadrobot.debug.plugin.flipper.ui.data.FlipperItem.Group
-
-internal class FlipperFeaturesDiffCallback : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldItem: FlipperItem,
- newItem: FlipperItem,
- ): Boolean {
- return when {
- oldItem is Feature && newItem is Feature -> oldItem.id == newItem.id
- oldItem is Group && newItem is Group -> oldItem.name == newItem.name
- else -> false
- }
- }
-
- override fun areContentsTheSame(
- oldItem: FlipperItem,
- newItem: FlipperItem,
- ): Boolean {
- return oldItem == newItem
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/GroupViewHolder.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/GroupViewHolder.kt
deleted file mode 100644
index 46174763..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/GroupViewHolder.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.recycler
-
-import android.view.View
-import androidx.core.view.isVisible
-import androidx.recyclerview.widget.RecyclerView
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperFeatureGroupBinding
-
-internal class GroupViewHolder(
- itemView: View,
- private val onGroupClick: (groupName: String) -> Unit,
- private val onTogglesStateChanged: (groupName: String, checked: Boolean) -> Unit,
-) : RecyclerView.ViewHolder(itemView) {
-
- private val binding = ItemFlipperFeatureGroupBinding.bind(itemView)
-
- fun bind(
- name: String,
- editable: Boolean,
- allEnabled: Boolean,
- ) = with(binding) {
- root.setOnClickListener(null)
- everyToggleInGroup.setOnCheckedChangeListener(null)
-
- groupName.text = name
- everyToggleInGroup.isChecked = allEnabled
- everyToggleInGroup.isVisible = editable
-
- root.setOnClickListener {
- onGroupClick.invoke(name)
- }
- everyToggleInGroup.setOnCheckedChangeListener { _, isChecked ->
- onTogglesStateChanged.invoke(name, isChecked)
- }
- }
-}
diff --git a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/StringFeatureViewHolder.kt b/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/StringFeatureViewHolder.kt
deleted file mode 100644
index 4bb39435..00000000
--- a/plugins/flipper/src/main/kotlin/com/redmadrobot/debug/plugin/flipper/ui/recycler/StringFeatureViewHolder.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.redmadrobot.debug.plugin.flipper.ui.recycler
-
-import android.view.View
-import androidx.core.view.isVisible
-import androidx.recyclerview.widget.RecyclerView
-import com.redmadrobot.debug.plugin.flipper.databinding.ItemFlipperFeatureStringBinding
-import com.redmadrobot.flipper.config.FlipperValue
-
-internal class StringFeatureViewHolder(
- itemView: View,
- private val onFeatureValueChanged: (feature: String, value: FlipperValue) -> Unit,
-) : RecyclerView.ViewHolder(itemView) {
-
- private val binding = ItemFlipperFeatureStringBinding.bind(itemView)
-
- fun bind(
- featureId: String,
- value: FlipperValue.StringValue,
- description: String,
- editable: Boolean,
- ) = with(binding) {
-
- flipperPluginValue.apply {
- setText(value.value)
- isEnabled = editable
- clearFocus()
- }
- flipperPluginValueLabel.text = description
-
- flipperPluginUpdate.setOnClickListener {
- onFeatureValueChanged(featureId, FlipperValue.StringValue(flipperPluginValue.text.toString()))
- flipperPluginValue.clearFocus()
- }
-
- flipperPluginValue.setOnFocusChangeListener { _, hasFocus ->
- flipperPluginUpdate.isVisible = hasFocus
- }
- }
-}
diff --git a/plugins/flipper/src/main/res/drawable/icon_clear.xml b/plugins/flipper/src/main/res/drawable/icon_clear.xml
new file mode 100644
index 00000000..844b6b62
--- /dev/null
+++ b/plugins/flipper/src/main/res/drawable/icon_clear.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/plugins/flipper/src/main/res/drawable/icon_search.xml b/plugins/flipper/src/main/res/drawable/icon_search.xml
new file mode 100644
index 00000000..a5687c63
--- /dev/null
+++ b/plugins/flipper/src/main/res/drawable/icon_search.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/plugins/flipper/src/main/res/layout/dialog_flipper_source.xml b/plugins/flipper/src/main/res/layout/dialog_flipper_source.xml
deleted file mode 100644
index acbed849..00000000
--- a/plugins/flipper/src/main/res/layout/dialog_flipper_source.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
diff --git a/plugins/flipper/src/main/res/layout/fragment_flipper_features.xml b/plugins/flipper/src/main/res/layout/fragment_flipper_features.xml
deleted file mode 100644
index 10878557..00000000
--- a/plugins/flipper/src/main/res/layout/fragment_flipper_features.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/flipper/src/main/res/layout/item_flipper_feature_boolean.xml b/plugins/flipper/src/main/res/layout/item_flipper_feature_boolean.xml
deleted file mode 100644
index 3b7a57ec..00000000
--- a/plugins/flipper/src/main/res/layout/item_flipper_feature_boolean.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
diff --git a/plugins/flipper/src/main/res/layout/item_flipper_feature_group.xml b/plugins/flipper/src/main/res/layout/item_flipper_feature_group.xml
deleted file mode 100644
index e032d3ae..00000000
--- a/plugins/flipper/src/main/res/layout/item_flipper_feature_group.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/plugins/flipper/src/main/res/layout/item_flipper_feature_string.xml b/plugins/flipper/src/main/res/layout/item_flipper_feature_string.xml
deleted file mode 100644
index 365b0e97..00000000
--- a/plugins/flipper/src/main/res/layout/item_flipper_feature_string.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/flipper/src/main/res/layout/item_flipper_source.xml b/plugins/flipper/src/main/res/layout/item_flipper_source.xml
deleted file mode 100644
index b3a86ef1..00000000
--- a/plugins/flipper/src/main/res/layout/item_flipper_source.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/flipper/src/main/res/values/strings.xml b/plugins/flipper/src/main/res/values/strings.xml
index b81074df..bfb7a1aa 100644
--- a/plugins/flipper/src/main/res/values/strings.xml
+++ b/plugins/flipper/src/main/res/values/strings.xml
@@ -1,8 +1,10 @@
Сбросить все тоглы на состояние по-умолчанию?
- Выбрать источник
- Сбросить изменения
+ Switch source
+ Reset
Update
value
+ Filter
+ Cancel
diff --git a/plugins/servers/build.gradle.kts b/plugins/servers/build.gradle.kts
index 4bc6bb47..5a9fb268 100644
--- a/plugins/servers/build.gradle.kts
+++ b/plugins/servers/build.gradle.kts
@@ -37,7 +37,11 @@ android {
}
buildFeatures {
- viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
namespace = "com.redmadrobot.debug.plugin.servers"
}
@@ -50,5 +54,6 @@ dependencies {
implementation(project(":core"))
implementation(project(":common"))
implementation(kotlin("stdlib"))
+ implementation(stack.kotlinx.serialization.json)
kapt(androidx.room.compiler)
}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServerSelectedEvent.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServerSelectedEvent.kt
index a35d0527..a99d706c 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServerSelectedEvent.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServerSelectedEvent.kt
@@ -2,5 +2,8 @@ package com.redmadrobot.debug.plugin.servers
import com.redmadrobot.debug.core.DebugEvent
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
public data class ServerSelectedEvent(val debugServer: DebugServer) : DebugEvent
+
+public data class StageSelectedEvent(val debugServer: DebugStage) : DebugEvent
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPlugin.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPlugin.kt
index d7c9312c..d60bc0d2 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPlugin.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPlugin.kt
@@ -1,23 +1,26 @@
package com.redmadrobot.debug.plugin.servers
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import com.redmadrobot.debug.core.internal.CommonContainer
+import androidx.compose.runtime.Composable
+import com.redmadrobot.debug.core.annotation.DebugPanelInternal
import com.redmadrobot.debug.core.data.DebugDataProvider
import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.debug.core.plugin.Plugin
+import com.redmadrobot.debug.core.internal.CommonContainer
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
+import com.redmadrobot.debug.core.plugin.Plugin
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
-import com.redmadrobot.debug.plugin.servers.ui.ServersFragment
+import com.redmadrobot.debug.plugin.servers.data.model.DebugServerData
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
+import com.redmadrobot.debug.plugin.servers.ui.ServersScreen
import kotlinx.coroutines.runBlocking
+@OptIn(DebugPanelInternal::class)
public class ServersPlugin(
- private val preInstalledServers: List = emptyList()
+ private val preInstalledServers: List = emptyList(),
) : Plugin() {
init {
preInstalledServers.find { it.isDefault }
- ?: throw IllegalStateException("DebugPanel - ServersPlugin can't be initialized. At least one server must be default")
+ ?: error("ServersPlugin can't be initialized. At least one server must be default")
}
public companion object {
@@ -25,18 +28,20 @@ public class ServersPlugin(
public fun getSelectedServer(): DebugServer {
return runBlocking {
- getPlugin()
- .getContainer()
- .serversRepository
- .getSelectedServer()
+ getPlugin().getContainer().serversRepository.getSelectedServer()
}
}
public fun getDefaultServer(): DebugServer {
- return getPlugin()
- .getContainer()
- .serversRepository
- .getDefault()
+ return getPlugin().getContainer().serversRepository.getDefault()
+ }
+
+ public fun getSelectedStage(): DebugStage {
+ return getPlugin().getContainer().stagesRepository.getSelectedStage()
+ }
+
+ public fun getDefaultStage(): DebugStage {
+ return getPlugin().getContainer().stagesRepository.getDefault()
}
}
@@ -50,15 +55,13 @@ public class ServersPlugin(
return ServersPluginContainer(preInstalledServers, commonContainer)
}
- override fun getFragment(): Fragment {
- return ServersFragment().apply {
- arguments = bundleOf(ServersFragment.IS_EDIT_MODE_KEY to false)
- }
+ @Composable
+ override fun content() {
+ ServersScreen(isEditMode = false)
}
- override fun getSettingFragment(): Fragment {
- return ServersFragment().apply {
- arguments = bundleOf(ServersFragment.IS_EDIT_MODE_KEY to true)
- }
+ @Composable
+ override fun settingsContent() {
+ ServersScreen(isEditMode = true)
}
}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPluginContainer.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPluginContainer.kt
index 781a120c..80b7228f 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPluginContainer.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPluginContainer.kt
@@ -2,30 +2,41 @@ package com.redmadrobot.debug.plugin.servers
import com.redmadrobot.debug.core.internal.CommonContainer
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
-import com.redmadrobot.debug.plugin.servers.data.LocalDebugServerRepository
+import com.redmadrobot.debug.plugin.servers.data.DebugServerRepository
+import com.redmadrobot.debug.plugin.servers.data.DebugStageRepository
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugServerData
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
import com.redmadrobot.debug.plugin.servers.data.storage.ServersPluginDatabase
import com.redmadrobot.debug.plugin.servers.ui.ServersViewModel
internal class ServersPluginContainer(
- private val preInstalledServers: List,
+ private val preinstalledStages: List,
private val container: CommonContainer
) : PluginDependencyContainer {
private val pluginStorage by lazy { ServersPluginDatabase.getInstance(container.context) }
val serversRepository by lazy {
- LocalDebugServerRepository(
+ DebugServerRepository(
container.context,
pluginStorage.getDebugServersDao(),
- preInstalledServers
+ preinstalledStages.filterIsInstance(),
+ )
+ }
+
+ val stagesRepository by lazy {
+ DebugStageRepository(
+ container.context,
+ pluginStorage.getDebugStagesDao(),
+ preinstalledStages.filterIsInstance()
)
}
fun createServersViewModel(): ServersViewModel {
return ServersViewModel(
- container.context,
- serversRepository
+ serversRepository,
+ stagesRepository
)
}
}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/DebugServerRepository.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/DebugServerRepository.kt
index ec3a0aa2..6c6f6376 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/DebugServerRepository.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/DebugServerRepository.kt
@@ -1,23 +1,77 @@
package com.redmadrobot.debug.plugin.servers.data
+import android.content.Context
+import androidx.core.content.edit
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.storage.DebugServersDao
+import com.redmadrobot.debug.plugin.servers.data.storage.SharedPreferencesProvider
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
-internal interface DebugServerRepository {
+internal class DebugServerRepository(
+ private val context: Context,
+ private val debugServersDao: DebugServersDao,
+ private val preInstalledServers: List
+) {
- fun getPreInstalledServers(): List
+ companion object {
+ private const val SELECTED_SERVER_URL = "SELECTED_SERVER_URL"
+ private const val SELECTED_SERVER_NAME = "SELECTED_SERVER_NAME"
+ }
- fun saveSelectedServer(selectedServer: DebugServer)
+ private val sharedPreferences by lazy {
+ SharedPreferencesProvider.get(context)
+ }
+ fun getPreInstalledServers(): List {
+ return preInstalledServers
+ }
- fun getDefault(): DebugServer
+ fun saveSelectedServer(selectedServer: DebugServer) {
+ sharedPreferences.edit {
+ putString(SELECTED_SERVER_NAME, selectedServer.name)
+ putString(SELECTED_SERVER_URL, selectedServer.url)
+ }
+ }
- suspend fun getSelectedServer(): DebugServer
+ suspend fun getSelectedServer(): DebugServer {
+ val serverName = sharedPreferences.getString(SELECTED_SERVER_NAME, null)
+ val serverUrl = sharedPreferences.getString(SELECTED_SERVER_URL, null)
- suspend fun getServers(): List
+ return if (serverName != null && serverUrl != null) {
+ preInstalledServers.find { it.name == serverName && it.url == serverUrl }
+ ?: debugServersDao.getServer(serverName, serverUrl)
+ ?: getDefault()
+ } else {
+ getDefault()
+ }
+ }
- suspend fun addServer(server: DebugServer)
+ fun getDefault(): DebugServer {
+ return preInstalledServers.first { it.isDefault }
+ }
- suspend fun removeServer(server: DebugServer)
+ suspend fun addServer(server: DebugServer) {
+ withContext(Dispatchers.IO) {
+ debugServersDao.insert(server)
+ }
+ }
- suspend fun updateServer(server: DebugServer)
+ suspend fun getServers(): List {
+ return withContext(Dispatchers.IO) {
+ debugServersDao.getAll()
+ }
+ }
+
+ suspend fun removeServer(server: DebugServer) {
+ withContext(Dispatchers.IO) {
+ debugServersDao.remove(server)
+ }
+ }
+
+ suspend fun updateServer(server: DebugServer) {
+ withContext(Dispatchers.IO) {
+ debugServersDao.update(server)
+ }
+ }
}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/DebugStageRepository.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/DebugStageRepository.kt
new file mode 100644
index 00000000..20161c41
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/DebugStageRepository.kt
@@ -0,0 +1,66 @@
+package com.redmadrobot.debug.plugin.servers.data
+
+import android.content.Context
+import androidx.core.content.edit
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
+import com.redmadrobot.debug.plugin.servers.data.storage.DebugStagesDao
+import com.redmadrobot.debug.plugin.servers.data.storage.SharedPreferencesProvider
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+internal class DebugStageRepository(
+ private val context: Context,
+ private val debugStagesDao: DebugStagesDao,
+ private val preInstalledStages: List,
+) {
+
+ companion object {
+ private const val SELECTED_STAGE_HOSTS_HASH = "SELECTED_STAGE_HOSTS_HASH"
+ private const val SELECTED_STAGE_NAME = "SELECTED_STAGE_NAME"
+ }
+
+ private val sharedPreferences by lazy {
+ SharedPreferencesProvider.get(context)
+ }
+
+ fun getPreInstalledStages(): List {
+ return preInstalledStages
+ }
+
+ fun saveSelectedStage(selectedStage: DebugStage) {
+ sharedPreferences.edit {
+ putString(SELECTED_STAGE_NAME, selectedStage.name)
+ putInt(SELECTED_STAGE_HOSTS_HASH, selectedStage.hosts.hashCode())
+ }
+ }
+
+ fun getSelectedStage(): DebugStage {
+ val stageName = sharedPreferences.getString(SELECTED_STAGE_NAME, null)
+ val hostsHash = sharedPreferences.getInt(SELECTED_STAGE_HOSTS_HASH, -1)
+
+ return if (stageName != null) {
+ preInstalledStages
+ .find { it.name == stageName && it.hosts.hashCode() == hostsHash }
+ ?: debugStagesDao.getStage(stageName)
+ ?: getDefault()
+ } else {
+ getDefault()
+ }
+ }
+
+ fun getDefault(): DebugStage {
+ return preInstalledStages.first { it.isDefault }
+ }
+
+ suspend fun getStages(): List {
+ return withContext(Dispatchers.IO) {
+ debugStagesDao.getAll()
+ }
+ }
+
+ suspend fun updateStage(server: DebugStage) {
+ withContext(Dispatchers.IO) {
+ debugStagesDao.update(server)
+ }
+ }
+}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/LocalDebugServerRepository.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/LocalDebugServerRepository.kt
deleted file mode 100644
index bce6553b..00000000
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/LocalDebugServerRepository.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.redmadrobot.debug.plugin.servers.data
-
-import android.content.Context
-import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
-import com.redmadrobot.debug.plugin.servers.data.storage.DebugServersDao
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-
-internal class LocalDebugServerRepository(
- private val context: Context,
- private val debugServersDao: DebugServersDao,
- private val preInstalledServers: List
-) : DebugServerRepository {
-
- companion object {
- private const val NAME = ":servers"
- private const val SELECTED_SERVER_URL = "SELECTED_SERVER_URL"
- private const val SELECTED_SERVER_NAME = "SELECTED_SERVER_NAME"
- }
-
-
- private val sharedPreferences by lazy {
- val prefFileName = "${context.packageName}$NAME"
- context.getSharedPreferences(prefFileName, 0)
- }
-
-
- override fun getPreInstalledServers(): List {
- return preInstalledServers
- }
-
- override fun saveSelectedServer(selectedServer: DebugServer) {
- sharedPreferences.edit().apply {
- putString(SELECTED_SERVER_NAME, selectedServer.name)
- putString(SELECTED_SERVER_URL, selectedServer.url)
- }.apply()
- }
-
- override suspend fun getSelectedServer(): DebugServer {
- val serverName = sharedPreferences.getString(SELECTED_SERVER_NAME, null)
- val serverUrl = sharedPreferences.getString(SELECTED_SERVER_URL, null)
-
- return if (serverName != null && serverUrl != null) {
- preInstalledServers.find { it.name == serverName && it.url == serverUrl }
- ?: debugServersDao.getServer(serverName, serverUrl)
- ?: getDefault()
- } else {
- getDefault()
- }
- }
-
- override fun getDefault(): DebugServer {
- return preInstalledServers.first { it.isDefault }
- }
-
- override suspend fun addServer(server: DebugServer) {
- withContext(Dispatchers.IO) {
- debugServersDao.insert(server)
- }
- }
-
- override suspend fun getServers(): List {
- return withContext(Dispatchers.IO) {
- debugServersDao.getAll()
- }
- }
-
- override suspend fun removeServer(server: DebugServer) {
- withContext(Dispatchers.IO) {
- debugServersDao.remove(server)
- }
- }
-
- override suspend fun updateServer(server: DebugServer) {
- withContext(Dispatchers.IO) {
- debugServersDao.update(server)
- }
- }
-}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugServer.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugServer.kt
index 2a6d4972..554c77ec 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugServer.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugServer.kt
@@ -6,11 +6,11 @@ import androidx.room.PrimaryKey
@Entity(tableName = DebugServer.TABLE_NAME)
public data class DebugServer(
@PrimaryKey(autoGenerate = true)
- val id: Int = 0,
- val name: String,
+ override val id: Int = 0,
+ override val name: String,
val url: String,
- val isDefault: Boolean = false
-) {
+ override val isDefault: Boolean = false
+) : DebugServerData {
internal companion object {
const val TABLE_NAME = "debug_server"
}
@@ -19,5 +19,4 @@ public data class DebugServer(
val otherServer = other as DebugServer
return this.name == otherServer.name && this.url == otherServer.url
}
-
}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugServerData.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugServerData.kt
new file mode 100644
index 00000000..88cd8193
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugServerData.kt
@@ -0,0 +1,10 @@
+package com.redmadrobot.debug.plugin.servers.data.model
+
+import com.redmadrobot.debug.core.annotation.DebugPanelInternal
+
+@DebugPanelInternal
+public interface DebugServerData {
+ public val id: Int
+ public val name: String
+ public val isDefault: Boolean
+}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugStage.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugStage.kt
new file mode 100644
index 00000000..d8335758
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/model/DebugStage.kt
@@ -0,0 +1,23 @@
+package com.redmadrobot.debug.plugin.servers.data.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = DebugStage.TABLE_NAME)
+public data class DebugStage(
+ @PrimaryKey(autoGenerate = true)
+ override val id: Int = 0,
+ override val name: String,
+ val hosts: Map,
+ override val isDefault: Boolean = false
+) : DebugServerData {
+
+ internal companion object {
+ const val TABLE_NAME = "debug_stage"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ val otherServer = other as DebugStage
+ return this.hosts.size == otherServer.hosts.size && this.hosts == otherServer.hosts
+ }
+}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/DbConverters.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/DbConverters.kt
new file mode 100644
index 00000000..9cd85265
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/DbConverters.kt
@@ -0,0 +1,20 @@
+package com.redmadrobot.debug.plugin.servers.data.storage
+
+import androidx.room.TypeConverter
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+
+
+internal class DbConverters {
+
+ @TypeConverter
+ fun fromMap(map: Map?): String {
+ return Json.encodeToString(map ?: emptyMap())
+ }
+
+ @TypeConverter
+ fun toMap(data: String?): Map {
+ return data?.let { Json.decodeFromString(data) } ?: emptyMap()
+ }
+}
\ No newline at end of file
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/DebugStagesDao.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/DebugStagesDao.kt
new file mode 100644
index 00000000..417e02af
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/DebugStagesDao.kt
@@ -0,0 +1,23 @@
+package com.redmadrobot.debug.plugin.servers.data.storage
+
+import androidx.room.*
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
+
+@Dao
+internal interface DebugStagesDao {
+
+ @Query("SELECT * FROM ${DebugStage.TABLE_NAME}")
+ suspend fun getAll(): List
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insert(server: DebugStage)
+
+ @Delete
+ suspend fun remove(server: DebugStage)
+
+ @Update
+ suspend fun update(server: DebugStage)
+
+ @Query("SELECT * FROM ${DebugStage.TABLE_NAME} WHERE name = :name")
+ fun getStage(name: String): DebugStage?
+}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/ServersPluginDatabase.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/ServersPluginDatabase.kt
index 0094d5d4..dc168d83 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/ServersPluginDatabase.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/ServersPluginDatabase.kt
@@ -4,15 +4,20 @@ import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
@Database(
- entities = [DebugServer::class],
- version = 1
+ entities = [DebugServer::class, DebugStage::class],
+ version = 2
)
+@TypeConverters(DbConverters::class)
internal abstract class ServersPluginDatabase : RoomDatabase() {
abstract fun getDebugServersDao(): DebugServersDao
+ abstract fun getDebugStagesDao(): DebugStagesDao
+
companion object {
private const val DATABASE_NAME = "servers_plugin_db"
@@ -21,7 +26,10 @@ internal abstract class ServersPluginDatabase : RoomDatabase() {
context.applicationContext,
ServersPluginDatabase::class.java,
DATABASE_NAME
- ).build()
+ )
+ .fallbackToDestructiveMigration()
+ .allowMainThreadQueries()
+ .build()
}
}
}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/SharedPreferencesFactory.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/SharedPreferencesFactory.kt
new file mode 100644
index 00000000..22f09b88
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/data/storage/SharedPreferencesFactory.kt
@@ -0,0 +1,13 @@
+package com.redmadrobot.debug.plugin.servers.data.storage
+
+import android.content.Context
+import android.content.SharedPreferences
+
+internal object SharedPreferencesProvider {
+ private const val NAME = ":servers"
+
+ fun get(context: Context): SharedPreferences {
+ val prefFileName = "${context.packageName}$NAME"
+ return context.getSharedPreferences(prefFileName, 0)
+ }
+}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/util/DebugServerInterceptor.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/interceptor/DebugServerInterceptor.kt
similarity index 93%
rename from plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/util/DebugServerInterceptor.kt
rename to plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/interceptor/DebugServerInterceptor.kt
index a9d2fec5..dd6e8a77 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/util/DebugServerInterceptor.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/interceptor/DebugServerInterceptor.kt
@@ -1,4 +1,4 @@
-package com.redmadrobot.debug.plugin.servers.util
+package com.redmadrobot.debug.plugin.servers.interceptor
import com.redmadrobot.debug.core.DebugPanel
import com.redmadrobot.debug.core.extension.getPlugin
@@ -23,7 +23,7 @@ public class DebugServerInterceptor : Interceptor {
}
/**
- * Дополнительная Модификация запроса
+ * Additional request modification
* */
public fun modifyRequest(block: (Request, DebugServer) -> Request): DebugServerInterceptor {
this.requestModifier = block
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/interceptor/DebugStageInterceptor.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/interceptor/DebugStageInterceptor.kt
new file mode 100644
index 00000000..36ec20e8
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/interceptor/DebugStageInterceptor.kt
@@ -0,0 +1,57 @@
+package com.redmadrobot.debug.plugin.servers.interceptor
+
+import com.redmadrobot.debug.core.DebugPanel
+import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.plugin.servers.ServersPlugin
+import com.redmadrobot.debug.plugin.servers.ServersPluginContainer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
+import okhttp3.HttpUrl
+import okhttp3.Interceptor
+import okhttp3.Request
+import okhttp3.Response
+import java.net.URI
+
+public class DebugStageInterceptor(private val hostName: String) : Interceptor {
+
+ private var requestModifier: ((Request, DebugStage) -> Request?)? = null
+
+ private val stageRepository by lazy {
+ getPlugin()
+ .getContainer()
+ .stagesRepository
+ }
+
+ /**
+ * Additional request modification
+ * */
+ public fun modifyRequest(block: (Request, DebugStage) -> Request): DebugStageInterceptor {
+ this.requestModifier = block
+ return this
+ }
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ var request = chain.request()
+ return if (DebugPanel.isInitialized) {
+ val debugStage = stageRepository.getSelectedStage()
+ val host = debugStage.hosts[hostName]
+ if (host != null) {
+ val newUrl = request.getNewUrl(host)
+ request = request.newBuilder()
+ .url(newUrl)
+ .build()
+ }
+ chain.proceed(requestModifier?.invoke(request, debugStage) ?: request)
+ } else {
+ chain.proceed(request)
+ }
+ }
+
+
+ private fun Request.getNewUrl(debugServer: String): HttpUrl {
+ val serverUri = URI(debugServer)
+ return this.url.newBuilder()
+ .scheme(serverUri.scheme)
+ .host(serverUri.host)
+ .build()
+ }
+}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/listener/DefaultOnServerChangedListener.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/listener/DefaultOnServerChangedListener.kt
deleted file mode 100644
index fc438885..00000000
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/listener/DefaultOnServerChangedListener.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.redmadrobot.debug.plugin.servers.listener
-
-import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
-
-internal class DefaultOnServerChangedListener : OnServerChangedListener {
- override fun onChanged(server: DebugServer?) {/*do nothing*/}
-}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/listener/OnServerChangedListener.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/listener/OnServerChangedListener.kt
deleted file mode 100644
index b1dca5e6..00000000
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/listener/OnServerChangedListener.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.redmadrobot.debug.plugin.servers.listener
-
-import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
-
-internal interface OnServerChangedListener {
- fun onChanged(server: DebugServer?)
-}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServerHostDialog.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServerHostDialog.kt
deleted file mode 100644
index 2700f823..00000000
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServerHostDialog.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.redmadrobot.debug.plugin.servers.ui
-
-import android.os.Bundle
-import android.util.Patterns
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.LinearLayout
-import androidx.fragment.app.DialogFragment
-import androidx.fragment.app.FragmentManager
-import com.redmadrobot.debug.common.extension.obtainShareViewModel
-import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.debug.plugin.servers.R
-import com.redmadrobot.debug.plugin.servers.databinding.DialogServerBinding
-import com.redmadrobot.debug.plugin.servers.ServersPlugin
-import com.redmadrobot.debug.plugin.servers.ServersPluginContainer
-
-internal class ServerHostDialog : DialogFragment() {
-
- companion object {
- const val KEY_NAME = "NAME"
- const val KEY_URL = "URL"
- const val KEY_ID = "ID"
- private const val TAG = "AddServerDialog"
-
- fun show(fragmentManager: FragmentManager, params: Bundle? = null) {
- ServerHostDialog().apply {
- arguments = params
- }.show(fragmentManager, TAG)
- }
- }
-
- private var _binding: DialogServerBinding? = null
- private val binding get() = checkNotNull(_binding)
-
- private val shareViewModel by lazy {
- obtainShareViewModel {
- getPlugin()
- .getContainer()
- .createServersViewModel()
- }
- }
-
- private var isEditMode = false
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- _binding = DialogServerBinding.inflate(inflater, container, false)
- return binding.root
- }
-
- override fun onStart() {
- super.onStart()
- dialog?.window?.setLayout(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- )
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- binding.setViews()
- obtainArguments()
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- private fun obtainArguments() {
- val name = arguments?.getString(KEY_NAME)
- val url = arguments?.getString(KEY_URL)
- if (!name.isNullOrEmpty() && !url.isNullOrEmpty()) {
- binding.serverName.setText(name)
- binding.serverHost.setText(url)
- isEditMode = true
- }
- }
-
- private fun DialogServerBinding.setViews() {
- saveServerButton.setOnClickListener {
- val name = serverName.text.toString()
- val url = serverHost.text.toString()
- if (isDataValid(name, url)) {
- save(name, url)
- } else {
- showWrongHostError()
- }
- }
- serverName.requestFocus()
- }
-
- private fun save(name: String, url: String) {
- if (isEditMode) {
- update(name, url)
- } else {
- saveNew(name, url)
- }
- }
-
- private fun update(name: String, url: String) {
- val id = arguments?.getInt(KEY_ID)
- id?.let {
- shareViewModel.updateServerData(id, name, url)
- }
- dialog?.dismiss()
- }
-
- private fun saveNew(name: String, url: String) {
- shareViewModel.addServer(name, url)
- dialog?.dismiss()
- }
-
- private fun isDataValid(name: String, url: String): Boolean {
- return when {
- name.isEmpty() -> {
- showEmptyNameError()
- false
- }
- !Patterns.WEB_URL.matcher(url).matches() -> {
- showWrongHostError()
- false
- }
- else -> {
- true
- }
- }
- }
-
- private fun showEmptyNameError() {
- binding.serverNameInputLayout.error = getString(R.string.error_empty_name)
- }
-
- private fun showWrongHostError() {
- binding.serverNameInputLayout.error = getString(R.string.error_wrong_host)
- }
-}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersFragment.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersFragment.kt
deleted file mode 100644
index 18abd101..00000000
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersFragment.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.redmadrobot.debug.plugin.servers.ui
-
-import android.os.Bundle
-import android.view.View
-import androidx.core.view.isVisible
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.redmadrobot.debug.common.base.PluginFragment
-import com.redmadrobot.debug.common.extension.observe
-import com.redmadrobot.debug.common.extension.obtainShareViewModel
-import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.debug.panel.common.databinding.ItemSectionHeaderBinding
-import com.redmadrobot.debug.plugin.servers.R
-import com.redmadrobot.itemsadapter.ItemsAdapter
-import com.redmadrobot.itemsadapter.itemsAdapter
-import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
-import com.redmadrobot.debug.plugin.servers.databinding.FragmentServersBinding
-import com.redmadrobot.debug.plugin.servers.databinding.ItemDebugServerBinding
-import com.redmadrobot.debug.plugin.servers.ServersPlugin
-import com.redmadrobot.debug.plugin.servers.ServersPluginContainer
-import com.redmadrobot.debug.plugin.servers.ui.item.DebugServerItems
-import com.redmadrobot.itemsadapter.bind
-import com.redmadrobot.debug.panel.common.R as CommonR
-
-internal class ServersFragment : PluginFragment(R.layout.fragment_servers) {
-
- companion object {
- const val IS_EDIT_MODE_KEY = "IS_EDIT_MODE_KEY"
- }
-
- private var _binding: FragmentServersBinding? = null
- private val binding get() = checkNotNull(_binding)
-
- private val isEditMode by lazy {
- requireNotNull(arguments).getBoolean(IS_EDIT_MODE_KEY)
- }
-
- private val viewModel by lazy {
- obtainShareViewModel {
- getPlugin()
- .getContainer()
- .createServersViewModel()
- }
- }
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
- observe(viewModel.state, ::render)
- viewModel.loadServers()
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- _binding = FragmentServersBinding.bind(view)
- binding.setViews()
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-
- private fun FragmentServersBinding.setViews() {
- serverList.layoutManager = LinearLayoutManager(requireContext())
- addServer.setOnClickListener {
- ServerHostDialog.show(childFragmentManager)
- }
- addServer.isVisible = isEditMode
- }
-
- private fun render(state: ServersViewState) {
- val adapter = createAdapterByState(state)
- binding.serverList.adapter = adapter
- }
-
- private fun createAdapterByState(state: ServersViewState): ItemsAdapter {
- val items = state.preInstalledServers.plus(state.addedServers)
- return itemsAdapter(items) { item ->
- when (item) {
- is DebugServerItems.Header -> {
- bind(CommonR.layout.item_section_header) {
- itemSectionTitle.text = item.header
- }
- }
- is DebugServerItems.PreinstalledServer -> {
- bind(R.layout.item_debug_server) {
- itemServerName.text = item.debugServer.name
- isSelectedIcon.isVisible = item.isSelected && !isEditMode
- if (!isEditMode) {
- root.setOnClickListener {
- viewModel.onServerSelected(item.debugServer)
- }
- }
- }
- }
- is DebugServerItems.AddedServer -> {
- bind(R.layout.item_debug_server) {
- itemServerName.text = item.debugServer.name
- isSelectedIcon.isVisible = item.isSelected && !isEditMode
- itemServerDelete.isVisible = isEditMode
- val server = item.debugServer
- itemServerDelete.setOnClickListener { viewModel.removeServer(server) }
- root.setOnClickListener {
- if (!isEditMode) {
- viewModel.onServerSelected(server)
- } else {
- editServerData(server)
- }
- }
- }
- }
- }
- }
- }
-
- private fun editServerData(debugServer: DebugServer) {
- val bundle = Bundle().apply {
- putInt(ServerHostDialog.KEY_ID, debugServer.id)
- putString(ServerHostDialog.KEY_NAME, debugServer.name)
- putString(ServerHostDialog.KEY_URL, debugServer.url)
- }
- ServerHostDialog.show(childFragmentManager, bundle)
- }
-}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersScreen.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersScreen.kt
new file mode 100644
index 00000000..f90ae23a
--- /dev/null
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersScreen.kt
@@ -0,0 +1,356 @@
+package com.redmadrobot.debug.plugin.servers.ui
+
+import android.annotation.SuppressLint
+import androidx.annotation.StringRes
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Button
+import androidx.compose.material.Card
+import androidx.compose.material.ExtendedFloatingActionButton
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Scaffold
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
+import androidx.lifecycle.Lifecycle
+import com.redmadrobot.debug.core.extension.OnLifecycleEvent
+import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.core.extension.provideViewModel
+import com.redmadrobot.debug.plugin.servers.R
+import com.redmadrobot.debug.plugin.servers.ServersPlugin
+import com.redmadrobot.debug.plugin.servers.ServersPluginContainer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
+
+@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
+@Composable
+internal fun ServersScreen(
+ viewModel: ServersViewModel = provideViewModel {
+ getPlugin()
+ .getContainer()
+ .createServersViewModel()
+ },
+ isEditMode: Boolean
+) {
+ val state by viewModel.state.collectAsState()
+
+ Scaffold(
+ floatingActionButton = {
+ if (isEditMode) {
+ ExtendedFloatingActionButton(
+ onClick = { viewModel.onAddClicked() },
+ text = { Text("Add") },
+ icon = {
+ Icon(
+ painterResource(R.drawable.icon_add_server),
+ contentDescription = null
+ )
+ }
+ )
+ }
+ }
+ ) {
+ ServersScreenLayout(
+ state = state,
+ isEditMode = isEditMode,
+ onServerClick = if (!isEditMode) viewModel::onServerClicked else viewModel::onEditServerClicked,
+ onStageClick = viewModel::onStageClicked,
+ onServerDeleteClick = viewModel::onRemoveServerClicked
+ )
+ }
+ if (state.serverDialogState.show) {
+ ServerDialog(
+ state = state.serverDialogState,
+ onNameChange = viewModel::onServerNameChanged,
+ onUrlChange = viewModel::onServerUrlChanged,
+ onDismiss = viewModel::dismissDialogs,
+ onSaveClick = viewModel::onSaveServerClicked,
+ )
+ }
+ OnLifecycleEvent { event ->
+ if (event == Lifecycle.Event.ON_RESUME) {
+ viewModel.loadServers()
+ }
+ }
+}
+
+@Composable
+private fun ServersScreenLayout(
+ state: ServersViewState,
+ isEditMode: Boolean,
+ onServerClick: (DebugServer) -> Unit,
+ onServerDeleteClick: (DebugServer) -> Unit,
+ onStageClick: (DebugStage) -> Unit,
+) {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ contentPadding = PaddingValues(start = 16.dp, end = 16.dp, bottom = 64.dp),
+ ) {
+ StageItems(
+ items = state.preInstalledStages,
+ titleRes = R.string.pre_installed_stages,
+ isSelectable = !isEditMode,
+ onItemClick = onStageClick.takeIf { !isEditMode },
+ )
+ ServerItems(
+ items = state.preInstalledServers,
+ titleRes = R.string.pre_installed_servers,
+ isSelectable = !isEditMode,
+ showDelete = false,
+ onItemClick = onServerClick.takeIf { !isEditMode },
+ onDeleteClick = onServerDeleteClick,
+ )
+ StageItems(
+ items = state.addedStages,
+ titleRes = R.string.added_stages,
+ isSelectable = !isEditMode,
+ onItemClick = onStageClick,
+ )
+ ServerItems(
+ items = state.addedServers,
+ titleRes = R.string.added_servers,
+ isSelectable = !isEditMode,
+ showDelete = isEditMode,
+ onItemClick = onServerClick,
+ onDeleteClick = onServerDeleteClick,
+ )
+ }
+}
+
+private fun LazyListScope.ServerItems(
+ items: List,
+ @StringRes titleRes: Int,
+ isSelectable: Boolean,
+ showDelete: Boolean,
+ onDeleteClick: (DebugServer) -> Unit,
+ onItemClick: ((DebugServer) -> Unit)? = null,
+) {
+ if (items.isEmpty()) return
+
+ TitleItem(titleRes)
+ items(items) { item ->
+ ServerItem(
+ server = item.server,
+ selected = item.isSelected && isSelectable,
+ showDelete = showDelete,
+ onItemClick = onItemClick,
+ onDeleteClick = onDeleteClick,
+ )
+ }
+}
+
+private fun LazyListScope.StageItems(
+ items: List,
+ @StringRes titleRes: Int,
+ isSelectable: Boolean,
+ onItemClick: ((DebugStage) -> Unit)? = null,
+) {
+ if (items.isEmpty()) return
+
+ TitleItem(titleRes)
+ items(items) { item ->
+ StageItem(
+ stage = item.server,
+ selected = item.isSelected && isSelectable,
+ onItemClick = onItemClick,
+ )
+ }
+}
+
+private fun LazyListScope.TitleItem(@StringRes titleRes: Int) {
+ item {
+ Text(
+ text = stringResource(id = titleRes).uppercase(),
+ modifier = Modifier
+ .fillParentMaxWidth()
+ .padding(vertical = 16.dp),
+ fontSize = 16.sp,
+ )
+ }
+}
+
+@Composable
+private fun ServerItem(
+ server: DebugServer,
+ selected: Boolean,
+ showDelete: Boolean,
+ onDeleteClick: (DebugServer) -> Unit,
+ onItemClick: ((DebugServer) -> Unit)? = null,
+) {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .defaultMinSize(minHeight = 56.dp)
+ .clickable { onItemClick?.invoke(server) }
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ horizontalArrangement = Arrangement.spacedBy(32.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.icon_router),
+ contentDescription = null,
+ tint = MaterialTheme.colors.primary
+ )
+ Text(server.name, fontWeight = FontWeight.SemiBold)
+ Box(modifier = Modifier.weight(1f)) {
+ if (selected) {
+ Icon(
+ painterResource(R.drawable.icon_selected),
+ contentDescription = null,
+ modifier = Modifier.align(Alignment.CenterEnd)
+ )
+ } else if (showDelete) {
+ IconButton(
+ modifier = Modifier.align(Alignment.CenterEnd),
+ onClick = { onDeleteClick(server) },
+ ) {
+ Icon(
+ painterResource(R.drawable.icon_delete),
+ contentDescription = null,
+ tint = Color.Red
+ )
+ }
+ }
+ }
+ }
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(text = "url: ${server.url}", color = Color.Gray, fontSize = 12.sp)
+ }
+ }
+}
+
+@Composable
+private fun StageItem(
+ stage: DebugStage,
+ selected: Boolean,
+ onItemClick: ((DebugStage) -> Unit)? = null,
+) {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .defaultMinSize(minHeight = 56.dp)
+ .clickable { onItemClick?.invoke(stage) }
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ horizontalArrangement = Arrangement.spacedBy(32.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(R.drawable.icon_stage),
+ contentDescription = null,
+ tint = MaterialTheme.colors.primary
+ )
+ Text(stage.name, fontWeight = FontWeight.SemiBold)
+ Box(modifier = Modifier.weight(1f)) {
+ if (selected) {
+ Icon(
+ painterResource(R.drawable.icon_selected),
+ contentDescription = null,
+ modifier = Modifier.align(Alignment.CenterEnd)
+ )
+ }
+ }
+ }
+ Spacer(modifier = Modifier.height(8.dp))
+ Column {
+ stage.hosts.forEach { (key, value) ->
+ Text(text = "$key: $value", color = Color.Gray, fontSize = 12.sp)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ServerDialog(
+ state: ServerDialogState,
+ onNameChange: (String) -> Unit,
+ onUrlChange: (String) -> Unit,
+ onDismiss: () -> Unit,
+ onSaveClick: () -> Unit,
+) {
+ Dialog(
+ onDismissRequest = onDismiss,
+ properties = DialogProperties(usePlatformDefaultWidth = false)
+ ) {
+ Surface(
+ shape = RoundedCornerShape(16.dp),
+ color = Color.White,
+ modifier = Modifier.wrapContentHeight()
+ ) {
+ Column(
+ modifier = Modifier.padding(16.dp)
+ ) {
+ OutlinedTextField(
+ value = state.serverName,
+ onValueChange = onNameChange,
+ label = { Text(stringResource(R.string.name)) },
+ isError = state.inputErrors?.nameError != null
+ )
+ if (state.inputErrors?.nameError != null) {
+ Text(text = stringResource(id = state.inputErrors.nameError), color = Color.Red)
+ }
+ Spacer(modifier = Modifier.height(16.dp))
+ OutlinedTextField(
+ value = state.serverUrl,
+ onValueChange = onUrlChange,
+ label = { Text(stringResource(R.string.server_host_hint)) },
+ isError = state.inputErrors?.urlError != null
+ )
+ if (state.inputErrors?.urlError != null) {
+ Text(text = stringResource(id = state.inputErrors.urlError), color = Color.Red)
+ }
+ Button(
+ onClick = onSaveClick,
+ modifier = Modifier.align(Alignment.End)
+ ) {
+ Text(stringResource(R.string.save_server).uppercase())
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewModel.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewModel.kt
index 590ff1a1..bd49eab3 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewModel.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewModel.kt
@@ -1,129 +1,204 @@
package com.redmadrobot.debug.plugin.servers.ui
-import android.content.Context
-import androidx.lifecycle.MutableLiveData
+import android.util.Patterns
import androidx.lifecycle.viewModelScope
-import com.redmadrobot.debug.common.base.PluginViewModel
-import com.redmadrobot.debug.common.extension.safeLaunch
import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.core.extension.safeLaunch
+import com.redmadrobot.debug.core.internal.PluginViewModel
import com.redmadrobot.debug.plugin.servers.R
-import com.redmadrobot.debug.plugin.servers.data.DebugServerRepository
-import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
import com.redmadrobot.debug.plugin.servers.ServerSelectedEvent
import com.redmadrobot.debug.plugin.servers.ServersPlugin
-import com.redmadrobot.debug.plugin.servers.ui.item.DebugServerItems
+import com.redmadrobot.debug.plugin.servers.StageSelectedEvent
+import com.redmadrobot.debug.plugin.servers.data.DebugServerRepository
+import com.redmadrobot.debug.plugin.servers.data.DebugStageRepository
+import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
internal class ServersViewModel(
- private val context: Context,
- private val serversRepository: DebugServerRepository
+ private val serversRepository: DebugServerRepository,
+ private val stagesRepository: DebugStageRepository
) : PluginViewModel() {
- val state = MutableLiveData().apply {
- /*Default state*/
- value = ServersViewState(
- preInstalledServers = emptyList(),
- addedServers = emptyList()
- )
- }
+ private val _state = MutableStateFlow(ServersViewState())
+ val state: StateFlow = _state.asStateFlow()
fun loadServers() {
- viewModelScope.safeLaunch {
- withContext(Dispatchers.IO) {
- loadPreInstalledServers()
- loadAddedServers()
+ viewModelScope.launch(Dispatchers.IO) {
+ _state.update { serversState ->
+ serversState.copy(
+ preInstalledServers = serversRepository.getPreInstalledServers()
+ .mapToServerItems(),
+ preInstalledStages = stagesRepository.getPreInstalledStages().mapToStageItems(),
+ addedServers = serversRepository.getServers().mapToServerItems(),
+ addedStages = stagesRepository.getStages().mapToStageItems()
+ )
}
}
}
- fun addServer(name: String, url: String) {
- val server = DebugServer(name = name, url = url, isDefault = false)
- viewModelScope.safeLaunch {
- serversRepository.addServer(server)
- loadAddedServers()
+ fun onAddClicked() {
+ _state.update { serversState ->
+ serversState.copy(serverDialogState = serversState.serverDialogState.copy(show = true))
}
}
- fun removeServer(debugServer: DebugServer) {
- viewModelScope.safeLaunch {
- serversRepository.removeServer(debugServer)
- loadAddedServers()
+ fun dismissDialogs() {
+ _state.update { serversState ->
+ serversState.copy(serverDialogState = ServerDialogState())
}
}
- fun updateServerData(id: Int, name: String, url: String) {
- val itemForUpdate = state.value?.addedServers
- ?.find { it is DebugServerItems.AddedServer && it.debugServer.id == id }
- as? DebugServerItems.AddedServer
+ fun onServerNameChanged(name: String) {
+ _state.update { serversState ->
+ serversState.copy(serverDialogState = serversState.serverDialogState.copy(serverName = name))
+ }
+ }
- val serverForUpdate = itemForUpdate?.debugServer
- val updatedServer = serverForUpdate?.copy(name = name, url = url)
+ fun onServerUrlChanged(url: String) {
+ _state.update { serversState ->
+ serversState.copy(serverDialogState = serversState.serverDialogState.copy(serverUrl = url))
+ }
+ }
- updatedServer?.let { server ->
- viewModelScope.safeLaunch {
- serversRepository.updateServer(server)
- loadAddedServers()
+ fun onSaveServerClicked() {
+ val dialogState = _state.value.serverDialogState
+ val inputErrors = checkInputErrors(dialogState)
+
+ if (inputErrors != null) {
+ _state.update { serversState ->
+ serversState.copy(
+ serverDialogState = dialogState.copy(
+ inputErrors = inputErrors
+ )
+ )
}
+
+ return
+ }
+
+ if (dialogState.editableServerId == null) {
+ addServer(dialogState.serverName, dialogState.serverUrl)
+ } else {
+ updateServerData(
+ dialogState.editableServerId,
+ dialogState.serverName,
+ dialogState.serverUrl
+ )
+ }
+
+ _state.update { serversState ->
+ serversState.copy(serverDialogState = ServerDialogState())
}
}
- fun onServerSelected(debugServer: DebugServer) {
- viewModelScope.launch {
- if (debugServer != getSelectedServer()) {
- getPlugin().pushEvent(ServerSelectedEvent(debugServer))
- serversRepository.saveSelectedServer(debugServer)
- loadServers()
- }
+ private fun checkInputErrors(dialogState: ServerDialogState): ServerDialogErrors? {
+ val nameError = R.string.error_empty_name.takeIf { dialogState.serverName.isEmpty() }
+ val urlError = R.string.error_wrong_host.takeIf {
+ !Patterns.WEB_URL.matcher(dialogState.serverUrl).matches()
+ }
+
+ return if (nameError != null || urlError != null) {
+ ServerDialogErrors(nameError = nameError, urlError = urlError)
+ } else {
+ null
}
}
- private suspend fun loadPreInstalledServers() {
- val servers = serversRepository.getPreInstalledServers()
- val headerText = context.getString(R.string.pre_installed_servers)
- val serverItems = mapToPreinstalledItems(headerText, servers)
- withContext(Dispatchers.Main) {
- state.value = state.value?.copy(preInstalledServers = serverItems)
+ fun onRemoveServerClicked(debugServer: DebugServer) {
+ viewModelScope.safeLaunch {
+ serversRepository.removeServer(debugServer)
+ _state.update { serversState ->
+ serversState.copy(
+ addedServers = serversRepository.getServers().mapToServerItems()
+ )
+ }
}
}
- private suspend fun loadAddedServers() {
- val servers = serversRepository.getServers()
- val headerText = context.getString(R.string.added_servers)
- val serverItems = mapToAddedItems(headerText, servers)
- withContext(Dispatchers.Main) {
- state.value = state.value?.copy(addedServers = serverItems)
+ private fun addServer(name: String, url: String) {
+ val server = DebugServer(name = name, url = url, isDefault = false)
+ viewModelScope.safeLaunch {
+ serversRepository.addServer(server)
+ _state.update { serversState ->
+ serversState.copy(
+ addedServers = serversRepository.getServers().mapToServerItems()
+ )
+ }
}
}
- private suspend fun mapToPreinstalledItems(
- header: String,
- servers: List
- ): List {
- val items = servers.map { debugServer ->
- val isSelected = getSelectedServer().url == debugServer.url
- DebugServerItems.PreinstalledServer(debugServer, isSelected)
+ private fun updateServerData(id: Int, name: String, url: String) {
+ val serverForUpdate = _state.value.addedServers
+ .find { it.server.id == id }
+ ?.server
+
+ serverForUpdate?.let {
+ val updatedServer = serverForUpdate.copy(name = name, url = url)
+ viewModelScope.safeLaunch {
+ serversRepository.updateServer(updatedServer)
+ _state.update { serversState ->
+ serversState.copy(
+ addedServers = serversRepository.getServers().mapToServerItems()
+ )
+ }
+ }
}
+ }
- return listOf(/*Заголовок списка*/DebugServerItems.Header(header)).plus(items)
+ fun onEditServerClicked(debugServer: DebugServer) {
+ _state.update { serversState ->
+ serversState.copy(
+ serverDialogState = ServerDialogState(
+ editableServerId = debugServer.id,
+ serverName = debugServer.name,
+ serverUrl = debugServer.url,
+ show = true
+ )
+ )
+ }
}
- private suspend fun mapToAddedItems(
- header: String,
- servers: List
- ): List {
- if (servers.isEmpty()) return emptyList()
- val items = servers.map { debugServer ->
- val isSelected = getSelectedServer().url == debugServer.url
- DebugServerItems.AddedServer(debugServer, isSelected)
+ fun onServerClicked(debugServer: DebugServer) {
+ viewModelScope.launch {
+ val selectedServer = serversRepository.getSelectedServer()
+ if (debugServer != selectedServer) {
+ getPlugin().pushEvent(ServerSelectedEvent(debugServer))
+ serversRepository.saveSelectedServer(debugServer)
+ loadServers()
+ }
}
+ }
- return listOf(/*Заголовок списка*/DebugServerItems.Header(header)).plus(items)
+ fun onStageClicked(debugStage: DebugStage) {
+ viewModelScope.launch {
+ val selectedStage = stagesRepository.getSelectedStage()
+ if (debugStage != selectedStage) {
+ stagesRepository.saveSelectedStage(debugStage)
+ getPlugin().pushEvent(StageSelectedEvent(debugStage))
+ loadServers()
+ }
+ }
}
- private suspend fun getSelectedServer(): DebugServer {
- return serversRepository.getSelectedServer()
+ private suspend fun List.mapToServerItems(): List {
+ val selectedServer = serversRepository.getSelectedServer()
+ return map { debugServer ->
+ val isSelected = debugServer == selectedServer
+ ServerItemData(debugServer, isSelected)
+ }
}
+ private fun List.mapToStageItems(): List {
+ val selectedStage = stagesRepository.getSelectedStage()
+ return map { debugStage ->
+ val isSelected = debugStage == selectedStage
+ StageItemData(debugStage, isSelected)
+ }
+ }
}
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewState.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewState.kt
index 1df7294c..79add53e 100644
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewState.kt
+++ b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/ServersViewState.kt
@@ -1,8 +1,28 @@
package com.redmadrobot.debug.plugin.servers.ui
-import com.redmadrobot.debug.plugin.servers.ui.item.DebugServerItems
+import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
internal data class ServersViewState(
- val preInstalledServers: List,
- val addedServers: List
+ val preInstalledServers: List = emptyList(),
+ val addedServers: List = emptyList(),
+ val preInstalledStages: List = emptyList(),
+ val addedStages: List = emptyList(),
+ val serverDialogState: ServerDialogState = ServerDialogState()
)
+
+internal data class ServerDialogState(
+ val show: Boolean = false,
+ val serverName: String = "",
+ val serverUrl: String = "",
+ val editableServerId: Int? = null,
+ val inputErrors: ServerDialogErrors? = null
+)
+
+internal data class ServerDialogErrors(
+ val nameError: Int?,
+ val urlError: Int?
+)
+
+internal data class ServerItemData(val server: DebugServer, val isSelected: Boolean)
+internal data class StageItemData(val server: DebugStage, val isSelected: Boolean)
diff --git a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/item/DebugServerItems.kt b/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/item/DebugServerItems.kt
deleted file mode 100644
index f90e9ea5..00000000
--- a/plugins/servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ui/item/DebugServerItems.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.redmadrobot.debug.plugin.servers.ui.item
-
-import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
-
-internal sealed class DebugServerItems {
-
- data class Header(val header: String) : DebugServerItems()
-
- data class PreinstalledServer(
- var debugServer: DebugServer,
- var isSelected: Boolean
- ) : DebugServerItems()
-
- data class AddedServer(
- var debugServer: DebugServer,
- var isSelected: Boolean
- ) : DebugServerItems()
-}
diff --git a/plugins/servers/src/main/res/drawable/icon_router.xml b/plugins/servers/src/main/res/drawable/icon_router.xml
index af4e8e58..70ac290e 100644
--- a/plugins/servers/src/main/res/drawable/icon_router.xml
+++ b/plugins/servers/src/main/res/drawable/icon_router.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/plugins/servers/src/main/res/drawable/icon_stage.xml b/plugins/servers/src/main/res/drawable/icon_stage.xml
new file mode 100644
index 00000000..dcde3147
--- /dev/null
+++ b/plugins/servers/src/main/res/drawable/icon_stage.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/plugins/servers/src/main/res/layout/dialog_server.xml b/plugins/servers/src/main/res/layout/dialog_server.xml
deleted file mode 100644
index 3a4042f3..00000000
--- a/plugins/servers/src/main/res/layout/dialog_server.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/servers/src/main/res/layout/fragment_servers.xml b/plugins/servers/src/main/res/layout/fragment_servers.xml
deleted file mode 100644
index 6680cb82..00000000
--- a/plugins/servers/src/main/res/layout/fragment_servers.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/plugins/servers/src/main/res/layout/item_debug_server.xml b/plugins/servers/src/main/res/layout/item_debug_server.xml
deleted file mode 100644
index 1c71d4ec..00000000
--- a/plugins/servers/src/main/res/layout/item_debug_server.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/servers/src/main/res/values/strings.xml b/plugins/servers/src/main/res/values/strings.xml
index a5ed16f8..8e6c23fd 100644
--- a/plugins/servers/src/main/res/values/strings.xml
+++ b/plugins/servers/src/main/res/values/strings.xml
@@ -1,11 +1,13 @@
Pre-installed servers
+ Pre-installed stages
https://google.com
Wrong host format.
Must be filled
save
Default server
- Added
+ Added servers
+ Added stages
Name
diff --git a/plugins/variable/build.gradle.kts b/plugins/variable/build.gradle.kts
index 424f6d4f..c389d8b6 100644
--- a/plugins/variable/build.gradle.kts
+++ b/plugins/variable/build.gradle.kts
@@ -39,6 +39,11 @@ android {
buildFeatures {
viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
namespace = "com.redmadrobot.debug.plugin.variable"
}
diff --git a/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/VariablePlugin.kt b/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/VariablePlugin.kt
index 9f75c358..58560dac 100644
--- a/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/VariablePlugin.kt
+++ b/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/VariablePlugin.kt
@@ -2,12 +2,16 @@ package com.redmadrobot.debug.plugin.variable
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import com.redmadrobot.debug.core.internal.CommonContainer
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidViewBinding
import com.redmadrobot.debug.core.extension.getPlugin
-import com.redmadrobot.debug.core.plugin.Plugin
+import com.redmadrobot.debug.core.internal.CommonContainer
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
-import com.redmadrobot.debug.plugin.variable.ui.VariableFragment
+import com.redmadrobot.debug.core.plugin.Plugin
+import com.redmadrobot.debug.plugin.variable.databinding.FragmentVariableBinding
import kotlin.reflect.KClass
public class VariablePlugin(
@@ -27,8 +31,12 @@ public class VariablePlugin(
)
}
- override fun getFragment(): Fragment {
- return VariableFragment()
+ @Composable
+ override fun content() {
+ AndroidViewBinding(
+ factory = FragmentVariableBinding::inflate,
+ modifier = Modifier.verticalScroll(rememberScrollState())
+ )
}
}
diff --git a/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableFragment.kt b/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableFragment.kt
index d2fefd7c..e56dd37a 100644
--- a/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableFragment.kt
+++ b/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableFragment.kt
@@ -4,13 +4,13 @@ import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
-import com.redmadrobot.debug.common.extension.obtainShareViewModel
import com.redmadrobot.debug.core.extension.getPlugin
+import com.redmadrobot.debug.core.extension.obtainShareViewModel
+import com.redmadrobot.debug.plugin.variable.R
import com.redmadrobot.debug.plugin.variable.VariablePlugin
import com.redmadrobot.debug.plugin.variable.VariablePluginContainer
-import com.redmadrobot.debug.plugin.variable.ui.recycler.VariableAdapter
-import com.redmadrobot.debug.plugin.variable.R
import com.redmadrobot.debug.plugin.variable.databinding.FragmentVariableBinding
+import com.redmadrobot.debug.plugin.variable.ui.recycler.VariableAdapter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
diff --git a/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableViewModel.kt b/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableViewModel.kt
index ab51bee2..2d07ff41 100644
--- a/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableViewModel.kt
+++ b/plugins/variable/src/main/kotlin/com/redmadrobot/debug/plugin/variable/ui/VariableViewModel.kt
@@ -1,6 +1,6 @@
package com.redmadrobot.debug.plugin.variable.ui
-import com.redmadrobot.debug.common.base.PluginViewModel
+import com.redmadrobot.debug.core.internal.PluginViewModel
import com.redmadrobot.debug.plugin.variable.VariableItem
import com.redmadrobot.debug.plugin.variable.VariableWidget
import com.redmadrobot.debug.plugin.variable.VariableWidgetSettings
diff --git a/plugins/variable/src/main/res/layout/fragment_container_variable.xml b/plugins/variable/src/main/res/layout/fragment_container_variable.xml
new file mode 100644
index 00000000..d49b3574
--- /dev/null
+++ b/plugins/variable/src/main/res/layout/fragment_container_variable.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/plugins/variable/src/main/res/layout/fragment_variable.xml b/plugins/variable/src/main/res/layout/fragment_variable.xml
index 1f14ffee..388f7cc9 100644
--- a/plugins/variable/src/main/res/layout/fragment_variable.xml
+++ b/plugins/variable/src/main/res/layout/fragment_variable.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts
index d2d48124..593ba518 100644
--- a/sample/build.gradle.kts
+++ b/sample/build.gradle.kts
@@ -23,6 +23,11 @@ android {
buildFeatures {
viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
buildTypes {
getByName("release") {
@@ -34,6 +39,7 @@ android {
dependencies {
implementation(kotlin("stdlib"))
implementation(androidx.appcompat)
+ implementation(androidx.compose.runtime)
implementation(stack.material)
implementation(androidx.constraintlayout)
implementation(rmr.flipper)
diff --git a/sample/src/debug/kotlin/com/redmadrobot/debug_sample/debug_data/DebugServersProvider.kt b/sample/src/debug/kotlin/com/redmadrobot/debug_sample/debug_data/DebugServersProvider.kt
index b4c65441..f493ec7c 100644
--- a/sample/src/debug/kotlin/com/redmadrobot/debug_sample/debug_data/DebugServersProvider.kt
+++ b/sample/src/debug/kotlin/com/redmadrobot/debug_sample/debug_data/DebugServersProvider.kt
@@ -2,13 +2,44 @@ package com.redmadrobot.debug_sample.debug_data
import com.redmadrobot.debug.core.data.DebugDataProvider
import com.redmadrobot.debug.plugin.servers.data.model.DebugServer
+import com.redmadrobot.debug.plugin.servers.data.model.DebugServerData
+import com.redmadrobot.debug.plugin.servers.data.model.DebugStage
-class DebugServersProvider : DebugDataProvider> {
+class DebugServersProvider {
- override fun provideData(): List {
+ fun provideData(): List {
return listOf(
- DebugServer(name = "debug 1", url = "https://testserver1.com", isDefault = true),
- DebugServer(name = "debug 2", url = "https://testserver2.com")
+ DebugServer(
+ name = "debug 1",
+ url = "https://testserver1.com",
+ isDefault = true
+ ),
+ DebugServer(
+ name = "debug 2", url = "https://testserver2.com"
+ ),
+ DebugServer(
+ name = "debug 3", url = "https://testserver3.com"
+ ),
+ DebugServer(
+ name = "debug 4", url = "https://testserver4.com"
+ ),
+ DebugStage(
+ name = "debug stage 1",
+ hosts = mapOf(
+ "main" to "https://testserver1main.com",
+ "s3" to "https://testserver1s3.com",
+ "wss" to "https://testserver1wss.com"
+ ),
+ isDefault = true
+ ),
+ DebugStage(
+ name = "debug stage 2",
+ hosts = mapOf(
+ "main" to "https://testserver2main.com",
+ "s3" to "https://testserver2s3.com",
+ "wss" to "https://testserver2wss.com"
+ )
+ ),
)
}
}
diff --git a/sample/src/main/kotlin/com/redmadrobot/debug_sample/App.kt b/sample/src/main/kotlin/com/redmadrobot/debug_sample/App.kt
index 470a7b71..e0830914 100644
--- a/sample/src/main/kotlin/com/redmadrobot/debug_sample/App.kt
+++ b/sample/src/main/kotlin/com/redmadrobot/debug_sample/App.kt
@@ -45,13 +45,6 @@ class App : Application() {
VariablePlugin(
customWidgets = DebugVariableWidgetsProvider().provideData()
),
-// , FeatureTogglesPlugin(
-// featureTogglesConfig = FeatureTogglesConfig(
-// FeatureToggleWrapperImpl.toggleNames,
-// FeatureToggleWrapperImpl(),
-// this
-// )
-// )
)
)
}
diff --git a/sample/src/main/kotlin/com/redmadrobot/debug_sample/FeatureToggleWrapperImpl.kt b/sample/src/main/kotlin/com/redmadrobot/debug_sample/FeatureToggleWrapperImpl.kt
deleted file mode 100644
index 0cab6c00..00000000
--- a/sample/src/main/kotlin/com/redmadrobot/debug_sample/FeatureToggleWrapperImpl.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-//package com.redmadrobot.debug_sample
-//
-//import com.redmadrobot.feature_togles_plugin.toggles.FeatureToggleWrapper
-//
-//class FeatureToggleWrapperImpl : FeatureToggleWrapper {
-// companion object {
-// val toggleNames = listOf("f1", "f2")
-// }
-//
-// override fun toggleValue(name: String): Boolean {
-// return name == toggleNames.firstOrNull()
-// }
-//}
diff --git a/sample/src/main/kotlin/com/redmadrobot/debug_sample/MainActivity.kt b/sample/src/main/kotlin/com/redmadrobot/debug_sample/MainActivity.kt
index 1df4ca24..71db2dec 100644
--- a/sample/src/main/kotlin/com/redmadrobot/debug_sample/MainActivity.kt
+++ b/sample/src/main/kotlin/com/redmadrobot/debug_sample/MainActivity.kt
@@ -53,10 +53,10 @@ class MainActivity : AppCompatActivity() {
}
DebugPanel.observeEvents()
- ?.onEach { event ->
+ .onEach { event ->
when (event) {
is AccountSelectedEvent -> {
- //Обработка выбора аккаунта
+ showSelectedAccount(event.debugAccount.login)
}
is ServerSelectedEvent -> {
@@ -64,12 +64,12 @@ class MainActivity : AppCompatActivity() {
}
}
}
- ?.launchIn(lifecycleScope)
+ .launchIn(lifecycleScope)
}
private fun ActivityMainBinding.setViews() {
- chooseAccount.setOnClickListener {
- chooseAccount()
+ openDebugPanel.setOnClickListener {
+ openDebugPanel()
}
requestTest.setOnClickListener {
makeTestRequest()
@@ -142,8 +142,16 @@ class MainActivity : AppCompatActivity() {
).show()
}
- private fun chooseAccount() {
- DebugPanel.showPanel(supportFragmentManager)
+ private fun showSelectedAccount(account: String) {
+ Toast.makeText(
+ this,
+ "Account $account selected",
+ Toast.LENGTH_LONG
+ ).show()
+ }
+
+ private fun openDebugPanel() {
+ DebugPanel.showPanel(this)
}
@OptIn(DelicateCoroutinesApi::class)
diff --git a/sample/src/main/kotlin/com/redmadrobot/debug_sample/network/ApiFactory.kt b/sample/src/main/kotlin/com/redmadrobot/debug_sample/network/ApiFactory.kt
index 83c50645..b127dc7f 100644
--- a/sample/src/main/kotlin/com/redmadrobot/debug_sample/network/ApiFactory.kt
+++ b/sample/src/main/kotlin/com/redmadrobot/debug_sample/network/ApiFactory.kt
@@ -1,10 +1,14 @@
package com.redmadrobot.debug_sample.network
-import com.redmadrobot.debug.plugin.servers.util.DebugServerInterceptor
+import com.redmadrobot.debug.plugin.servers.interceptor.DebugStageInterceptor
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import retrofit2.Retrofit
+import java.net.HttpURLConnection
+import java.net.URL
+import java.net.URLConnection
+import java.net.URLStreamHandler
object ApiFactory {
@@ -18,9 +22,10 @@ object ApiFactory {
}
private fun getSampleClient(onCalled: (String) -> Unit): OkHttpClient {
+
return OkHttpClient.Builder()
.addInterceptor(
- DebugServerInterceptor().modifyRequest { request, server ->
+ DebugStageInterceptor("s3").modifyRequest { request, server ->
if (server.name == "Test") {
request.newBuilder()
.addHeader("Authorization", "testToken")
diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml
index b4dcb76f..bd97e4f9 100644
--- a/sample/src/main/res/layout/activity_main.xml
+++ b/sample/src/main/res/layout/activity_main.xml
@@ -38,7 +38,7 @@
app:layout_constraintTop_toBottomOf="@id/label_feature_toggle_2" />
+ app:layout_constraintTop_toBottomOf="@+id/open_debug_panel" />