diff --git a/android/app/src/main/java/com/soniox/remotemic/MainActivity.kt b/android/app/src/main/java/com/soniox/remotemic/MainActivity.kt
index 8eaf4fb..d18d4b8 100644
--- a/android/app/src/main/java/com/soniox/remotemic/MainActivity.kt
+++ b/android/app/src/main/java/com/soniox/remotemic/MainActivity.kt
@@ -8,6 +8,7 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.pm.PackageManager
+import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.os.Handler
@@ -20,6 +21,7 @@ import android.text.TextWatcher
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
+import android.view.ViewTreeObserver
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ScrollView
import android.widget.TextView
@@ -56,6 +58,7 @@ class MainActivity : AppCompatActivity() {
private var isApplyingRemoteUpdate = false
private var lastLocalText = ""
private var lastLocalUserEditTime = 0L
+ private var isKeyboardCurrentlyVisible = false
private val debounceHandler = Handler(Looper.getMainLooper())
private var pendingSyncRunnable: Runnable? = null
@@ -81,11 +84,11 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
- setupKeyboardInsetHandling()
+ setupKeyboardVisibilityDetection()
setupUI()
checkPermissions()
- AppLogger.log("Main", "اپلیکیشن راهاندازی شد (همگامسازی بلادرنگ بدون باگ پاککردن)")
+ AppLogger.log("Main", "اپلیکیشن راهاندازی شد (طراحی ریسپانسیو و فوکوس کامل اینپوتباکس روی کیبورد)")
// Initialize Collaborative WebSocket Client
streamDictationClient = StreamDictationClient(
@@ -156,22 +159,49 @@ class MainActivity : AppCompatActivity() {
)
}
- private fun setupKeyboardInsetHandling() {
+ /**
+ * Dual-engine keyboard visibility detector (WindowInsets + OnGlobalLayoutListener)
+ * Guarantees 100% detection on all Android versions and keyboards.
+ */
+ private fun setupKeyboardVisibilityDetection() {
+ // Engine 1: Modern WindowInsets
ViewCompat.setOnApplyWindowInsetsListener(binding.rootLayout) { _, insets ->
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
-
- if (imeVisible) {
- binding.bottomVoiceSection.visibility = View.GONE
- binding.tvSubtitle.visibility = View.GONE
- binding.btnMiniMic.visibility = View.VISIBLE
- } else {
- binding.bottomVoiceSection.visibility = View.VISIBLE
- binding.tvSubtitle.visibility = View.VISIBLE
- binding.btnMiniMic.visibility = View.GONE
- }
-
+ updateKeyboardUIMode(imeVisible)
insets
}
+
+ // Engine 2: Global Layout Frame Calculation (Fallback for OEM soft keyboards)
+ binding.rootLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
+ private val r = Rect()
+ override fun onGlobalLayout() {
+ binding.rootLayout.getWindowVisibleDisplayFrame(r)
+ val screenHeight = binding.rootLayout.rootView.height
+ val keypadHeight = screenHeight - r.bottom
+ val isKeyboardOpen = keypadHeight > screenHeight * 0.15
+ updateKeyboardUIMode(isKeyboardOpen)
+ }
+ })
+ }
+
+ private fun updateKeyboardUIMode(isKeyboardOpen: Boolean) {
+ if (isKeyboardCurrentlyVisible == isKeyboardOpen) return
+ isKeyboardCurrentlyVisible = isKeyboardOpen
+
+ if (isKeyboardOpen) {
+ // KEYBOARD OPEN: Hide big mic circle, hide action buttons & divider completely!
+ // Give 100% of available space above keyboard exclusively to the huge text editor box!
+ binding.bottomVoiceSection.visibility = View.GONE
+ binding.actionDivider.visibility = View.GONE
+ binding.actionButtonsRow.visibility = View.GONE
+ binding.tvSubtitle.visibility = View.GONE
+ } else {
+ // KEYBOARD CLOSED: Restore spacious layout with large glowing mic button & action bar
+ binding.bottomVoiceSection.visibility = View.VISIBLE
+ binding.actionDivider.visibility = View.VISIBLE
+ binding.actionButtonsRow.visibility = View.VISIBLE
+ binding.tvSubtitle.visibility = View.VISIBLE
+ }
}
private fun insertSpeechAtCursor(speechText: String) {
@@ -287,11 +317,6 @@ class MainActivity : AppCompatActivity() {
binding.btnMic.setOnTouchListener { _, event ->
handleMicTouch(event)
}
-
- // Touch listener for Compact Mini Mic Button (above keyboard)
- binding.btnMiniMic.setOnTouchListener { _, event ->
- handleMicTouch(event)
- }
}
private fun handleMicTouch(event: MotionEvent): Boolean {
@@ -413,7 +438,6 @@ class MainActivity : AppCompatActivity() {
binding.tvInstruction.text = getString(R.string.release_to_type)
binding.tvInstruction.setTextColor(ContextCompat.getColor(this, R.color.accent_red))
binding.btnMic.setBackgroundResource(R.drawable.bg_mic_button_active)
- binding.btnMiniMic.setBackgroundColor(ContextCompat.getColor(this, R.color.accent_red))
startPulseAnimation()
streamDictationClient?.startRecording(voiceInsertionCursorStart)
@@ -425,7 +449,6 @@ class MainActivity : AppCompatActivity() {
stopPulseAnimation()
binding.btnMic.setBackgroundResource(R.drawable.bg_mic_button)
- binding.btnMiniMic.setBackgroundColor(ContextCompat.getColor(this, R.color.accent_blue))
binding.tvInstruction.text = "⏳ در حال درج و همگامسازی..."
binding.tvInstruction.setTextColor(ContextCompat.getColor(this, R.color.accent_yellow))
diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml
index 1cec95f..f945bc3 100644
--- a/android/app/src/main/res/layout/activity_main.xml
+++ b/android/app/src/main/res/layout/activity_main.xml
@@ -98,6 +98,7 @@
-
+
-
+
-
-
-