summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
blob: 82a6712b68c81818ae6a916138d489287a7e8343 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

package org.yuzu.yuzu_emu.applets.keyboard

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.view.KeyEvent
import android.view.View
import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import androidx.annotation.Keep
import androidx.core.view.ViewCompat
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
import java.io.Serializable

@Keep
object SoftwareKeyboard {
    lateinit var data: KeyboardData
    val dataLock = Object()

    private fun executeNormalImpl(config: KeyboardConfig) {
        val emulationActivity = NativeLibrary.sEmulationActivity.get()
        data = KeyboardData(SwkbdResult.Cancel.ordinal, "")
        val fragment = KeyboardDialogFragment.newInstance(config)
        fragment.show(emulationActivity!!.supportFragmentManager, KeyboardDialogFragment.TAG)
    }

    private fun executeInlineImpl(config: KeyboardConfig) {
        val emulationActivity = NativeLibrary.sEmulationActivity.get()

        val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay)
        val im =
            overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        im.showSoftInput(overlayView, InputMethodManager.SHOW_FORCED)

        // There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
        val handler = Handler(Looper.myLooper()!!)
        val delayMs = 500
        handler.postDelayed(object : Runnable {
            override fun run() {
                val insets = ViewCompat.getRootWindowInsets(overlayView)
                val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
                if (isKeyboardVisible) {
                    handler.postDelayed(this, delayMs.toLong())
                    return
                }

                // No longer visible, submit the result.
                NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
            }
        }, delayMs.toLong())
    }

    @JvmStatic
    fun executeNormal(config: KeyboardConfig): KeyboardData {
        NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { executeNormalImpl(config) }
        synchronized(dataLock) {
            dataLock.wait()
        }
        return data
    }

    @JvmStatic
    fun executeInline(config: KeyboardConfig) {
        NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { executeInlineImpl(config) }
    }

    // Corresponds to Service::AM::Applets::SwkbdType
    enum class SwkbdType {
        Normal,
        NumberPad,
        Qwerty,
        Unknown3,
        Latin,
        SimplifiedChinese,
        TraditionalChinese,
        Korean
    }

    // Corresponds to Service::AM::Applets::SwkbdPasswordMode
    enum class SwkbdPasswordMode {
        Disabled,
        Enabled
    }

    // Corresponds to Service::AM::Applets::SwkbdResult
    enum class SwkbdResult {
        Ok,
        Cancel
    }

    @Keep
    data class KeyboardConfig(
        var ok_text: String? = null,
        var header_text: String? = null,
        var sub_text: String? = null,
        var guide_text: String? = null,
        var initial_text: String? = null,
        var left_optional_symbol_key: Short = 0,
        var right_optional_symbol_key: Short = 0,
        var max_text_length: Int = 0,
        var min_text_length: Int = 0,
        var initial_cursor_position: Int = 0,
        var type: Int = 0,
        var password_mode: Int = 0,
        var text_draw_type: Int = 0,
        var key_disable_flags: Int = 0,
        var use_blur_background: Boolean = false,
        var enable_backspace_button: Boolean = false,
        var enable_return_button: Boolean = false,
        var disable_cancel_button: Boolean = false
    ) : Serializable

    // Corresponds to Frontend::KeyboardData
    @Keep
    data class KeyboardData(var result: Int, var text: String)
}