mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-11-04 04:34:07 +01:00 
			
		
		
		
	android: Convert keyboard applet to kotlin and refactor
This commit is contained in:
		
							parent
							
								
									d5ebfc8e21
								
							
						
					
					
						commit
						d30103b69f
					
				@ -83,22 +83,22 @@ open class EmulationActivity : AppCompatActivity() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
 | 
			
		||||
        if (event.action == android.view.KeyEvent.ACTION_DOWN) {
 | 
			
		||||
            if (keyCode == android.view.KeyEvent.KEYCODE_ENTER) {
 | 
			
		||||
        if (event.action == KeyEvent.ACTION_DOWN) {
 | 
			
		||||
            if (keyCode == KeyEvent.KEYCODE_ENTER) {
 | 
			
		||||
                // Special case, we do not support multiline input, dismiss the keyboard.
 | 
			
		||||
                val overlayView: View =
 | 
			
		||||
                    this.findViewById<View>(R.id.surface_input_overlay)
 | 
			
		||||
                    this.findViewById(R.id.surface_input_overlay)
 | 
			
		||||
                val im =
 | 
			
		||||
                    overlayView.context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
 | 
			
		||||
                im.hideSoftInputFromWindow(overlayView.windowToken, 0);
 | 
			
		||||
                im.hideSoftInputFromWindow(overlayView.windowToken, 0)
 | 
			
		||||
            } else {
 | 
			
		||||
                val textChar = event.getUnicodeChar();
 | 
			
		||||
                val textChar = event.unicodeChar
 | 
			
		||||
                if (textChar == 0) {
 | 
			
		||||
                    // No text, button input.
 | 
			
		||||
                    NativeLibrary.SubmitInlineKeyboardInput(keyCode);
 | 
			
		||||
                    NativeLibrary.SubmitInlineKeyboardInput(keyCode)
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Text submitted.
 | 
			
		||||
                    NativeLibrary.SubmitInlineKeyboardText(textChar.toChar().toString());
 | 
			
		||||
                    NativeLibrary.SubmitInlineKeyboardText(textChar.toChar().toString())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,264 +0,0 @@
 | 
			
		||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.applets;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.Dialog;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.DialogInterface;
 | 
			
		||||
import android.graphics.Rect;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.ResultReceiver;
 | 
			
		||||
import android.text.InputFilter;
 | 
			
		||||
import android.text.InputType;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.view.WindowInsets;
 | 
			
		||||
import android.view.inputmethod.InputMethodManager;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.FrameLayout;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.appcompat.app.AlertDialog;
 | 
			
		||||
import androidx.core.view.ViewCompat;
 | 
			
		||||
import androidx.fragment.app.DialogFragment;
 | 
			
		||||
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 | 
			
		||||
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication;
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary;
 | 
			
		||||
import org.yuzu.yuzu_emu.R;
 | 
			
		||||
import org.yuzu.yuzu_emu.activities.EmulationActivity;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
public final class SoftwareKeyboard {
 | 
			
		||||
    /// Corresponds to Service::AM::Applets::SwkbdType
 | 
			
		||||
    private interface SwkbdType {
 | 
			
		||||
        int Normal = 0;
 | 
			
		||||
        int NumberPad = 1;
 | 
			
		||||
        int Qwerty = 2;
 | 
			
		||||
        int Unknown3 = 3;
 | 
			
		||||
        int Latin = 4;
 | 
			
		||||
        int SimplifiedChinese = 5;
 | 
			
		||||
        int TraditionalChinese = 6;
 | 
			
		||||
        int Korean = 7;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /// Corresponds to Service::AM::Applets::SwkbdPasswordMode
 | 
			
		||||
    private interface SwkbdPasswordMode {
 | 
			
		||||
        int Disabled = 0;
 | 
			
		||||
        int Enabled = 1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /// Corresponds to Service::AM::Applets::SwkbdResult
 | 
			
		||||
    private interface SwkbdResult {
 | 
			
		||||
        int Ok = 0;
 | 
			
		||||
        int Cancel = 1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public static class KeyboardConfig implements java.io.Serializable {
 | 
			
		||||
        public String ok_text;
 | 
			
		||||
        public String header_text;
 | 
			
		||||
        public String sub_text;
 | 
			
		||||
        public String guide_text;
 | 
			
		||||
        public String initial_text;
 | 
			
		||||
        public short left_optional_symbol_key;
 | 
			
		||||
        public short right_optional_symbol_key;
 | 
			
		||||
        public int max_text_length;
 | 
			
		||||
        public int min_text_length;
 | 
			
		||||
        public int initial_cursor_position;
 | 
			
		||||
        public int type;
 | 
			
		||||
        public int password_mode;
 | 
			
		||||
        public int text_draw_type;
 | 
			
		||||
        public int key_disable_flags;
 | 
			
		||||
        public boolean use_blur_background;
 | 
			
		||||
        public boolean enable_backspace_button;
 | 
			
		||||
        public boolean enable_return_button;
 | 
			
		||||
        public boolean disable_cancel_button;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Corresponds to Frontend::KeyboardData
 | 
			
		||||
    public static class KeyboardData {
 | 
			
		||||
        public int result;
 | 
			
		||||
        public String text;
 | 
			
		||||
 | 
			
		||||
        private KeyboardData(int result, String text) {
 | 
			
		||||
            this.result = result;
 | 
			
		||||
            this.text = text;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class KeyboardDialogFragment extends DialogFragment {
 | 
			
		||||
        static KeyboardDialogFragment newInstance(KeyboardConfig config) {
 | 
			
		||||
            KeyboardDialogFragment frag = new KeyboardDialogFragment();
 | 
			
		||||
            Bundle args = new Bundle();
 | 
			
		||||
            args.putSerializable("config", config);
 | 
			
		||||
            frag.setArguments(args);
 | 
			
		||||
            return frag;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @NonNull
 | 
			
		||||
        @Override
 | 
			
		||||
        public Dialog onCreateDialog(Bundle savedInstanceState) {
 | 
			
		||||
            final Activity emulationActivity = getActivity();
 | 
			
		||||
            assert emulationActivity != null;
 | 
			
		||||
 | 
			
		||||
            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
 | 
			
		||||
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 | 
			
		||||
            params.leftMargin = params.rightMargin =
 | 
			
		||||
                    YuzuApplication.getAppContext().getResources().getDimensionPixelSize(
 | 
			
		||||
                            R.dimen.dialog_margin);
 | 
			
		||||
 | 
			
		||||
            KeyboardConfig config = Objects.requireNonNull(
 | 
			
		||||
                    (KeyboardConfig) requireArguments().getSerializable("config"));
 | 
			
		||||
 | 
			
		||||
            // Set up the input
 | 
			
		||||
            EditText editText = new EditText(YuzuApplication.getAppContext());
 | 
			
		||||
            editText.setHint(config.initial_text);
 | 
			
		||||
            editText.setSingleLine(!config.enable_return_button);
 | 
			
		||||
            editText.setLayoutParams(params);
 | 
			
		||||
            editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(config.max_text_length)});
 | 
			
		||||
 | 
			
		||||
            // Handle input type
 | 
			
		||||
            int input_type = 0;
 | 
			
		||||
            switch (config.type)
 | 
			
		||||
            {
 | 
			
		||||
                case SwkbdType.Normal:
 | 
			
		||||
                case SwkbdType.Qwerty:
 | 
			
		||||
                case SwkbdType.Unknown3:
 | 
			
		||||
                case SwkbdType.Latin:
 | 
			
		||||
                case SwkbdType.SimplifiedChinese:
 | 
			
		||||
                case SwkbdType.TraditionalChinese:
 | 
			
		||||
                case SwkbdType.Korean:
 | 
			
		||||
                default:
 | 
			
		||||
                    input_type = InputType.TYPE_CLASS_TEXT;
 | 
			
		||||
                    if (config.password_mode == SwkbdPasswordMode.Enabled)
 | 
			
		||||
                    {
 | 
			
		||||
                        input_type |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case SwkbdType.NumberPad:
 | 
			
		||||
                    input_type = InputType.TYPE_CLASS_NUMBER;
 | 
			
		||||
                    if (config.password_mode == SwkbdPasswordMode.Enabled)
 | 
			
		||||
                    {
 | 
			
		||||
                        input_type |= InputType.TYPE_NUMBER_VARIATION_PASSWORD;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Apply input type
 | 
			
		||||
            editText.setInputType(input_type);
 | 
			
		||||
 | 
			
		||||
            FrameLayout container = new FrameLayout(emulationActivity);
 | 
			
		||||
            container.addView(editText);
 | 
			
		||||
 | 
			
		||||
            String headerText = config.header_text.isEmpty() ? emulationActivity.getString(R.string.software_keyboard) : config.header_text;
 | 
			
		||||
            String okText = config.header_text.isEmpty() ? emulationActivity.getString(android.R.string.ok) : config.ok_text;
 | 
			
		||||
 | 
			
		||||
            MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
 | 
			
		||||
                    .setTitle(headerText)
 | 
			
		||||
                    .setView(container);
 | 
			
		||||
            setCancelable(false);
 | 
			
		||||
 | 
			
		||||
            builder.setPositiveButton(okText, null);
 | 
			
		||||
            builder.setNegativeButton(emulationActivity.getString(android.R.string.cancel), null);
 | 
			
		||||
 | 
			
		||||
            final AlertDialog dialog = builder.create();
 | 
			
		||||
            dialog.create();
 | 
			
		||||
            if (dialog.getButton(DialogInterface.BUTTON_POSITIVE) != null) {
 | 
			
		||||
                dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener((view) -> {
 | 
			
		||||
                    data.result = SwkbdResult.Ok;
 | 
			
		||||
                    data.text = editText.getText().toString();
 | 
			
		||||
                    dialog.dismiss();
 | 
			
		||||
 | 
			
		||||
                    synchronized (finishLock) {
 | 
			
		||||
                        finishLock.notifyAll();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            if (dialog.getButton(DialogInterface.BUTTON_NEUTRAL) != null) {
 | 
			
		||||
                dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener((view) -> {
 | 
			
		||||
                    data.result = SwkbdResult.Ok;
 | 
			
		||||
                    dialog.dismiss();
 | 
			
		||||
                    synchronized (finishLock) {
 | 
			
		||||
                        finishLock.notifyAll();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            if (dialog.getButton(DialogInterface.BUTTON_NEGATIVE) != null) {
 | 
			
		||||
                dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener((view) -> {
 | 
			
		||||
                    data.result = SwkbdResult.Cancel;
 | 
			
		||||
                    dialog.dismiss();
 | 
			
		||||
                    synchronized (finishLock) {
 | 
			
		||||
                        finishLock.notifyAll();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return dialog;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static KeyboardData data;
 | 
			
		||||
    private static final Object finishLock = new Object();
 | 
			
		||||
 | 
			
		||||
    private static void ExecuteNormalImpl(KeyboardConfig config) {
 | 
			
		||||
        final EmulationActivity emulationActivity = NativeLibrary.sEmulationActivity.get();
 | 
			
		||||
 | 
			
		||||
        data = new KeyboardData(SwkbdResult.Cancel, "");
 | 
			
		||||
 | 
			
		||||
        KeyboardDialogFragment fragment = KeyboardDialogFragment.newInstance(config);
 | 
			
		||||
        fragment.show(emulationActivity.getSupportFragmentManager(), "keyboard");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void ExecuteInlineImpl(KeyboardConfig config) {
 | 
			
		||||
        final EmulationActivity emulationActivity = NativeLibrary.sEmulationActivity.get();
 | 
			
		||||
 | 
			
		||||
        var overlayView = emulationActivity.findViewById(R.id.surface_input_overlay);
 | 
			
		||||
        InputMethodManager im = (InputMethodManager)overlayView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 | 
			
		||||
        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.
 | 
			
		||||
        final Handler handler = new Handler();
 | 
			
		||||
        final int delayMs = 500;
 | 
			
		||||
        handler.postDelayed(new Runnable() {
 | 
			
		||||
            public void run() {
 | 
			
		||||
                var insets = ViewCompat.getRootWindowInsets(overlayView);
 | 
			
		||||
                var isKeyboardVisible = insets.isVisible(WindowInsets.Type.ime());
 | 
			
		||||
                if (isKeyboardVisible) {
 | 
			
		||||
                    handler.postDelayed(this, delayMs);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // No longer visible, submit the result.
 | 
			
		||||
                NativeLibrary.SubmitInlineKeyboardInput(android.view.KeyEvent.KEYCODE_ENTER);
 | 
			
		||||
            }
 | 
			
		||||
        }, delayMs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static KeyboardData ExecuteNormal(KeyboardConfig config) {
 | 
			
		||||
        NativeLibrary.sEmulationActivity.get().runOnUiThread(() -> ExecuteNormalImpl(config));
 | 
			
		||||
 | 
			
		||||
        synchronized (finishLock) {
 | 
			
		||||
            try {
 | 
			
		||||
                finishLock.wait();
 | 
			
		||||
            } catch (Exception ignored) {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void ExecuteInline(KeyboardConfig config) {
 | 
			
		||||
        NativeLibrary.sEmulationActivity.get().runOnUiThread(() -> ExecuteInlineImpl(config));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void ShowError(String error) {
 | 
			
		||||
        NativeLibrary.displayAlertMsg(
 | 
			
		||||
                YuzuApplication.getAppContext().getResources().getString(R.string.software_keyboard),
 | 
			
		||||
                error, false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,117 @@
 | 
			
		||||
// 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.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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    data class KeyboardData(var result: Int, var text: String)
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,100 @@
 | 
			
		||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.applets.keyboard.ui
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.content.DialogInterface
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.text.InputFilter
 | 
			
		||||
import android.text.InputType
 | 
			
		||||
import androidx.fragment.app.DialogFragment
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.applets.keyboard.SoftwareKeyboard
 | 
			
		||||
import org.yuzu.yuzu_emu.applets.keyboard.SoftwareKeyboard.KeyboardConfig
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
 | 
			
		||||
 | 
			
		||||
class KeyboardDialogFragment : DialogFragment() {
 | 
			
		||||
    private lateinit var binding: DialogEditTextBinding
 | 
			
		||||
    private lateinit var config: KeyboardConfig
 | 
			
		||||
 | 
			
		||||
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
 | 
			
		||||
        binding = DialogEditTextBinding.inflate(layoutInflater)
 | 
			
		||||
        config = requireArguments().serializable(CONFIG)!!
 | 
			
		||||
 | 
			
		||||
        // Set up the input
 | 
			
		||||
        binding.editText.hint = config.initial_text
 | 
			
		||||
        binding.editText.isSingleLine = !config.enable_return_button
 | 
			
		||||
        binding.editText.filters =
 | 
			
		||||
            arrayOf<InputFilter>(InputFilter.LengthFilter(config.max_text_length))
 | 
			
		||||
 | 
			
		||||
        // Handle input type
 | 
			
		||||
        var inputType: Int
 | 
			
		||||
        when (config.type) {
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.Normal.ordinal,
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.Qwerty.ordinal,
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.Unknown3.ordinal,
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.Latin.ordinal,
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.SimplifiedChinese.ordinal,
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.TraditionalChinese.ordinal,
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.Korean.ordinal -> {
 | 
			
		||||
                inputType = InputType.TYPE_CLASS_TEXT
 | 
			
		||||
                if (config.password_mode == SoftwareKeyboard.SwkbdPasswordMode.Enabled.ordinal) {
 | 
			
		||||
                    inputType = inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            SoftwareKeyboard.SwkbdType.NumberPad.ordinal -> {
 | 
			
		||||
                inputType = InputType.TYPE_CLASS_NUMBER
 | 
			
		||||
                if (config.password_mode == SoftwareKeyboard.SwkbdPasswordMode.Enabled.ordinal) {
 | 
			
		||||
                    inputType = inputType or InputType.TYPE_NUMBER_VARIATION_PASSWORD
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                inputType = InputType.TYPE_CLASS_TEXT
 | 
			
		||||
                if (config.password_mode == SoftwareKeyboard.SwkbdPasswordMode.Enabled.ordinal) {
 | 
			
		||||
                    inputType = inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        binding.editText.inputType = inputType
 | 
			
		||||
 | 
			
		||||
        val headerText =
 | 
			
		||||
            config.header_text!!.ifEmpty { resources.getString(R.string.software_keyboard) }
 | 
			
		||||
        val okText =
 | 
			
		||||
            if (config.header_text!!.isEmpty()) resources.getString(android.R.string.ok) else config.ok_text!!
 | 
			
		||||
 | 
			
		||||
        return MaterialAlertDialogBuilder(requireContext())
 | 
			
		||||
            .setTitle(headerText)
 | 
			
		||||
            .setView(binding.root)
 | 
			
		||||
            .setPositiveButton(okText) { _, _ ->
 | 
			
		||||
                SoftwareKeyboard.data.result = SoftwareKeyboard.SwkbdResult.Ok.ordinal
 | 
			
		||||
                SoftwareKeyboard.data.text = binding.editText.text.toString()
 | 
			
		||||
            }
 | 
			
		||||
            .setNegativeButton(resources.getString(android.R.string.cancel)) { _, _ ->
 | 
			
		||||
                SoftwareKeyboard.data.result = SoftwareKeyboard.SwkbdResult.Cancel.ordinal
 | 
			
		||||
            }
 | 
			
		||||
            .create()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDismiss(dialog: DialogInterface) {
 | 
			
		||||
        super.onDismiss(dialog)
 | 
			
		||||
        synchronized(SoftwareKeyboard.dataLock) {
 | 
			
		||||
            SoftwareKeyboard.dataLock.notifyAll()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val TAG = "KeyboardDialogFragment"
 | 
			
		||||
        const val CONFIG = "keyboard_config"
 | 
			
		||||
 | 
			
		||||
        fun newInstance(config: KeyboardConfig?): KeyboardDialogFragment {
 | 
			
		||||
            val frag = KeyboardDialogFragment()
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putSerializable(CONFIG, config)
 | 
			
		||||
            frag.arguments = args
 | 
			
		||||
            return frag
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -253,19 +253,19 @@ void AndroidKeyboard::SubmitNormalText(const ResultData& data) const {
 | 
			
		||||
 | 
			
		||||
void InitJNI(JNIEnv* env) {
 | 
			
		||||
    s_software_keyboard_class = reinterpret_cast<jclass>(
 | 
			
		||||
        env->NewGlobalRef(env->FindClass("org/yuzu/yuzu_emu/applets/SoftwareKeyboard")));
 | 
			
		||||
        env->NewGlobalRef(env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard")));
 | 
			
		||||
    s_keyboard_config_class = reinterpret_cast<jclass>(env->NewGlobalRef(
 | 
			
		||||
        env->FindClass("org/yuzu/yuzu_emu/applets/SoftwareKeyboard$KeyboardConfig")));
 | 
			
		||||
        env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig")));
 | 
			
		||||
    s_keyboard_data_class = reinterpret_cast<jclass>(env->NewGlobalRef(
 | 
			
		||||
        env->FindClass("org/yuzu/yuzu_emu/applets/SoftwareKeyboard$KeyboardData")));
 | 
			
		||||
        env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardData")));
 | 
			
		||||
 | 
			
		||||
    s_swkbd_execute_normal = env->GetStaticMethodID(
 | 
			
		||||
        s_software_keyboard_class, "ExecuteNormal",
 | 
			
		||||
        "(Lorg/yuzu/yuzu_emu/applets/SoftwareKeyboard$KeyboardConfig;)Lorg/yuzu/yuzu_emu/"
 | 
			
		||||
        "applets/SoftwareKeyboard$KeyboardData;");
 | 
			
		||||
        s_software_keyboard_class, "executeNormal",
 | 
			
		||||
        "(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)Lorg/yuzu/yuzu_emu/"
 | 
			
		||||
        "applets/keyboard/SoftwareKeyboard$KeyboardData;");
 | 
			
		||||
    s_swkbd_execute_inline =
 | 
			
		||||
        env->GetStaticMethodID(s_software_keyboard_class, "ExecuteInline",
 | 
			
		||||
                               "(Lorg/yuzu/yuzu_emu/applets/SoftwareKeyboard$KeyboardConfig;)V");
 | 
			
		||||
        env->GetStaticMethodID(s_software_keyboard_class, "executeInline",
 | 
			
		||||
                               "(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)V");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CleanupJNI(JNIEnv* env) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								src/android/app/src/main/res/layout/dialog_edit_text.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/android/app/src/main/res/layout/dialog_edit_text.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<androidx.constraintlayout.widget.ConstraintLayout
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
    <com.google.android.material.textfield.TextInputLayout
 | 
			
		||||
        android:id="@+id/edit_text_layout"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_margin="24dp"
 | 
			
		||||
        app:layout_constraintTop_toTopOf="parent">
 | 
			
		||||
 | 
			
		||||
        <com.google.android.material.textfield.TextInputEditText
 | 
			
		||||
            android:id="@+id/edit_text"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:inputType="none" />
 | 
			
		||||
 | 
			
		||||
    </com.google.android.material.textfield.TextInputLayout>
 | 
			
		||||
 | 
			
		||||
</androidx.constraintlayout.widget.ConstraintLayout>
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user