Skip to content

Commit

Permalink
Add selection dialog (drop down/combo box) for Android
Browse files Browse the repository at this point in the history
The handling of IGUIComboBox uses the new setAndSendSelected() method.
getInputDialogState() is now inputDialogHasReturnValue() and returns whether the input dialog has a return value as a Boolean value.
New data types (enum) are added: GameActivity.DialogType (Java) and porting::AndroidDialogType (C++).
getLastDialogType() is added and returns current/last shown dialog's type.
showDialog()/showDialogUI() is now showTextDialog()/showTextDialogUI().


Co-authored-by: Gregor Parzefall <82708541+grorp@users.noreply.github.com>
  • Loading branch information
srifqi and grorp committed Oct 27, 2023
1 parent ddce858 commit 6f82468
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 63 deletions.
56 changes: 46 additions & 10 deletions android/app/src/main/java/net/minetest/minetest/GameActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ public class GameActivity extends NativeActivity {
System.loadLibrary("minetest");
}

private int messageReturnCode = -1;
enum DialogType { TEXT_INPUT, SELECTION_INPUT }

private DialogType lastDialogType = DialogType.TEXT_INPUT;
private boolean hasReturnValue = false;
private String messageReturnValue = "";
private int selectionReturnValue = 0;

@Override
public void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -85,11 +89,17 @@ public void onBackPressed() {
// Ignore the back press so Minetest can handle it
}

