feat(android): Add the keyboard height code

This commit is contained in:
PapaTutuWawa 2023-09-18 20:50:08 +02:00
parent 1852f2d198
commit f949b008b3
6 changed files with 104 additions and 2 deletions

View File

@ -27,3 +27,7 @@ Thanks to [ekasetiawans](https://github.com/ekasetiawans) for [flutter_backgroun
was essentially the blueprint for the service and background service APIs. They were reimplemented was essentially the blueprint for the service and background service APIs. They were reimplemented
to allow the root isolate to pass some additional data to the service, which `flutter_background_service` to allow the root isolate to pass some additional data to the service, which `flutter_background_service`
did not support. did not support.
Thanks to [nschairer](https://github.com/nschairer) for [flutter_keyboard_height](https://github.com/nschairer/keyboard_height_plugin), which was the base for keeping track of the keyboard height.
Due to having an issue with the height calculation if the Android device uses gesture navigation, I
[forked the package](https://git.polynom.me/moxxy/keyboard_height_plugin) and modified the height calculation.

View File

@ -2,6 +2,9 @@ package org.moxxy.moxxy_native
const val TAG = "moxxy_native" const val TAG = "moxxy_native"
// The event channel name for the keyboard height
const val KEYBOARD_HEIGHT_EVENT_CHANNEL_NAME = "org.moxxy.moxxyv2/notification_stream"
// The size of buffers to use for various operations // The size of buffers to use for various operations
const val BUFFER_SIZE = 4096 const val BUFFER_SIZE = 4096

View File

@ -29,6 +29,7 @@ import org.moxxy.moxxy_native.notifications.NotificationsImplementation
import org.moxxy.moxxy_native.picker.FilePickerType import org.moxxy.moxxy_native.picker.FilePickerType
import org.moxxy.moxxy_native.picker.MoxxyPickerApi import org.moxxy.moxxy_native.picker.MoxxyPickerApi
import org.moxxy.moxxy_native.picker.PickerResultListener import org.moxxy.moxxy_native.picker.PickerResultListener
import org.moxxy.moxxy_native.platform.KeyboardStreamHandler
import org.moxxy.moxxy_native.platform.MoxxyPlatformApi import org.moxxy.moxxy_native.platform.MoxxyPlatformApi
import org.moxxy.moxxy_native.platform.PlatformImplementation import org.moxxy.moxxy_native.platform.PlatformImplementation
import org.moxxy.moxxy_native.service.BackgroundService import org.moxxy.moxxy_native.service.BackgroundService
@ -102,6 +103,10 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, ServiceAware, BroadcastR
IntentFilter(SERVICE_FOREGROUND_METHOD_CHANNEL_KEY), IntentFilter(SERVICE_FOREGROUND_METHOD_CHANNEL_KEY),
) )
// Special handling for the keyboard height
val keyboardChannel = EventChannel(flutterPluginBinding.getBinaryMessenger(), KEYBOARD_HEIGHT_EVENT_CHANNEL_NAME)
keyboardChannel?.setStreamHandler(KeyboardStreamHandler)
// Register the picker handler // Register the picker handler
pickerListener = PickerResultListener(context!!) pickerListener = PickerResultListener(context!!)
Log.d(TAG, "Attached to engine") Log.d(TAG, "Attached to engine")
@ -118,20 +123,24 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, ServiceAware, BroadcastR
override fun onAttachedToActivity(binding: ActivityPluginBinding) { override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity activity = binding.activity
binding.addActivityResultListener(pickerListener) binding.addActivityResultListener(pickerListener)
KeyboardStreamHandler.activity = activity
Log.d(TAG, "Attached to activity") Log.d(TAG, "Attached to activity")
} }
override fun onDetachedFromActivityForConfigChanges() { override fun onDetachedFromActivityForConfigChanges() {
activity = null activity = null
KeyboardStreamHandler.activity = null
Log.d(TAG, "Detached from activity") Log.d(TAG, "Detached from activity")
} }
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity activity = binding.activity
KeyboardStreamHandler.activity = activity
} }
override fun onDetachedFromActivity() { override fun onDetachedFromActivity() {
activity = null activity = null
KeyboardStreamHandler.activity = null
Log.d(TAG, "Detached from activity") Log.d(TAG, "Detached from activity")
} }

