From 36b4b451705c0334eab3bcb4578bcf1397b1f45b Mon Sep 17 00:00:00 2001 From: volzhs Date: Sat, 26 Nov 2016 23:30:46 +0900 Subject: [PATCH] Check keystore field when export Android release apk --- tools/editor/project_export.cpp | 209 ++++++++++++++++++++++++++++++++ tools/editor/project_export.h | 11 ++ tools/editor/property_editor.h | 2 + 3 files changed, 222 insertions(+) diff --git a/tools/editor/project_export.cpp b/tools/editor/project_export.cpp index 103962716b..f4f3959729 100644 --- a/tools/editor/project_export.cpp +++ b/tools/editor/project_export.cpp @@ -589,6 +589,11 @@ void ProjectExportDialog::custom_action(const String&) { return; } + if (platform.to_lower()=="android" && _check_android_setting(exporter)==false){ + // not filled all field for Android release + return; + } + String extension = exporter->get_binary_extension(); file_export_password->set_editable( exporter->requires_password(exporter->is_debugging_enabled()) ); @@ -602,6 +607,204 @@ void ProjectExportDialog::custom_action(const String&) { } +LineEdit* ProjectExportDialog::_create_keystore_input(Control* container, const String& p_label, const String& name) { + + HBoxContainer* hb=memnew(HBoxContainer); + Label* lb=memnew(Label); + LineEdit* input=memnew(LineEdit); + + lb->set_text(p_label); + lb->set_custom_minimum_size(Size2(140*EDSCALE,0)); + lb->set_align(Label::ALIGN_RIGHT); + + input->set_custom_minimum_size(Size2(170*EDSCALE,0)); + input->set_name(name); + + hb->add_constant_override("separation", 10*EDSCALE); + hb->add_child(lb); + hb->add_child(input); + container->add_child(hb); + + return input; + +} + +void ProjectExportDialog::_create_android_keystore_window() { + + keystore_file_dialog = memnew( EditorFileDialog ); + add_child(keystore_file_dialog); + keystore_file_dialog->set_mode(EditorFileDialog::MODE_OPEN_DIR); + keystore_file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + keystore_file_dialog->set_current_dir( "res://" ); + + keystore_file_dialog->set_title(TTR("Target Path:")); + keystore_file_dialog->connect("dir_selected", this,"_keystore_dir_selected"); + + keystore_create_dialog=memnew(ConfirmationDialog); + VBoxContainer* vb=memnew(VBoxContainer); + vb->set_size(Size2(340*EDSCALE,0)); + keystore_create_dialog->set_title(TTR("Create Android keystore")); + + _create_keystore_input(vb, TTR("Full name"), "name"); + _create_keystore_input(vb, TTR("Organizational unit"), "unit"); + _create_keystore_input(vb, TTR("Organization"), "org"); + _create_keystore_input(vb, TTR("City"), "city"); + _create_keystore_input(vb, TTR("State"), "state"); + _create_keystore_input(vb, TTR("2 letter country code"), "code"); + _create_keystore_input(vb, TTR("User alias"), "alias"); + LineEdit* pass=_create_keystore_input(vb, TTR("Password"), "pass"); + pass->set_placeholder(TTR("at least 6 characters")); + _create_keystore_input(vb, TTR("File name"), "file"); + + Label* lb_path=memnew(Label); + LineEdit* path=memnew(LineEdit); + Button* btn=memnew(Button); + HBoxContainer* hb=memnew(HBoxContainer); + + lb_path->set_text(TTR("Path : (better to save outside of project)")); + path->set_h_size_flags(SIZE_EXPAND_FILL); + path->set_name("path"); + btn->set_text(" .. "); + btn->connect("pressed", keystore_file_dialog, "popup_centered_ratio"); + + vb->add_spacer(); + vb->add_child(lb_path); + hb->add_child(path); + hb->add_child(btn); + vb->add_child(hb); + + keystore_create_dialog->add_child(vb); + keystore_create_dialog->set_child_rect(vb); + add_child(keystore_create_dialog); + + keystore_create_dialog->connect("confirmed", this, "_create_android_keystore"); + path->connect("text_changed", this, "_check_keystore_path"); + + confirm_keystore = memnew(ConfirmationDialog); + confirm_keystore->connect("confirmed", keystore_create_dialog, "popup_centered_minsize"); + add_child(confirm_keystore); + +} + +void ProjectExportDialog::_keystore_dir_selected(const String& path) { + + LineEdit* edit=keystore_create_dialog->find_node("path", true, false)->cast_to(); + edit->set_text(path.simplify_path()); + +} + +void ProjectExportDialog::_keystore_created() { + + if (error->is_connected("popup_hide", this, "_keystore_created")){ + error->disconnect("popup_hide", this, "_keystore_created"); + } + custom_action("export_pck"); + +} + +void ProjectExportDialog::_check_keystore_path(const String& path) { + + LineEdit* edit=keystore_create_dialog->find_node("path", true, false)->cast_to(); + bool exists = DirAccess::exists(path); + if (!exists) { + edit->add_color_override("font_color", Color(1,0,0,1)); + } else { + edit->add_color_override("font_color", Color(0,1,0,1)); + } + +} + +void ProjectExportDialog::_create_android_keystore() { + + Vector names=String("name,unit,org,city,state,code,alias,pass").split(","); + String path=keystore_create_dialog->find_node("path", true, false)->cast_to()->get_text(); + String file=keystore_create_dialog->find_node("file", true, false)->cast_to()->get_text(); + + if (file.ends_with(".keystore")==false) { + file+=".keystore"; + } + String fullpath=path.plus_file(file); + String info="CN=$name, OU=$unit, O=$org, L=$city, S=$state, C=$code"; + Dictionary dic; + + for (int i=0;ifind_node(names[i], true, false)->cast_to(); + dic[names[i]]=edit->get_text(); + info=info.replace("$"+names[i], edit->get_text()); + } + + String jarsigner=EditorSettings::get_singleton()->get("android/jarsigner"); + String keytool=jarsigner.get_base_dir().plus_file("keytool"); + String os_name=OS::get_singleton()->get_name(); + if (os_name.to_lower()=="windows") { + keytool+=".exe"; + } + + bool exist=FileAccess::exists(keytool); + if (!exist) { + error->set_text("Can't find 'keytool'"); + error->popup_centered_minsize(); + return; + } + + List args; + args.push_back("-genkey"); + args.push_back("-v"); + args.push_back("-keystore"); + args.push_back(fullpath); + args.push_back("-alias"); + args.push_back(dic["alias"]); + args.push_back("-storepass"); + args.push_back(dic["pass"]); + args.push_back("-keypass"); + args.push_back(dic["pass"]); + args.push_back("-keyalg"); + args.push_back("RSA"); + args.push_back("-keysize"); + args.push_back("2048"); + args.push_back("-validity"); + args.push_back("10000"); + args.push_back("-dname"); + args.push_back(info); + int retval; + OS::get_singleton()->execute(keytool,args,true,NULL,NULL,&retval); + + if (retval==0) { // success + platform_options->_edit_set("keystore/release", fullpath); + platform_options->_edit_set("keystore/release_user", dic["alias"]); + platform_options->_edit_set("keystore/release_password", dic["pass"]); + + error->set_text("Android keystore created at \n"+fullpath); + error->connect("popup_hide", this, "_keystore_created"); + error->popup_centered_minsize(); + } else { // fail + error->set_text("Fail to create android keystore at \n"+fullpath); + error->popup_centered_minsize(); + } + +} + +bool ProjectExportDialog::_check_android_setting(const Ref& exporter) { + + bool is_debugging = exporter->get("debug/debugging_enabled"); + String release = exporter->get("keystore/release"); + String user = exporter->get("keystore/release_user"); + String password = exporter->get("keystore/release_password"); + + if (!is_debugging && (release=="" || user=="" || password=="")){ + if (release==""){ + confirm_keystore->set_text(TTR("Release keystore is not set.\nDo you want to create one?")); + confirm_keystore->popup_centered_minsize(); + } else { + error->set_text(TTR("Fill Keystore/Release User and Release Password")); + error->popup_centered_minsize(); + } + return false; + } + + return true; + +} void ProjectExportDialog::_group_selected() { @@ -1123,6 +1326,10 @@ void ProjectExportDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("export_platform"),&ProjectExportDialog::export_platform); + ObjectTypeDB::bind_method(_MD("_create_android_keystore"),&ProjectExportDialog::_create_android_keystore); + ObjectTypeDB::bind_method(_MD("_check_keystore_path"),&ProjectExportDialog::_check_keystore_path); + ObjectTypeDB::bind_method(_MD("_keystore_dir_selected"),&ProjectExportDialog::_keystore_dir_selected); + ObjectTypeDB::bind_method(_MD("_keystore_created"),&ProjectExportDialog::_keystore_created); // ADD_SIGNAL(MethodInfo("instance")); @@ -1479,6 +1686,8 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) { ei="EditorIcons"; ot="Object"; pending_update_tree=true; + + _create_android_keystore_window(); } diff --git a/tools/editor/project_export.h b/tools/editor/project_export.h index 8cf2bf3afc..c749e04b95 100644 --- a/tools/editor/project_export.h +++ b/tools/editor/project_export.h @@ -73,6 +73,7 @@ private: bool pending_update_tree; AcceptDialog *error; ConfirmationDialog *confirm; + ConfirmationDialog *confirm_keystore; Button *button_reload; LineEdit *filters, *filters_exclude; @@ -145,6 +146,9 @@ private: SpinBox *sample_max_hz; CheckButton *sample_trim; + ConfirmationDialog* keystore_create_dialog; + EditorFileDialog* keystore_file_dialog; + void _export_mode_changed(int p_idx); void _prop_edited(String what); @@ -190,6 +194,13 @@ private: void _export_action_pck(const String& p_file); void ok_pressed(); void custom_action(const String&); + LineEdit* _create_keystore_input(Control* container, const String& p_label, const String& name); + void _create_android_keystore_window(); + void _create_android_keystore(); + bool _check_android_setting(const Ref& exporter); + void _check_keystore_path(const String& path); + void _keystore_dir_selected(const String& path); + void _keystore_created(); void _save_export_cfg(); void _format_toggled(); diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h index 3fe332bf87..cd9e754cb6 100644 --- a/tools/editor/property_editor.h +++ b/tools/editor/property_editor.h @@ -219,6 +219,8 @@ class PropertyEditor : public Control { void _edit_button(Object *p_item, int p_column, int p_button); void _node_removed(Node *p_node); + +friend class ProjectExportDialog; void _edit_set(const String& p_name, const Variant& p_value); void _draw_flags(Object *ti,const Rect2& p_rect);