Skip to content

Commit 48ca3ab

Browse files
[Super Keyboard] - Make it possible to forward Android and iOS platform logs to Dart, and then handle with custom log printer (Resolves #2813) (#2815)
1 parent 51fed2c commit 48ca3ab

File tree

9 files changed

+270
-88
lines changed

9 files changed

+270
-88
lines changed
Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,79 @@
11
package com.flutterbountyhunters.superkeyboard.super_keyboard
22

33
import android.util.Log
4+
import io.flutter.plugin.common.MethodChannel
45

56
object SuperKeyboardLog {
6-
var isLoggingEnabled: Boolean = false
7+
private var isLoggingEnabled: Boolean = false
8+
private var reportTo: MethodChannel? = null
9+
10+
fun enable(reportTo: MethodChannel?) {
11+
isLoggingEnabled = true
12+
this.reportTo = reportTo
13+
}
14+
15+
fun disable() {
16+
isLoggingEnabled = false
17+
reportTo = null
18+
}
19+
20+
fun v(tag: String, message: String) {
21+
if (isLoggingEnabled) {
22+
if (reportTo == null) {
23+
Log.v(tag, message)
24+
} else {
25+
reportToDart("v", message);
26+
}
27+
}
28+
}
729

830
fun d(tag: String, message: String) {
9-
if (isLoggingEnabled) Log.d(tag, message)
31+
if (isLoggingEnabled) {
32+
if (reportTo == null) {
33+
Log.d(tag, message)
34+
} else {
35+
reportToDart("d", message);
36+
}
37+
}
1038
}
1139

1240
fun i(tag: String, message: String) {
13-
if (isLoggingEnabled) Log.i(tag, message)
41+
if (isLoggingEnabled) {
42+
if (reportTo == null) {
43+
Log.i(tag, message)
44+
} else {
45+
reportToDart("i", message);
46+
}
47+
}
1448
}
1549

1650
fun w(tag: String, message: String) {
17-
if (isLoggingEnabled) Log.w(tag, message)
51+
if (isLoggingEnabled) {
52+
if (reportTo == null) {
53+
Log.w(tag, message)
54+
} else {
55+
reportToDart("w", message);
56+
}
57+
}
1858
}
1959

2060
fun e(tag: String, message: String, throwable: Throwable? = null) {
21-
if (isLoggingEnabled) Log.e(tag, message, throwable)
61+
if (isLoggingEnabled) {
62+
if (reportTo == null) {
63+
Log.e(tag, message, throwable)
64+
} else {
65+
reportToDart("e", message);
66+
}
67+
}
2268
}
2369

24-
fun v(tag: String, message: String) {
25-
if (isLoggingEnabled) Log.v(tag, message)
70+
private fun reportToDart(level: String, message: String) {
71+
reportTo!!.invokeMethod(
72+
"log",
73+
mapOf(
74+
"level" to level,
75+
"message" to message,
76+
)
77+
)
2678
}
2779
}

super_keyboard/android/src/main/kotlin/com/flutterbountyhunters/superkeyboard/super_keyboard/SuperKeyboardPlugin.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,12 @@ class SuperKeyboardPlugin: FlutterPlugin, ActivityAware, DefaultLifecycleObserve
6161
channel.setMethodCallHandler { call, result ->
6262
when (call.method) {
6363
"startLogging" -> {
64-
SuperKeyboardLog.isLoggingEnabled = true
64+
val forwardToDart = call.argument<Boolean?>("sendPlatformLogsToDart") ?: false
65+
SuperKeyboardLog.enable(if (forwardToDart) channel else null)
6566
result.success(null)
6667
}
6768
"stopLogging" -> {
68-
SuperKeyboardLog.isLoggingEnabled = false
69+
SuperKeyboardLog.disable()
6970
result.success(null)
7071
}
7172
else -> result.notImplemented()
@@ -130,6 +131,7 @@ class SuperKeyboardPlugin: FlutterPlugin, ActivityAware, DefaultLifecycleObserve
130131

131132
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
132133
SuperKeyboardLog.d("super_keyboard", "Detached from Flutter engine")
134+
SuperKeyboardLog.disable()
133135
this.binding = null
134136
}
135137

@@ -199,11 +201,13 @@ class SuperKeyboardPlugin: FlutterPlugin, ActivityAware, DefaultLifecycleObserve
199201
animation: WindowInsetsAnimationCompat
200202
) {
201203
// Report whether the keyboard has fully opened or fully closed.
202-
SuperKeyboardLog.v("super_keyboard", "Insets animation callback - onEnd - current keyboard state: $keyboardState")
204+
SuperKeyboardLog.i("super_keyboard", "Insets animation callback - onEnd - current keyboard state: $keyboardState")
203205
if (keyboardState == KeyboardState.Opening) {
206+
SuperKeyboardLog.i("super_keyboard", "Sending new keyboard state: open")
204207
keyboardState = KeyboardState.Open
205208
sendMessageKeyboardOpened()
206209
} else if (keyboardState == KeyboardState.Closing) {
210+
SuperKeyboardLog.i("super_keyboard", "Sending new keyboard state: closing")
207211
keyboardState = KeyboardState.Closed
208212
sendMessageKeyboardClosed()
209213
}

super_keyboard/example/lib/main.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class _SuperKeyboardDemoAppState extends State<SuperKeyboardDemoApp> {
2929

3030
Future<void> initSuperKeyboard() async {
3131
if (_isFlutterLoggingEnabled) {
32-
SuperKeyboard.startLogging();
32+
SKLog.startLogging();
3333
}
3434
}
3535

@@ -155,9 +155,9 @@ class _SuperKeyboardDemoAppState extends State<SuperKeyboardDemoApp> {
155155
_isFlutterLoggingEnabled = newValue;
156156

157157
if (_isFlutterLoggingEnabled) {
158-
SuperKeyboard.startLogging();
158+
SKLog.startLogging();
159159
} else {
160-
SuperKeyboard.stopLogging();
160+
SKLog.stopLogging();
161161
}
162162
});
163163
},

super_keyboard/ios/Classes/SuperKeyboardPlugin.swift

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ public class SuperKeyboardPlugin: NSObject, FlutterPlugin {
1818

1919
init(binaryMessenger: FlutterBinaryMessenger) {
2020
super.init()
21-
21+
2222
channel = FlutterMethodChannel(name: "super_keyboard_ios", binaryMessenger: binaryMessenger)
23-
23+
2424
// Register for keyboard notifications
2525
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
2626
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow(_:)), name: UIResponder.keyboardDidShowNotification, object: nil)
@@ -33,12 +33,22 @@ public class SuperKeyboardPlugin: NSObject, FlutterPlugin {
3333

3434
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
3535
switch call.method {
36+
case "startLogging":
37+
let sendPlatformLogsToDart = (call.arguments as? [String: Any])?["sendPlatformLogsToDart"] as? Bool ?? false
38+
39+
SuperKeyboardLog.enable(reportTo: sendPlatformLogsToDart ? channel : nil)
40+
41+
result(nil)
42+
case "stopLogging":
43+
SuperKeyboardLog.disable()
44+
result(nil)
3645
default:
3746
result(FlutterMethodNotImplemented)
3847
}
3948
}
4049

4150
@objc private func keyboardWillShow(_ notification: Notification) {
51+
SuperKeyboardLog.log(message: "Keyboard will show")
4252
channel!.invokeMethod("keyboardWillShow", arguments: nil)
4353
}
4454

@@ -51,7 +61,8 @@ public class SuperKeyboardPlugin: NSObject, FlutterPlugin {
5161
// Calculate the current keyboard height
5262
let screenHeight = window.bounds.height
5363
let keyboardHeight = max(0, screenHeight - keyboardFrame.origin.y)
54-
64+
65+
SuperKeyboardLog.log(message: "Keyboard did show - reporting keyboard height: \(keyboardHeight)")
5566
channel!.invokeMethod("keyboardDidShow", arguments: [
5667
"keyboardHeight": keyboardHeight
5768
])
@@ -75,7 +86,8 @@ public class SuperKeyboardPlugin: NSObject, FlutterPlugin {
7586
default:
7687
keyboardType = .full
7788
}
78-
89+
90+
SuperKeyboardLog.log(message: "Keyboard frame change - new target keyboard height: \(keyboardFrame.height)")
7991
channel!.invokeMethod("keyboardWillChangeFrame", arguments: [
8092
"keyboardType": keyboardType.description,
8193
"targetKeyboardHeight": keyboardFrame.height
@@ -157,10 +169,12 @@ public class SuperKeyboardPlugin: NSObject, FlutterPlugin {
157169
// }
158170

159171
@objc private func keyboardWillHide(_ notification: Notification) {
172+
SuperKeyboardLog.log(message: "Keyboard will hide")
160173
channel!.invokeMethod("keyboardWillHide", arguments: nil)
161174
}
162175

163176
@objc private func keyboardDidHide(_ notification: Notification) {
177+
SuperKeyboardLog.log(message: "Keyboard did hide - reporting height: 0")
164178
channel!.invokeMethod("keyboardDidHide", arguments: [
165179
"keyboardHeight": 0
166180
])
@@ -184,3 +198,38 @@ enum KeyboardType {
184198
}
185199
}
186200
}
201+
202+
public class SuperKeyboardLog {
203+
static private var isLoggingEnabled: Bool = false
204+
static private var reportTo: FlutterMethodChannel? = nil
205+
206+
static func enable(reportTo: FlutterMethodChannel?) {
207+
isLoggingEnabled = true
208+
self.reportTo = reportTo
209+
}
210+
211+
static func disable() {
212+
isLoggingEnabled = false
213+
reportTo = nil
214+
}
215+
216+
// TODO: Add log levels - not sure what's typical for iOS. Android is V, D, I, W, E.
217+
static func log(message: String) {
218+
if (isLoggingEnabled) {
219+
if (reportTo != nil) {
220+
reportToDart(message)
221+
} else {
222+
print(message)
223+
}
224+
}
225+
}
226+
227+
static private func reportToDart(_ message: String) {
228+
reportTo!.invokeMethod(
229+
"log",
230+
arguments: [
231+
"message": message,
232+
]
233+
)
234+
}
235+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'dart:async';
2+
3+
import 'package:logging/logging.dart';
4+
5+
/// Loggers for Super Keyboard, which can be activated by log level and by focal
6+
/// area, and can also print to a given [LogPrinter].
7+
abstract class SKLog {
8+
static final superKeyboard = Logger("super_keyboard");
9+
static final unified = Logger("super_keyboard.unified");
10+
static final ios = Logger("super_keyboard.ios");
11+
static final android = Logger("super_keyboard.android");
12+
13+
static StreamSubscription<LogRecord>? _logRecordSubscription;
14+
15+
static void startLogging([Level level = Level.ALL, LogPrinter? printer]) {
16+
if (_logRecordSubscription != null) {
17+
_logRecordSubscription!.cancel();
18+
_logRecordSubscription = null;
19+
}
20+
21+
hierarchicalLoggingEnabled = true;
22+
superKeyboard.level = level;
23+
_logRecordSubscription = superKeyboard.onRecord.listen(printer ?? defaultLogPrinter);
24+
}
25+
26+
static void stopLogging() {
27+
superKeyboard.level = Level.OFF;
28+
29+
if (_logRecordSubscription != null) {
30+
_logRecordSubscription!.cancel();
31+
_logRecordSubscription = null;
32+
}
33+
}
34+
}
35+
36+
void defaultLogPrinter(LogRecord record) {
37+
// ignore: avoid_print
38+
print('${record.level.name}: ${record.time.toLogTime()}: ${record.message}');
39+
}
40+
41+
typedef LogPrinter = void Function(LogRecord);
42+
43+
extension on DateTime {
44+
String toLogTime() {
45+
String h = _twoDigits(hour);
46+
String min = _twoDigits(minute);
47+
String sec = _twoDigits(second);
48+
String ms = _threeDigits(millisecond);
49+
if (isUtc) {
50+
return "$h:$min:$sec.$ms";
51+
} else {
52+
return "$h:$min:$sec.$ms";
53+
}
54+
}
55+
56+
String _threeDigits(int n) {
57+
if (n >= 100) return "$n";
58+
if (n >= 10) return "0$n";
59+
return "00$n";
60+
}
61+
62+
String _twoDigits(int n) {
63+
if (n >= 10) return "$n";
64+
return "0$n";
65+
}
66+
}

0 commit comments

Comments
 (0)