View File

@ -164,7 +164,7 @@ class NotificationReceiver : BroadcastReceiver() {
} }
} }
fun handleTap(context: Context, intent: Intent) { private fun handleTap(context: Context, intent: Intent) {
MoxxyEventChannels.notificationEventSink?.success( MoxxyEventChannels.notificationEventSink?.success(
NotificationEvent( NotificationEvent(
intent.getLongExtra(NOTIFICATION_EXTRA_ID_KEY, -1), intent.getLongExtra(NOTIFICATION_EXTRA_ID_KEY, -1),

View File

@ -0,0 +1,73 @@
package org.moxxy.moxxy_native.platform
import android.app.Activity
import android.graphics.Rect
import android.util.Log
import android.view.View
import android.view.ViewTreeObserver
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import io.flutter.plugin.common.EventChannel
import org.moxxy.moxxy_native.MoxxyEventChannels
import org.moxxy.moxxy_native.TAG
object KeyboardStreamHandler : EventChannel.StreamHandler {
// The currently active activity. Set by @MoxxyNativePlugin.
var activity: Activity? = null
// The current bottom inset.
private var bottomInset: Int = 0
// The current event sink to use for sending events to the UI.
private var sink: EventChannel.EventSink? = null
private fun handleKeyboardHeightCheck(rootView: View?) {
rootView?.viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val r = Rect()
rootView.getWindowVisibleDisplayFrame(r)
val screenHeight = rootView.height
// Also subtract the height of the bottom inset as the SafeArea with "bottom: false"
// allows us to draw under the bottom system bar, if it is there.
val keypadHeight = screenHeight - r.bottom - bottomInset
val displayMetrics = activity?.resources?.displayMetrics
val logicalKeypadHeight = keypadHeight / (displayMetrics?.density ?: 1f)
if (keypadHeight > screenHeight * 0.15) {
sink?.success(logicalKeypadHeight.toDouble())
} else {
sink?.success(0.0)
}
}
})
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
// "register" the event sink
sink = events
val rootView = activity?.window?.decorView?.rootView
handleKeyboardHeightCheck(rootView)
if (rootView != null) {
ViewCompat.setOnApplyWindowInsetsListener(rootView!!) { _, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val triggerEvent = bottomInset != insets.bottom
bottomInset = insets.bottom
// Notify in case the inset changed
if (triggerEvent) handleKeyboardHeightCheck(rootView)
WindowInsetsCompat.CONSUMED
}
}
Log.d(TAG, "KeyboardStreamHandler: Attached stream")
}
override fun onCancel(arguments: Any?) {
MoxxyEventChannels.notificationEventSink = null
Log.d(TAG, "KeyboardStreamHandler: Detached stream")
}
}

View File

@ -1,6 +1,5 @@
// ignore_for_file: avoid_print // ignore_for_file: avoid_print
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
@ -51,6 +50,19 @@ class TestEvent extends BackgroundEvent {
class MyAppState extends State<MyApp> { class MyAppState extends State<MyApp> {
String? imagePath; String? imagePath;
@override
void initState() {
super.initState();
const EventChannel('org.moxxy.moxxyv2/notification_stream')
.receiveBroadcastStream()
.listen(
(event) {
print('Keyboard height: ${event as double}');
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
@ -221,6 +233,7 @@ class MyAppState extends State<MyApp> {
}, },
child: const Text('Share some text'), child: const Text('Share some text'),
), ),
const TextField(),
], ],
), ),
), ),