diff --git a/SPD-classes/src/main/java/com/watabou/utils/Bundle.java b/SPD-classes/src/main/java/com/watabou/utils/Bundle.java index 4361e6c43..71110899c 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/Bundle.java +++ b/SPD-classes/src/main/java/com/watabou/utils/Bundle.java @@ -28,6 +28,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; @@ -35,7 +36,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.PushbackInputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -45,6 +45,8 @@ import java.util.zip.GZIPOutputStream; public class Bundle { private static final String CLASS_NAME = "__className"; + + public static final String DEFAULT_KEY = "key"; private static HashMap aliases = new HashMap<>(); @@ -223,6 +225,25 @@ public class Bundle { return null; } } + + public Bundle[] getBundleArray(){ + return getBundleArray( DEFAULT_KEY ); + } + + public Bundle[] getBundleArray( String key ){ + try { + JSONArray array = data.getJSONArray( key ); + int length = array.length(); + Bundle[] result = new Bundle[length]; + for (int i=0; i < length; i++) { + result[i] = new Bundle( array.getJSONObject( i ) ); + } + return result; + } catch (JSONException e) { + Game.reportException(e); + return null; + } + } public Collection getCollection( String key ) { @@ -410,22 +431,32 @@ public class Bundle { public static Bundle read( InputStream stream ) throws IOException { try { - BufferedReader reader; + if (!stream.markSupported()){ + stream = new BufferedInputStream( stream, 2 ); + } //determines if we're reading a regular, or compressed file - PushbackInputStream pb = new PushbackInputStream( stream, 2 ); + stream.mark( 2 ); byte[] header = new byte[2]; - pb.unread(header, 0, pb.read(header)); + stream.read( header ); + stream.reset(); + //GZIP header is 0x1f8b - if( header[ 0 ] == (byte) 0x1f && header[ 1 ] == (byte) 0x8b ) - reader = new BufferedReader( new InputStreamReader( new GZIPInputStream( pb, GZIP_BUFFER ) ) ); - else - reader = new BufferedReader( new InputStreamReader( pb ) ); + if( header[ 0 ] == (byte) 0x1f && header[ 1 ] == (byte) 0x8b ) { + stream = new GZIPInputStream( stream, GZIP_BUFFER ); + } - JSONObject json = (JSONObject)new JSONTokener( reader.readLine() ).nextValue(); + //cannot just tokenize the stream directly as that constructor doesn't exist on Android + BufferedReader reader = new BufferedReader( new InputStreamReader( stream )); + Object json = new JSONTokener( reader.readLine() ).nextValue(); reader.close(); - return new Bundle( json ); + //if the data is an array, put it in a fresh object with the default key + if (json instanceof JSONArray){ + json = new JSONObject().put( DEFAULT_KEY, json ); + } + + return new Bundle( (JSONObject) json ); } catch (Exception e) { Game.reportException(e); throw new IOException(); diff --git a/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java b/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java index 15a4cf170..673f60fbf 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java +++ b/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java @@ -28,6 +28,8 @@ public abstract class PlatformSupport { public abstract void updateDisplaySize(); public abstract void updateSystemUI(); + + public abstract boolean connectedToUnmeteredNetwork(); //FIXME this is currently used because no platform-agnostic text input has been implemented. //should look into doing that using either plain openGL or Libgdx's libraries diff --git a/android/build.gradle b/android/build.gradle index 07313255f..994d057bb 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,6 +25,9 @@ android { debug { applicationIdSuffix ".indev" versionNameSuffix '-INDEV' + dependencies { + debugImplementation project(':services:updates:debugUpdates') + } } release { @@ -35,6 +38,10 @@ android { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + + dependencies { + releaseImplementation project(':services:updates:githubUpdates') + } } } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index c162fa2cb..2664cc4a3 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -6,6 +6,8 @@ + + diff --git a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidGame.java b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidGame.java index 379ab5efc..e57054e45 100644 --- a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidGame.java +++ b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidGame.java @@ -32,6 +32,8 @@ import com.badlogic.gdx.backends.android.AndroidApplication; import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; +import com.shatteredpixel.shatteredpixeldungeon.services.updates.UpdateImpl; +import com.shatteredpixel.shatteredpixeldungeon.services.updates.Updates; import com.watabou.noosa.Game; import com.watabou.utils.FileUtils; @@ -58,9 +60,13 @@ public class AndroidGame extends AndroidApplication { } catch (PackageManager.NameNotFoundException e) { Game.versionCode = 0; } + + if (UpdateImpl.supportsUpdates()){ + Updates.service = UpdateImpl.getUpdateService(); + } FileUtils.setDefaultFileProperties( Files.FileType.Local, "" ); - + // grab preferences directly using our instance first // so that we don't need to rely on Gdx.app, which isn't initialized yet. SPDSettings.set(instance.getPreferences("ShatteredPixelDungeon")); @@ -91,7 +97,7 @@ public class AndroidGame extends AndroidApplication { initialize(new ShatteredPixelDungeon(support), config); view = (GLSurfaceView)graphics.getView(); - + } @Override diff --git a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java index f84b56c05..aa16353df 100644 --- a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java +++ b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java @@ -22,7 +22,10 @@ package com.shatteredpixel.shatteredpixeldungeon.android; import android.annotation.SuppressLint; +import android.content.Context; import android.content.pm.ActivityInfo; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Build; import android.view.View; import android.view.WindowManager; @@ -140,6 +143,23 @@ public class AndroidPlatformSupport extends PlatformSupport { } + @Override + @SuppressWarnings("deprecation") + public boolean connectedToUnmeteredNetwork() { + //Returns true if using unmetered connection, use shortcut method if available + ConnectivityManager cm = (ConnectivityManager) AndroidGame.instance.getSystemService(Context.CONNECTIVITY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ + return !cm.isActiveNetworkMetered(); + } else { + NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); + return activeNetwork != null && activeNetwork.isConnectedOrConnecting() && + (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI + || activeNetwork.getType() == ConnectivityManager.TYPE_WIMAX + || activeNetwork.getType() == ConnectivityManager.TYPE_BLUETOOTH + || activeNetwork.getType() == ConnectivityManager.TYPE_ETHERNET); + } + } + @Override public void promptTextInput(final String title, final String hintText, final int maxLen, final boolean multiLine, final String posTxt, final String negTxt, final TextCallback callback) { Game.runOnRenderThread( new Callback() { diff --git a/core/build.gradle b/core/build.gradle index 38ebe7568..2ef02fa66 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -5,4 +5,6 @@ sourceCompatibility = targetCompatibility = appJavaCompatibility dependencies { api project(':SPD-classes') + //TODO might be nice to remove this, should decide + implementation project(':services') } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java index efbb986b1..32d9a6bc1 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java @@ -34,6 +34,7 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.LanguageButton; import com.shatteredpixel.shatteredpixeldungeon.ui.PrefsButton; import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton; +import com.shatteredpixel.shatteredpixeldungeon.ui.UpdateNotification; import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; import com.shatteredpixel.shatteredpixeldungeon.windows.WndStartGame; import com.watabou.glwrap.Blending; @@ -225,6 +226,10 @@ public class TitleScene extends PixelScene { btnExit.setPos( w - btnExit.width(), 0 ); add( btnExit ); + UpdateNotification updInfo = new UpdateNotification(); + updInfo.setRect(4, h-BTN_HEIGHT, updInfo.reqWidth() + 6, BTN_HEIGHT-4); + add(updInfo); + fadeIn(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/Updates.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/Updates.java new file mode 100644 index 000000000..1137f78f1 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/Updates.java @@ -0,0 +1,69 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.services.updates; + +public class Updates { + + public static UpdateService service; + + public static boolean supportsUpdates(){ + return service != null; + } + + private static boolean updateChecked = false; + + public static void checkForUpdate(){ + if (!supportsUpdates() || updateChecked) return; + service.checkForUpdate(new UpdateService.UpdateResultCallback() { + @Override + public void onUpdateAvailable(AvailableUpdateData update) { + updateChecked = true; + updateData = update; + } + + @Override + public void onNoUpdateFound() { + updateChecked = true; + } + + @Override + public void onConnectionFailed() { + updateChecked = false; + } + }); + } + + public static void launchUpdate( AvailableUpdateData data ){ + service.initializeUpdate( data ); + } + + private static AvailableUpdateData updateData = null; + + public static boolean updateAvailable(){ + return updateData != null; + } + + public static AvailableUpdateData updateData(){ + return updateData; + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/UpdateNotification.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/UpdateNotification.java new file mode 100644 index 000000000..a013a35e3 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/UpdateNotification.java @@ -0,0 +1,79 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.ui; + +import com.shatteredpixel.shatteredpixeldungeon.Chrome; +import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.services.updates.AvailableUpdateData; +import com.shatteredpixel.shatteredpixeldungeon.services.updates.Updates; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; +import com.watabou.noosa.Game; + +public class UpdateNotification extends StyledButton { + + private static AvailableUpdateData update; + + public UpdateNotification(){ + super( Chrome.Type.GREY_BUTTON_TR, Messages.get(UpdateNotification.class, "title") ); + textColor( Window.SHPX_COLOR ); + visible = false; + Updates.checkForUpdate(); + } + + @Override + public void update() { + super.update(); + + if (Updates.updateAvailable()){ + bg.alpha((float) (0.7f + Math.sin(Game.timeTotal*2)*0.3f)); + text.alpha((float) (0.7f + Math.sin(Game.timeTotal*2)*0.3f)); + visible = true; + } else { + visible = false; + } + + } + + @Override + protected void onClick() { + update = Updates.updateData(); + ShatteredPixelDungeon.scene().addToFront( new WndUpdate() ); + } + + public static class WndUpdate extends WndOptions { + + public WndUpdate(){ + super( + update.versionName == null ? Messages.get(WndUpdate.class,"title") : Messages.get(WndUpdate.class,"versioned_title", update.versionName), + update.desc == null ? Messages.get(WndUpdate.class,"desc") : update.desc, + Messages.get(WndUpdate.class,"button")); + } + + @Override + protected void onSelect(int index) { + if (index == 0) { + Updates.launchUpdate(update); + } + } + } +} diff --git a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/ui/ui.properties b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/ui/ui.properties index c863c0538..acca3a1d4 100644 --- a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/ui/ui.properties +++ b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/ui/ui.properties @@ -1,3 +1,9 @@ ui.quickslotbutton.select_item=Quickslot an item ui.toolbar.examine_prompt=Press again to search\nPress a tile to examine + +ui.updatenotification.title=Update +ui.updatenotification$wndupdate.title=An Update is Available! +ui.updatenotification$wndupdate.versioned_title=Update Available: %s +ui.updatenotification$wndupdate.desc=Shattered Pixel Dungeon is regularly updated with overhauls to existing game content, or entirely new content!\n\nGame balance is also frequently improved in game updates, so that specific items/heroes/enemies aren't too strong or too weak.\n\nUpdates also include fixes for bugs and other various stability improvements. +ui.updatenotification$wndupdate.button=Go To Update Page \ No newline at end of file diff --git a/desktop/build.gradle b/desktop/build.gradle index 8cc9999a9..ab82868b4 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -20,6 +20,10 @@ task debug(dependsOn: classes, type: JavaExec) { systemProperty 'Specification-Title', appName systemProperty 'Specification-Version', appVersionName + "-INDEV" systemProperty 'Implementation-Version', appVersionCode + + dependencies { + debugImplementation project(':services:updates:debugUpdates') + } } //need a separate task to compile dependencies first, seeing as we're setting them up in an odd way @@ -38,6 +42,10 @@ task release(dependsOn: compileForRelease, type: Jar) { attributes 'Specification-Version': appVersionName attributes 'Implementation-Version': appVersionCode } + + dependencies { + releaseImplementation project(':services:updates:githubUpdates') + } } dependencies { @@ -47,4 +55,8 @@ dependencies { implementation "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" implementation "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" + + //Need these at compile time to prevent errors there. + // The actual dependency used at runtime will vary based on source set. + compileOnly project(':services:updates:debugUpdates') } \ No newline at end of file diff --git a/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopLauncher.java b/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopLauncher.java index 3961c6b1b..77cea7157 100644 --- a/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopLauncher.java +++ b/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopLauncher.java @@ -29,6 +29,8 @@ import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Preferences; import com.badlogic.gdx.utils.SharedLibraryLoader; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; +import com.shatteredpixel.shatteredpixeldungeon.services.updates.UpdateImpl; +import com.shatteredpixel.shatteredpixeldungeon.services.updates.Updates; import com.watabou.noosa.Game; import com.watabou.utils.FileUtils; import com.watabou.utils.Point; @@ -79,6 +81,10 @@ public class DesktopLauncher { } catch (NumberFormatException e) { Game.versionCode = Integer.parseInt(System.getProperty("Implementation-Version")); } + + if (UpdateImpl.supportsUpdates()){ + Updates.service = UpdateImpl.getUpdateService(); + } Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); diff --git a/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java b/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java index 0b4e57d46..76817a530 100644 --- a/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java +++ b/desktop/src/main/java/com/shatteredpixel/shatteredpixeldungeon/desktop/DesktopPlatformSupport.java @@ -58,6 +58,11 @@ public class DesktopPlatformSupport extends PlatformSupport { } ); } + @Override + public boolean connectedToUnmeteredNetwork() { + return true; //no easy way to check this in desktop, just assume user doesn't care + } + @Override public void promptTextInput(String title, String hintText, int maxLen, boolean multiLine, String posTxt, String negTxt, TextCallback callback) { diff --git a/services/build.gradle b/services/build.gradle new file mode 100644 index 000000000..e94412de9 --- /dev/null +++ b/services/build.gradle @@ -0,0 +1,4 @@ +apply plugin: 'java-library' + +[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' +sourceCompatibility = targetCompatibility = appJavaCompatibility \ No newline at end of file diff --git a/services/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/AvailableUpdateData.java b/services/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/AvailableUpdateData.java new file mode 100644 index 000000000..104026916 --- /dev/null +++ b/services/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/AvailableUpdateData.java @@ -0,0 +1,33 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.services.updates; + +public class AvailableUpdateData { + + public String versionName; + public int versionCode; + + public String desc; + + public String URL; + +} diff --git a/services/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateService.java b/services/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateService.java new file mode 100644 index 000000000..a7f01d2c8 --- /dev/null +++ b/services/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateService.java @@ -0,0 +1,36 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.services.updates; + +public abstract class UpdateService { + + public static abstract class UpdateResultCallback { + public abstract void onUpdateAvailable( AvailableUpdateData update ); + public abstract void onNoUpdateFound(); + public abstract void onConnectionFailed(); + } + + public abstract void checkForUpdate( UpdateResultCallback callback ); + + public abstract void initializeUpdate( AvailableUpdateData update ); + +} diff --git a/services/updates/debugUpdates/build.gradle b/services/updates/debugUpdates/build.gradle new file mode 100644 index 000000000..e83a9603e --- /dev/null +++ b/services/updates/debugUpdates/build.gradle @@ -0,0 +1,9 @@ +apply plugin: 'java-library' + +[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' +sourceCompatibility = targetCompatibility = appJavaCompatibility + +dependencies { + implementation project(':SPD-classes') + api project(':services') +} diff --git a/services/updates/debugUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/DebugUpdates.java b/services/updates/debugUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/DebugUpdates.java new file mode 100644 index 000000000..0c0c5889c --- /dev/null +++ b/services/updates/debugUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/DebugUpdates.java @@ -0,0 +1,55 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.services.updates; + + +import com.watabou.noosa.Game; +import com.watabou.utils.DeviceCompat; + +public class DebugUpdates extends UpdateService { + + private static AvailableUpdateData debugUpdateInfo; + + @Override + public void checkForUpdate(UpdateResultCallback callback) { + + //turn on to test update UI + if (false){ + debugUpdateInfo = new AvailableUpdateData(); + debugUpdateInfo.versionCode = Game.versionCode+1; + debugUpdateInfo.URL = "http://www.google.com"; + + callback.onUpdateAvailable(debugUpdateInfo); + } else { + debugUpdateInfo = null; + + callback.onNoUpdateFound(); + } + + } + + @Override + public void initializeUpdate(AvailableUpdateData update) { + DeviceCompat.openURI( update.URL ); + } + +} diff --git a/services/updates/debugUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateImpl.java b/services/updates/debugUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateImpl.java new file mode 100644 index 000000000..3764de9e8 --- /dev/null +++ b/services/updates/debugUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateImpl.java @@ -0,0 +1,36 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.services.updates; + +public class UpdateImpl { + + private static UpdateService updateChecker = new DebugUpdates(); + + public static UpdateService getUpdateService(){ + return updateChecker; + } + + public static boolean supportsUpdates(){ + return true; + } + +} diff --git a/services/updates/githubUpdates/build.gradle b/services/updates/githubUpdates/build.gradle new file mode 100644 index 000000000..36293c71f --- /dev/null +++ b/services/updates/githubUpdates/build.gradle @@ -0,0 +1,9 @@ +apply plugin: 'java-library' + +[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' +sourceCompatibility = targetCompatibility = appJavaCompatibility + +dependencies { + implementation project(':SPD-classes') + api project(':services') +} \ No newline at end of file diff --git a/services/updates/githubUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/GitHubUpdates.java b/services/updates/githubUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/GitHubUpdates.java new file mode 100644 index 000000000..a649fc581 --- /dev/null +++ b/services/updates/githubUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/GitHubUpdates.java @@ -0,0 +1,122 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.services.updates; + + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Net; +import com.watabou.noosa.Game; +import com.watabou.utils.Bundle; +import com.watabou.utils.DeviceCompat; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.ssl.SSLProtocolException; + +public class GitHubUpdates extends UpdateService { + + private static Pattern descPattern = Pattern.compile("(.*?)(\r\n|\n|\r)(\r\n|\n|\r)---", Pattern.DOTALL + Pattern.MULTILINE); + private static Pattern versionCodePattern = Pattern.compile("internal version number: ([0-9]*)", Pattern.CASE_INSENSITIVE); + + @Override + public void checkForUpdate(UpdateResultCallback callback) { + + if (!Game.platform.connectedToUnmeteredNetwork()){ + callback.onConnectionFailed(); + } + + Net.HttpRequest httpGet = new Net.HttpRequest(Net.HttpMethods.GET); + httpGet.setUrl("https://api.github.com/repos/00-Evan/shattered-pixel-dungeon/releases"); + httpGet.setHeader("Accept", "application/vnd.github.v3+json"); + + Gdx.net.sendHttpRequest(httpGet, new Net.HttpResponseListener() { + @Override + public void handleHttpResponse(Net.HttpResponse httpResponse) { + try { + Bundle latestRelease = null; + int latestVersionCode = Game.versionCode; + + boolean includePrereleases = Game.version.toLowerCase().contains("beta"); + + for (Bundle b : Bundle.read( httpResponse.getResultAsStream() ).getBundleArray()){ + Matcher m = versionCodePattern.matcher(b.getString("body")); + + if (m.find()){ + int releaseVersion = Integer.parseInt(m.group(1)); + if (releaseVersion > latestVersionCode + && (includePrereleases || !b.getBoolean("prerelease"))){ + latestRelease = b; + latestVersionCode = releaseVersion; + } + } + + } + + if (latestRelease == null){ + callback.onNoUpdateFound(); + } else { + + AvailableUpdateData update = new AvailableUpdateData(); + + update.versionName = latestRelease.getString("name"); + update.versionCode = latestVersionCode; + Matcher m = descPattern.matcher(latestRelease.getString("body")); + m.find(); + update.desc = m.group(1); + update.URL = latestRelease.getString("html_url"); + + callback.onUpdateAvailable(update); + } + } catch (Exception e) { + Game.reportException( e ); + callback.onConnectionFailed(); + } + } + + @Override + public void failed(Throwable t) { + //Failure in SSL handshake, possibly because GitHub requires TLS 1.2+. + // Often happens for old OS versions with outdated security protocols. + // Future update attempts won't work anyway, so just pretend nothing was found. + if (t instanceof SSLProtocolException){ + callback.onNoUpdateFound(); + } else { + Game.reportException(t); + callback.onConnectionFailed(); + } + } + + @Override + public void cancelled() { + callback.onConnectionFailed(); + } + }); + + } + + @Override + public void initializeUpdate(AvailableUpdateData update) { + DeviceCompat.openURI( update.URL ); + } + +} diff --git a/services/updates/githubUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateImpl.java b/services/updates/githubUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateImpl.java new file mode 100644 index 000000000..626c99131 --- /dev/null +++ b/services/updates/githubUpdates/src/main/java/com/shatteredpixel/shatteredpixeldungeon/services/updates/UpdateImpl.java @@ -0,0 +1,36 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2019 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.services.updates; + +public class UpdateImpl { + + private static UpdateService updateChecker = new GitHubUpdates(); + + public static UpdateService getUpdateService(){ + return updateChecker; + } + + public static boolean supportsUpdates(){ + return true; + } + +} diff --git a/settings.gradle b/settings.gradle index 3723f1661..8c9a768e1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ -include ':core', ':SPD-classes', ':android', ':desktop' \ No newline at end of file +include ':core', ':SPD-classes', ':android', ':desktop', ':services', + ':services:updates:debugUpdates', ':services:updates:githubUpdates' \ No newline at end of file