mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 17:26:01 +08:00
Implement loadFromHttp
This commit is contained in:
parent
e30dd8956e
commit
6729f8bbce
@ -2,6 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
|||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.esotericsoftware.spine.android.SpineController
|
import com.esotericsoftware.spine.android.SpineController
|
||||||
import com.esotericsoftware.spine.android.SpineView
|
import com.esotericsoftware.spine.android.SpineView
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -44,6 +46,15 @@ fun SimpleAnimation(nav: NavHostController) {
|
|||||||
it.animationState.setAnimation(0, "walk", true)
|
it.animationState.setAnimation(0, "walk", true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// SpineView.loadFromHttp(
|
||||||
|
// URL("https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/4.2/examples/spineboy/export/spineboy.atlas"),
|
||||||
|
// URL("https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/4.2/examples/spineboy/export/spineboy-pro.skel"),
|
||||||
|
// context.filesDir,
|
||||||
|
// context,
|
||||||
|
// SpineController {
|
||||||
|
// it.animationState.setAnimation(0, "walk", true)
|
||||||
|
// }
|
||||||
|
// )
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(paddingValues)
|
modifier = Modifier.padding(paddingValues)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -80,9 +80,9 @@ public class AndroidSkeletonDrawable {
|
|||||||
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AndroidSkeletonDrawable fromHttp (URL atlasUrl, URL skeletonUrl) {
|
public static AndroidSkeletonDrawable fromHttp (URL atlasUrl, URL skeletonUrl, File targetDirectory) {
|
||||||
AndroidTextureAtlas atlas = AndroidTextureAtlas.fromHttp(atlasUrl);
|
AndroidTextureAtlas atlas = AndroidTextureAtlas.fromHttp(atlasUrl, targetDirectory);
|
||||||
SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl);
|
SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl, targetDirectory);
|
||||||
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,21 +35,22 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.nio.file.Files;
|
||||||
|
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData;
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData;
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.Null;
|
import com.badlogic.gdx.utils.Null;
|
||||||
|
import com.esotericsoftware.spine.android.utils.SpineHttpUtils;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.os.Build;
|
||||||
import kotlin.NotImplementedError;
|
|
||||||
|
|
||||||
public class AndroidTextureAtlas {
|
public class AndroidTextureAtlas {
|
||||||
private static interface BitmapLoader {
|
private static interface BitmapLoader {
|
||||||
@ -131,9 +132,64 @@ public class AndroidTextureAtlas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public AndroidTextureAtlas fromFile(File atlasFile) {
|
static public AndroidTextureAtlas fromFile(File atlasFile) {
|
||||||
TextureAtlasData data = new TextureAtlasData();
|
TextureAtlasData data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
data = loadTextureAtlasData(atlasFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return new AndroidTextureAtlas(data, path -> {
|
||||||
|
File imageFile = new File(path);
|
||||||
|
try (InputStream in = new BufferedInputStream(inputStream(imageFile))) {
|
||||||
|
return BitmapFactory.decodeStream(in);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new RuntimeException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static public AndroidTextureAtlas fromHttp(URL atlasUrl, File targetDirectory) {
|
||||||
|
File atlasFile = SpineHttpUtils.downloadFrom(atlasUrl, targetDirectory);
|
||||||
|
TextureAtlasData data;
|
||||||
|
try {
|
||||||
|
data = loadTextureAtlasData(atlasFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return new AndroidTextureAtlas(data, path -> {
|
||||||
|
String fileName = path.substring(path.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
|
String atlasUrlPath = atlasUrl.getPath();
|
||||||
|
int lastSlashIndex = atlasUrlPath.lastIndexOf('/');
|
||||||
|
String imagePath = atlasUrlPath.substring(0, lastSlashIndex + 1) + fileName;
|
||||||
|
|
||||||
|
File imageFile;
|
||||||
|
try {
|
||||||
|
URL imageUrl = new URL(atlasUrl.getProtocol(), atlasUrl.getHost(), atlasUrl.getPort(), imagePath);
|
||||||
|
imageFile = SpineHttpUtils.downloadFrom(imageUrl, targetDirectory);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream in = new BufferedInputStream(inputStream(imageFile))) {
|
||||||
|
return BitmapFactory.decodeStream(in);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new RuntimeException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputStream inputStream(File file) throws Exception {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
return Files.newInputStream(file.toPath());
|
||||||
|
} else {
|
||||||
|
//noinspection IOStreamConstructor
|
||||||
|
return new FileInputStream(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private TextureAtlasData loadTextureAtlasData(File atlasFile) {
|
||||||
|
TextureAtlasData data = new TextureAtlasData();
|
||||||
FileHandle inputFile = new FileHandle() {
|
FileHandle inputFile = new FileHandle() {
|
||||||
@Override
|
@Override
|
||||||
public InputStream read() {
|
public InputStream read() {
|
||||||
@ -145,21 +201,6 @@ public class AndroidTextureAtlas {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
data.load(inputFile, new FileHandle(atlasFile).parent(), false);
|
data.load(inputFile, new FileHandle(atlasFile).parent(), false);
|
||||||
} catch (Throwable t) {
|
return data;
|
||||||
throw new RuntimeException(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AndroidTextureAtlas(data, path -> {
|
|
||||||
File imageFile = new File(path);
|
|
||||||
try (InputStream in = new BufferedInputStream(new FileInputStream(imageFile))) {
|
|
||||||
return BitmapFactory.decodeStream(in);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
throw new RuntimeException(t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static public AndroidTextureAtlas fromHttp(URL atlasUrl) {
|
|
||||||
throw new NotImplementedError("TODO");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,6 +146,12 @@ public class SpineView extends View implements Choreographer.FrameCallback {
|
|||||||
return spineView;
|
return spineView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SpineView loadFromHttp(URL atlasUrl, URL skeletonUrl, File targetDirectory, Context context, SpineController controller) {
|
||||||
|
SpineView spineView = new SpineView(context, controller);
|
||||||
|
spineView.loadFromHttp(atlasUrl, skeletonUrl, targetDirectory);
|
||||||
|
return spineView;
|
||||||
|
}
|
||||||
|
|
||||||
public static SpineView loadFromDrawable(AndroidSkeletonDrawable drawable, Context context, SpineController controller) {
|
public static SpineView loadFromDrawable(AndroidSkeletonDrawable drawable, Context context, SpineController controller) {
|
||||||
SpineView spineView = new SpineView(context, controller);
|
SpineView spineView = new SpineView(context, controller);
|
||||||
spineView.loadFromDrawable(drawable);
|
spineView.loadFromDrawable(drawable);
|
||||||
@ -164,8 +170,8 @@ public class SpineView extends View implements Choreographer.FrameCallback {
|
|||||||
loadFrom(() -> AndroidSkeletonDrawable.fromFile(atlasFile, skeletonFile));
|
loadFrom(() -> AndroidSkeletonDrawable.fromFile(atlasFile, skeletonFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadFromHttp(URL atlasUrl, URL skeletonUrl) {
|
public void loadFromHttp(URL atlasUrl, URL skeletonUrl, File targetDirectory) {
|
||||||
loadFrom(() -> AndroidSkeletonDrawable.fromHttp(atlasUrl, skeletonUrl));
|
loadFrom(() -> AndroidSkeletonDrawable.fromHttp(atlasUrl, skeletonUrl, targetDirectory));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadFromDrawable(AndroidSkeletonDrawable drawable) {
|
public void loadFromDrawable(AndroidSkeletonDrawable drawable) {
|
||||||
|
|||||||
@ -55,7 +55,8 @@ public class SkeletonDataUtils {
|
|||||||
return skeletonLoader.readSkeletonData(new FileHandle(skeletonFile));
|
return skeletonLoader.readSkeletonData(new FileHandle(skeletonFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SkeletonData fromHttp(AndroidTextureAtlas atlas, URL skeletonUrl) {
|
public static SkeletonData fromHttp(AndroidTextureAtlas atlas, URL skeletonUrl, File targetDirectory) {
|
||||||
throw new NotImplementedError("TODO");
|
File skeletonFile = SpineHttpUtils.downloadFrom(skeletonUrl, targetDirectory);
|
||||||
|
return fromFile(atlas, skeletonFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,80 @@
|
|||||||
|
package com.esotericsoftware.spine.android.utils;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.esotericsoftware.spine.android.AndroidTextureAtlas;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
|
public class SpineHttpUtils {
|
||||||
|
|
||||||
|
public static File downloadFrom(URL url, File targetDirectory) throws RuntimeException {
|
||||||
|
HttpURLConnection urlConnection = null;
|
||||||
|
InputStream inputStream = null;
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
|
urlConnection.connect();
|
||||||
|
|
||||||
|
if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||||
|
throw new RuntimeException("Failed to connect: HTTP response code " + urlConnection.getResponseCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
inputStream = new BufferedInputStream(urlConnection.getInputStream());
|
||||||
|
|
||||||
|
String atlasUrlPath = url.getPath();
|
||||||
|
String fileName = atlasUrlPath.substring(atlasUrlPath.lastIndexOf('/') + 1);
|
||||||
|
File file = new File(targetDirectory, fileName);
|
||||||
|
|
||||||
|
// Create an OutputStream to write to the file
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
outputStream = Files.newOutputStream(file.toPath());
|
||||||
|
} else {
|
||||||
|
//noinspection IOStreamConstructor
|
||||||
|
outputStream = new FileOutputStream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
// Write the input stream to the output stream
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
if (outputStream != null) {
|
||||||
|
try {
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Nothing we can do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Nothing we can do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlConnection != null) {
|
||||||
|
urlConnection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user