diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index eb22512fe1..156ab3832c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -25,8 +25,21 @@ + + + + + + + + + + + + + diff --git a/android/src/io/anuke/mindustry/AndroidLauncher.java b/android/src/io/anuke/mindustry/AndroidLauncher.java index 436c734b4f..eb54d83eff 100644 --- a/android/src/io/anuke/mindustry/AndroidLauncher.java +++ b/android/src/io/anuke/mindustry/AndroidLauncher.java @@ -11,6 +11,7 @@ import android.telephony.TelephonyManager; import android.util.Log; import com.badlogic.gdx.backends.android.AndroidApplication; import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; +import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Base64Coder; import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.GooglePlayServicesNotAvailableException; @@ -19,23 +20,28 @@ import com.google.android.gms.security.ProviderInstaller; import io.anuke.kryonet.DefaultThreadImpl; import io.anuke.kryonet.KryoClient; import io.anuke.kryonet.KryoServer; -import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.core.Platform; +import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.ui.dialogs.FileChooser; import io.anuke.ucore.core.Settings; +import io.anuke.ucore.function.Consumer; import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.layout.Unit; import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.Locale; import java.util.Random; public class AndroidLauncher extends AndroidApplication{ + public static final int PERMISSION_REQUEST_CODE = 1; + boolean doubleScaleTablets = true; - int WRITE_REQUEST_CODE = 1; + FileChooser chooser; @Override protected void onCreate(Bundle savedInstanceState){ @@ -79,22 +85,7 @@ public class AndroidLauncher extends AndroidApplication{ @Override public void requestWritePerms() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED && - checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_EXTERNAL_STORAGE}, WRITE_REQUEST_CODE); - }else{ - if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_REQUEST_CODE); - } - - if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, WRITE_REQUEST_CODE); - } - } - } } @Override @@ -137,6 +128,34 @@ public class AndroidLauncher extends AndroidApplication{ return Base64Coder.decode(uuid); } } + + @Override + public void shareFile(FileHandle file){ + + } + + @Override + public void showFileChooser(String text, String content, Consumer cons, boolean open, String filetype) { + chooser = new FileChooser(text, file -> file.extension().equalsIgnoreCase(filetype), open, cons); + + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && + checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){ + chooser.show(); + chooser = null; + }else { + ArrayList perms = new ArrayList<>(); + + if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + } + + if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + perms.add(Manifest.permission.READ_EXTERNAL_STORAGE); + } + + requestPermissions(perms.toArray(new String[perms.size()]), PERMISSION_REQUEST_CODE); + } + } }; try { @@ -159,6 +178,19 @@ public class AndroidLauncher extends AndroidApplication{ initialize(new Mindustry(), config); } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + if(requestCode == PERMISSION_REQUEST_CODE){ + for(int i : grantResults){ + if(i != PackageManager.PERMISSION_GRANTED) return; + } + + if(chooser != null){ + chooser.show(); + } + } + } private boolean isPackageInstalled(String packagename) { try { diff --git a/core/src/io/anuke/mindustry/core/Platform.java b/core/src/io/anuke/mindustry/core/Platform.java index 6e7556f78d..ed5a7fef7b 100644 --- a/core/src/io/anuke/mindustry/core/Platform.java +++ b/core/src/io/anuke/mindustry/core/Platform.java @@ -9,7 +9,6 @@ import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.function.Consumer; import io.anuke.ucore.scene.ui.TextField; -import java.io.InputStream; import java.util.Date; import java.util.Locale; import java.util.Random; @@ -67,16 +66,14 @@ public abstract class Platform { public void shareFile(FileHandle file){} /**Download a file. Only used on GWT backend.*/ public void downloadFile(String name, byte[] bytes){} - /**Open a file chooser. Only used on GWT backend.*/ - public void openFile(Consumer cons){} /**Show a file chooser. Desktop only. * * @param text File chooser title text - * @param content Type of files to be loaded + * @param content Description of the type of files to be loaded * @param cons Selection listener - * @param open Whether to open or save files. - * @param filetype File extensions to filter. + * @param open Whether to open or save files + * @param filetype File extension to filter */ public void showFileChooser(String text, String content, Consumer cons, boolean open, String filetype){} /**Use the default thread provider from the kryonet module for this.*/ diff --git a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java index 90c9decd4e..1feaad8772 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java @@ -15,7 +15,6 @@ import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.io.MapMeta; import io.anuke.mindustry.io.MapTileData; -import io.anuke.mindustry.ui.dialogs.FileChooser; import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.ColorMapper; @@ -54,7 +53,6 @@ public class MapEditorDialog extends Dialog implements Disposable{ private MapResizeDialog resizeDialog; private ScrollPane pane; private FloatingDialog menu; - private FileChooser openFile, saveFile, openImage, saveImage; private boolean saved = false; private boolean shownWithMap = false; @@ -68,67 +66,6 @@ public class MapEditorDialog extends Dialog implements Disposable{ infoDialog = new MapInfoDialog(editor); - saveFile = new FileChooser("$text.saveimage", false, file -> { - file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension); - FileHandle result = file; - ui.loadAnd(() -> { - - try{ - if(!editor.getTags().containsKey("name")){ - editor.getTags().put("name", result.nameWithoutExtension()); - } - MapIO.writeMap(result.write(false), editor.getTags(), editor.getMap()); - }catch (Exception e){ - ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false))); - Log.err(e); - } - }); - }); - - openFile = new FileChooser("$text.loadimage", FileChooser.mapFilter, true, file -> { - ui.loadAnd(() -> { - try{ - DataInputStream stream = new DataInputStream(file.read()); - - MapMeta meta = MapIO.readMapMeta(stream); - MapTileData data = MapIO.readTileData(stream, meta, false); - - editor.beginEdit(data, meta.tags); - view.clearStack(); - }catch (Exception e){ - ui.showError(Bundles.format("text.editor.errorimageload", Strings.parseException(e, false))); - Log.err(e); - } - }); - }); - - saveImage = new FileChooser("$text.saveimage", false, file -> { - file = file.parent().child(file.nameWithoutExtension() + ".png"); - FileHandle result = file; - ui.loadAnd(() -> { - try{ - Pixmaps.write(MapIO.generatePixmap(editor.getMap()), result); - }catch (Exception e){ - ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false))); - Log.err(e); - } - }); - }); - - openImage = new FileChooser("$text.loadimage", FileChooser.pngFilter, true, file -> { - ui.loadAnd(() -> { - try{ - MapTileData data = MapIO.readPixmap(new Pixmap(file)); - - editor.beginEdit(data, editor.getTags()); - view.clearStack(); - }catch (Exception e){ - ui.showError(Bundles.format("text.editor.errorimageload", Strings.parseException(e, false))); - Log.err(e); - } - }); - }); - menu = new FloatingDialog("$text.menu"); menu.addCloseButton(); @@ -157,22 +94,79 @@ public class MapEditorDialog extends Dialog implements Disposable{ t.addImageTextButton("$text.editor.import", "icon-load-map", isize, () -> createDialog("$text.editor.import", "$text.editor.importmap", "$text.editor.importmap.description", "icon-load-map", (Listenable)loadDialog::show, - "$text.editor.importfile", "$text.editor.importfile.description", "icon-file", (Listenable)openFile::show, + "$text.editor.importfile", "$text.editor.importfile.description", "icon-file", (Listenable)() -> { + Platform.instance.showFileChooser("$text.loadimage", "Map Files", file -> { + ui.loadAnd(() -> { + try{ + DataInputStream stream = new DataInputStream(file.read()); + + MapMeta meta = MapIO.readMapMeta(stream); + MapTileData data = MapIO.readTileData(stream, meta, false); + + editor.beginEdit(data, meta.tags); + view.clearStack(); + }catch (Exception e){ + ui.showError(Bundles.format("text.editor.errorimageload", Strings.parseException(e, false))); + Log.err(e); + } + }); + }, true, mapExtension); + }, "$text.editor.importimage", "$text.editor.importimage.description", "icon-file-image", (Listenable)() -> { if(gwt){ ui.showError("text.web.unsupported"); }else { - openImage.show(); + Platform.instance.showFileChooser("$text.loadimage", "Image Files", file -> { + ui.loadAnd(() -> { + try{ + MapTileData data = MapIO.readPixmap(new Pixmap(file)); + + editor.beginEdit(data, editor.getTags()); + view.clearStack(); + }catch (Exception e){ + ui.showError(Bundles.format("text.editor.errorimageload", Strings.parseException(e, false))); + Log.err(e); + } + }); + }, true, "png"); } })); t.addImageTextButton("$text.editor.export", "icon-save-map", isize, () -> createDialog("$text.editor.export", - "$text.editor.exportfile", "$text.editor.exportfile.description", "icon-file", (Listenable)saveFile::show, + "$text.editor.exportfile", "$text.editor.exportfile.description", "icon-file", (Listenable)() -> { + Platform.instance.showFileChooser("$text.saveimage", "Map Files", file -> { + file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension); + FileHandle result = file; + ui.loadAnd(() -> { + + try{ + if(!editor.getTags().containsKey("name")){ + editor.getTags().put("name", result.nameWithoutExtension()); + } + MapIO.writeMap(result.write(false), editor.getTags(), editor.getMap()); + }catch (Exception e){ + ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false))); + Log.err(e); + } + }); + }, false, mapExtension); + }, "$text.editor.exportimage", "$text.editor.exportimage.description", "icon-file-image", (Listenable)() -> { if(gwt){ ui.showError("text.web.unsupported"); }else { - saveImage.show(); + Platform.instance.showFileChooser("$text.saveimage", "Image Files", file -> { + file = file.parent().child(file.nameWithoutExtension() + ".png"); + FileHandle result = file; + ui.loadAnd(() -> { + try{ + Pixmaps.write(MapIO.generatePixmap(editor.getMap()), result); + }catch (Exception e){ + ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false))); + Log.err(e); + } + }); + }, false, "png"); } })); @@ -374,6 +368,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ } public void build(){ + float size = 60; new table(){{ aleft(); @@ -387,7 +382,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ ButtonGroup group = new ButtonGroup<>(); int i = 1; - tools.defaults().size(60f, 64f).padBottom(-5.1f); + tools.defaults().size(size, size + 4f).padBottom(-5.1f); //tools.addImageButton("icon-back", 16*2, () -> hide()); @@ -432,7 +427,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ tools.table("button", t -> { t.add("$text.editor.teams"); - }).colspan(2).height(40).width(120f); + }).colspan(2).height(40).width(size*2f); tools.row(); @@ -456,13 +451,14 @@ public class MapEditorDialog extends Dialog implements Disposable{ row(); new table("button"){{ + atop(); Slider slider = new Slider(0, MapEditor.brushSizes.length-1, 1, false); slider.moved(f -> editor.setBrushSize(MapEditor.brushSizes[(int)(float)f])); new label("brush"); //new label(() -> Bundles.format("text.editor.brushsize", MapEditor.brushSizes[(int)slider.getValue()])).left(); row(); - add(slider).width(100f).padTop(4f); - }}.padBottom(10).padTop(5).top().end(); + add(slider).width(size*2f-20).padTop(4f); + }}.padTop(5).growY().top().end(); }}.left().growY().end(); diff --git a/core/src/io/anuke/mindustry/input/AndroidInput.java b/core/src/io/anuke/mindustry/input/AndroidInput.java index 570f759926..0cda261ec7 100644 --- a/core/src/io/anuke/mindustry/input/AndroidInput.java +++ b/core/src/io/anuke/mindustry/input/AndroidInput.java @@ -518,6 +518,8 @@ public class AndroidInput extends InputHandler implements GestureListener{ @Override public boolean pan(float x, float y, float deltaX, float deltaY){ + if(ui.hasMouse()) return false; + //can't pan in line mode with one finger! if(lineMode && !Gdx.input.isTouched(1)){ return false; diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java index f8c2fb2ca0..a50a555e19 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java @@ -245,7 +245,9 @@ public class BlocksFragment implements Fragment{ cati ++; } - group.getButtons().get(checkedi).setChecked(true); + if(group.getButtons().size > 0){ + group.getButtons().get(checkedi).setChecked(true); + } selectTable.row(); selectTable.add(stack).colspan(Category.values().length).padBottom(-5).height((size + 12)*maxrow); diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index 4c007bd4ad..72f8cd46bb 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -8,7 +8,6 @@ import io.anuke.kryonet.KryoServer; import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.net.Net; -import io.anuke.ucore.UCore; import io.anuke.ucore.util.OS; public class DesktopLauncher { @@ -22,7 +21,7 @@ public class DesktopLauncher { config.setWindowIcon("sprites/icon.png"); if(OS.isMac) { - config.setPreferencesConfig(UCore.getProperty("user.home") + "/Library/Application Support/Mindustry", FileType.Absolute); + config.setPreferencesConfig(OS.getAppDataDirectoryString("Mindustry"), FileType.Absolute); } Platform.instance = new DesktopPlatform(arg); diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java index 3dfb230c18..9af1a1c0d8 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java @@ -12,6 +12,7 @@ import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.ui.dialogs.FileChooser; import io.anuke.ucore.core.Settings; +import io.anuke.ucore.function.Consumer; import io.anuke.ucore.util.OS; import io.anuke.ucore.util.Strings; @@ -35,8 +36,6 @@ public class DesktopPlatform extends Platform { public DesktopPlatform(String[] args){ this.args = args; - - if(useDiscord) { DiscordEventHandlers handlers = new DiscordEventHandlers(); DiscordRPC.INSTANCE.Discord_Initialize(applicationId, handlers, true, ""); @@ -60,7 +59,7 @@ public class DesktopPlatform extends Platform { @Override public void showError(String text){ - //JOptionPane.showMessageDialog(null, text); + } @Override