public void showDialog(String acceptButton, String hint, String current, int editType) {
runOnUiThread(() -> showDialogUI(hint, current, editType));
public void showTextDialog(String acceptButton, String hint, String current, int editType) {
runOnUiThread(() -> showTextDialogUI(hint, current, editType));
}

public void showSelectionDialog(String[] optionList, int selectedIdx) {
runOnUiThread(() -> showSelectionDialogUI(optionList, selectedIdx));
}

private void showDialogUI(String hint, String current, int editType) {
private void showTextDialogUI(String hint, String current, int editType) {
lastDialogType = DialogType.TEXT_INPUT;
hasReturnValue = false;
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
Expand All @@ -114,7 +124,7 @@ else if (editType == 3)
// For multi-line, do not submit the text after pressing Enter key
if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
messageReturnCode = 0;
hasReturnValue = true;
messageReturnValue = editText.getText().toString();
alertDialog.dismiss();
return true;
Expand All @@ -128,29 +138,55 @@ else if (editType == 3)
doneButton.setText(R.string.ime_dialog_done);
doneButton.setOnClickListener((view -> {
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
messageReturnCode = 0;
hasReturnValue = true;
messageReturnValue = editText.getText().toString();
alertDialog.dismiss();
}));
}
alertDialog.setOnCancelListener(dialog -> {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
hasReturnValue = true;
messageReturnValue = current;
messageReturnCode = -1;
});
alertDialog.show();
editText.requestFocusTryShow();
}

public int getDialogState() {
return messageReturnCode;
public void showSelectionDialogUI(String[] optionList, int selectedIdx) {
lastDialogType = DialogType.SELECTION_INPUT;
hasReturnValue = false;
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setSingleChoiceItems(optionList, selectedIdx, (dialog, selection) -> {
hasReturnValue = true;
selectionReturnValue = selection;
dialog.dismiss();
});
builder.setOnCancelListener(dialog -> {
hasReturnValue = true;
selectionReturnValue = selectedIdx;
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}

public int getLastDialogType() {
return lastDialogType.ordinal();
}

public boolean dialogHasReturnValue() {
return hasReturnValue;
}

public String getDialogValue() {
messageReturnCode = -1;
hasReturnValue = false;
return messageReturnValue;
}

public int getDialogSelection() {
hasReturnValue = false;
return selectionReturnValue;
}

public float getDensity() {
return getResources().getDisplayMetrics().density;
}
Expand Down
5 changes: 3 additions & 2 deletions src/client/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2226,7 +2226,7 @@ void Game::openConsole(float scale, const wchar_t *line)
assert(scale > 0.0f && scale <= 1.0f);

#ifdef __ANDROID__
porting::showInputDialog(gettext("ok"), "", "", 2);
porting::showTextDialog(gettext("ok"), "", "", 2);
m_android_chat_open = true;
#else
if (gui_chat_console->isOpenInhibited())
Expand All @@ -2242,7 +2242,8 @@ void Game::openConsole(float scale, const wchar_t *line)
#ifdef __ANDROID__
void Game::handleAndroidChatInput()
{
if (m_android_chat_open && porting::getInputDialogState() == 0) {
if (m_android_chat_open && porting::inputDialogHasReturnValue() &&
porting::getLastInputDialogType() == porting::TEXT_INPUT) {
std::string text = porting::getInputDialogValue();
client->typeChatMessage(utf8_to_wide(text));
m_android_chat_open = false;
Expand Down
46 changes: 27 additions & 19 deletions src/gui/guiFormSpecMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3479,9 +3479,7 @@ bool GUIFormSpecMenu::getAndroidUIInput()
if (!hasAndroidUIInput())
return false;

// still waiting
if (porting::getInputDialogState() == -1)
return true;
porting::AndroidDialogType dialog_type = porting::getLastInputDialogType();

std::string fieldname = m_jni_field_name;
m_jni_field_name.clear();
Expand All @@ -3492,26 +3490,36 @@ bool GUIFormSpecMenu::getAndroidUIInput()

IGUIElement *element = getElementFromId(field.fid, true);

if (!element || element->getType() != irr::gui::EGUIET_EDIT_BOX)
if (!element)
return false;

gui::IGUIEditBox *editbox = (gui::IGUIEditBox *)element;
std::string text = porting::getInputDialogValue();
editbox->setText(utf8_to_wide(text).c_str());
auto element_type = element->getType();
if (dialog_type == porting::TEXT_INPUT && element_type == irr::gui::EGUIET_EDIT_BOX) {
gui::IGUIEditBox *editbox = (gui::IGUIEditBox *)element;
std::string text = porting::getInputDialogValue();
editbox->setText(utf8_to_wide(text).c_str());

bool enter_after_edit = false;
auto iter = field_enter_after_edit.find(fieldname);
if (iter != field_enter_after_edit.end()) {
enter_after_edit = iter->second;
}
if (enter_after_edit && editbox->getParent()) {
SEvent enter;
enter.EventType = EET_GUI_EVENT;
enter.GUIEvent.Caller = editbox;
enter.GUIEvent.Element = nullptr;
enter.GUIEvent.EventType = gui::EGET_EDITBOX_ENTER;
editbox->getParent()->OnEvent(enter);
bool enter_after_edit = false;
auto iter = field_enter_after_edit.find(fieldname);
if (iter != field_enter_after_edit.end()) {
enter_after_edit = iter->second;
}
if (enter_after_edit && editbox->getParent()) {
SEvent enter;
enter.EventType = EET_GUI_EVENT;
enter.GUIEvent.Caller = editbox;
enter.GUIEvent.Element = nullptr;
enter.GUIEvent.EventType = gui::EGET_EDITBOX_ENTER;
editbox->getParent()->OnEvent(enter);
}
} else if (dialog_type == porting::SELECTION_INPUT &&
element_type == irr::gui::EGUIET_COMBO_BOX) {
auto dropdown = (gui::IGUIComboBox *) element;
int selected = porting::getInputDialogSelection();
dropdown->setAndSendSelected(selected);
}

return false;
}
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions src/gui/guiPasswordChange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,9 @@ bool GUIPasswordChange::getAndroidUIInput()
if (!hasAndroidUIInput())
return false;

// still waiting
if (porting::getInputDialogState() == -1)
return true;
// has to be a text input
if (porting::getLastInputDialogType() != porting::TEXT_INPUT)
return false;

gui::IGUIElement *e = nullptr;
if (m_jni_field_name == "old_password")
Expand Down
39 changes: 26 additions & 13 deletions src/gui/modalMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,34 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
if (((gui::IGUIEditBox *)hovered)->isPasswordBox())
type = 3;

porting::showInputDialog(gettext("OK"), "",
wide_to_utf8(((gui::IGUIEditBox *)hovered)->getText()), type);
porting::showTextDialog(gettext("OK"), "",
wide_to_utf8(((gui::IGUIEditBox *) hovered)->getText()), type);
return retval;
}
}

if (event.EventType == EET_GUI_EVENT) {
if (event.GUIEvent.EventType == gui::EGET_LISTBOX_OPENED) {
gui::IGUIComboBox *dropdown = (gui::IGUIComboBox *) event.GUIEvent.Caller;

std::string field_name = getNameByID(dropdown->getID());
if (field_name.empty())
return false;

m_jni_field_name = field_name;

s32 selected_idx = dropdown->getSelected();
s32 option_size = dropdown->getItemCount();
std::string list_of_options[option_size];

for (s32 i = 0; i < option_size; i++) {
list_of_options[i] = wide_to_utf8(dropdown->getItem(i));
}

porting::showComboBoxDialog(list_of_options, option_size, selected_idx);
return true; // Prevent the Irrlicht dropdown from opening.
}
}
#endif

#ifdef HAVE_TOUCHSCREENGUI
Expand Down Expand Up @@ -345,16 +368,6 @@ bool GUIModalMenu::hasAndroidUIInput()
if (m_jni_field_name.empty())
return false;

// still waiting
if (porting::getInputDialogState() == -1)
return true;

// no value abort dialog processing
if (porting::getInputDialogState() != 0) {
m_jni_field_name.clear();
return false;
}

return true;
return porting::inputDialogHasReturnValue();
}
#endif
60 changes: 51 additions & 9 deletions src/porting_android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,14 @@ void initializePathsAndroid()
}
}

void showInputDialog(const std::string &acceptButton, const std::string &hint,
void showTextDialog(const std::string &acceptButton, const std::string &hint,
const std::string &current, int editType)
{
jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showDialog",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showTextDialog",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");

FATAL_ERROR_IF(showdialog == nullptr,
"porting::showInputDialog unable to find Java showDialog method");
"porting::showTextDialog unable to find Java showTextDialog method");

jstring jacceptButton = jnienv->NewStringUTF(acceptButton.c_str());
jstring jhint = jnienv->NewStringUTF(hint.c_str());
Expand All @@ -183,6 +183,27 @@ void showInputDialog(const std::string &acceptButton, const std::string &hint,
jacceptButton, jhint, jcurrent, jeditType);
}

void showComboBoxDialog(const std::string optionList[], s32 listSize, s32 selectedIdx)
{
jmethodID showdialog = jnienv->GetMethodID(nativeActivity, "showSelectionDialog",
"([Ljava/lang/String;I)V");

FATAL_ERROR_IF(showdialog == nullptr,
"porting::showComboBoxDialog unable to find Java showSelectionDialog method");

jclass jStringClass = jnienv->FindClass("java/lang/String");
jobjectArray jOptionList = jnienv->NewObjectArray(listSize, jStringClass, NULL);
jint jselectedIdx = selectedIdx;

for (s32 i = 0; i < listSize; i ++) {
jnienv->SetObjectArrayElement(jOptionList, i,
jnienv->NewStringUTF(optionList[i].c_str()));
}

jnienv->CallVoidMethod(app_global->activity->clazz, showdialog, jOptionList,
jselectedIdx);
}

void openURIAndroid(const std::string &url)
{
jmethodID url_open = jnienv->GetMethodID(nativeActivity, "openURI",
Expand All @@ -207,15 +228,26 @@ void shareFileAndroid(const std::string &path)
jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl);
}

int getInputDialogState()
AndroidDialogType getLastInputDialogType()
{
jmethodID lastdialogtype = jnienv->GetMethodID(nativeActivity,
"getLastDialogType", "()I");

FATAL_ERROR_IF(lastdialogtype == nullptr,
"porting::getLastInputDialogType unable to find Java getLastDialogType method");

int dialogType = jnienv->CallIntMethod(app_global->activity->clazz, lastdialogtype);
return static_cast<AndroidDialogType>(dialogType);
}

bool inputDialogHasReturnValue()
{
jmethodID dialogstate = jnienv->GetMethodID(nativeActivity,
"getDialogState", "()I");
jmethodID dialogstate = jnienv->GetMethodID(nativeActivity, "dialogHasReturnValue", "()Z");

FATAL_ERROR_IF(dialogstate == nullptr,
"porting::getInputDialogState unable to find Java getDialogState method");
"porting::inputDialogHasReturnValue unable to find Java dialogHasReturnValue method");

return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate);
return jnienv->CallBooleanMethod(app_global->activity->clazz, dialogstate);
}

std::string getInputDialogValue()
Expand All @@ -231,6 +263,16 @@ std::string getInputDialogValue()
return readJavaString((jstring) result);
}

int getInputDialogSelection()
{
jmethodID dialogvalue = jnienv->GetMethodID(nativeActivity, "getDialogSelection", "()I");

FATAL_ERROR_IF(dialogvalue == nullptr,
"porting::getInputDialogSelection unable to find Java getDialogSelection method");

return jnienv->CallIntMethod(app_global->activity->clazz, dialogvalue);
}

#ifndef SERVER
float getDisplayDensity()
{
Expand Down

0 comments on commit 6f82468

Please sign in to comment.