Browse Source

Add spine-android to formatter

Mario Zechner 1 year ago
parent
commit
12f11cbbf9
27 changed files with 853 additions and 1030 deletions
  1. 2 1
      formatters/build.gradle
  2. 29 31
      spine-android/app/src/main/java/com/esotericsoftware/spine/SimpleAnimationActivity.java
  3. 14 0
      spine-android/publish.sh
  4. 2 1
      spine-android/spine-android/build.gradle.kts
  5. 10 11
      spine-android/spine-android/src/androidTest/java/com/esotericsoftware/android/ExampleInstrumentedTest.java
  6. 100 118
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/AndroidSkeletonDrawable.java
  7. 1 4
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/AndroidTexture.java
  8. 26 37
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/AndroidTextureAtlas.java
  9. 12 14
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/DebugRenderer.java
  10. 17 27
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SkeletonRenderer.java
  11. 234 278
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineController.java
  12. 123 178
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineView.java
  13. 15 24
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/Alignment.java
  14. 64 67
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/Bounds.java
  15. 3 5
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/BoundsProvider.java
  16. 5 11
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/ContentMode.java
  17. 15 17
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/RawBounds.java
  18. 6 8
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/SetupPoseBounds.java
  19. 64 75
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/SkinAndAnimationBounds.java
  20. 1 1
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/AndroidSkeletonDrawableLoader.java
  21. 1 1
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/SpineControllerAfterPaintCallback.java
  22. 1 1
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/SpineControllerBeforePaintCallback.java
  23. 1 1
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/SpineControllerCallback.java
  24. 54 59
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/HttpUtils.java
  25. 44 50
      spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/SkeletonDataUtils.java
  26. 8 9
      spine-android/spine-android/src/test/java/com/esotericsoftware/android/ExampleUnitTest.java
  27. 1 1
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java

+ 2 - 1
formatters/build.gradle

@@ -8,7 +8,8 @@ spotless {
     lineEndings 'UNIX'
 
     java {
-        target 'spine-libgdx/**/*.java'
+        target 'spine-libgdx/**/*.java',
+               'spine-android/**/*.java'
         eclipse().configFile('formatters/eclipse-formatter.xml')
     }
 

+ 29 - 31
spine-android/app/src/main/java/com/esotericsoftware/spine/SimpleAnimationActivity.java

@@ -39,40 +39,38 @@ import com.esotericsoftware.spine.android.SpineController;
 import com.esotericsoftware.spine.android.SpineView;
 
 public class SimpleAnimationActivity extends AppCompatActivity {
-    /** @noinspection FieldCanBeLocal*/
-    private SpineView spineView;
-    /** @noinspection FieldCanBeLocal*/
-    private SpineController spineController;
+	/** @noinspection FieldCanBeLocal */
+	private SpineView spineView;
+	/** @noinspection FieldCanBeLocal */
+	private SpineController spineController;
 
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_simple_animation);
+	@Override
+	protected void onCreate (Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.activity_simple_animation);
 
-        // Set up the toolbar
-        Toolbar toolbar = findViewById(R.id.toolbar);
-        setSupportActionBar(toolbar);
-        if (getSupportActionBar() != null) {
-            getSupportActionBar().setTitle("Simple Animation");
-            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-            getSupportActionBar().setDisplayShowHomeEnabled(true);
-        }
+		// Set up the toolbar
+		Toolbar toolbar = findViewById(R.id.toolbar);
+		setSupportActionBar(toolbar);
+		if (getSupportActionBar() != null) {
+			getSupportActionBar().setTitle("Simple Animation");
+			getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+			getSupportActionBar().setDisplayShowHomeEnabled(true);
+		}
 
-        spineView = findViewById(R.id.spineView);
-        spineController = new SpineController( controller ->
-            controller.getAnimationState().setAnimation(0, "walk", true)
-        );
+		spineView = findViewById(R.id.spineView);
+		spineController = new SpineController(controller -> controller.getAnimationState().setAnimation(0, "walk", true));
 
-        spineView.setController(spineController);
-        spineView.loadFromAsset("spineboy.atlas","spineboy-pro.json");
-    }
+		spineView.setController(spineController);
+		spineView.loadFromAsset("spineboy.atlas", "spineboy-pro.json");
+	}
 
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item.getItemId() == android.R.id.home) {
-            finish();
-            return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
+	@Override
+	public boolean onOptionsItemSelected (MenuItem item) {
+		if (item.getItemId() == android.R.id.home) {
+			finish();
+			return true;
+		}
+		return super.onOptionsItemSelected(item);
+	}
 }

+ 14 - 0
spine-android/publish.sh

@@ -0,0 +1,14 @@
+#!/bin/sh
+
+#
+# 1. Set up PGP key for signing
+# 2. Create ~/.gradle/gradle.properties
+# 3. Add
+#    ossrhUsername=<sonatype-token-user-name>
+#    ossrhPassword=<sonatype-token>
+#    signing.gnupg.passphrase=<pgp-key-passphrase>
+#
+# After publishing via this script, log into https://oss.sonatype.org and release it manually after
+# checks pass ("Release & Drop").
+set -e
+ ./gradlew publishReleasePublicationToSonaTypeRepository --info

+ 2 - 1
spine-android/spine-android/build.gradle.kts

@@ -40,7 +40,7 @@ dependencies {
     androidTestImplementation(libs.androidx.espresso.core)
 }
 
-val libraryVersion = "4.2.2-SNAPSHOT" // Update this as needed
+val libraryVersion = "4.2.3-SNAPSHOT" // Update this as needed
 
 tasks.register<Jar>("sourceJar") {
     archiveClassifier.set("sources")
@@ -125,5 +125,6 @@ afterEvaluate {
     signing {
         useGpgCmd()
         sign(publishing.publications["release"])
+        sign(tasks.getByName("sourceJar"))
     }
 }

+ 10 - 11
spine-android/spine-android/src/androidTest/java/com/esotericsoftware/android/ExampleInstrumentedTest.java

@@ -1,3 +1,4 @@
+
 package com.esotericsoftware.android;
 
 import android.content.Context;
@@ -10,17 +11,15 @@ import org.junit.runner.RunWith;
 
 import static org.junit.Assert.*;
 
-/**
- * Instrumented test, which will execute on an Android device.
+/** Instrumented test, which will execute on an Android device.
  *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> */
 @RunWith(AndroidJUnit4.class)
 public class ExampleInstrumentedTest {
-    @Test
-    public void useAppContext() {
-        // Context of the app under test.
-        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        assertEquals("com.esotericsoftware.spine.test", appContext.getPackageName());
-    }
-}
+	@Test
+	public void useAppContext () {
+		// Context of the app under test.
+		Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+		assertEquals("com.esotericsoftware.spine.test", appContext.getPackageName());
+	}
+}

+ 100 - 118
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/AndroidSkeletonDrawable.java

@@ -48,129 +48,111 @@ import com.esotericsoftware.spine.android.utils.SkeletonDataUtils;
 import java.io.File;
 import java.net.URL;
 
-/**
- * A {@link AndroidSkeletonDrawable} bundles loading updating updating an {@link AndroidTextureAtlas}, {@link Skeleton}, and {@link AnimationState}
- * into a single easy-to-use class.
+/** A {@link AndroidSkeletonDrawable} bundles loading updating updating an {@link AndroidTextureAtlas}, {@link Skeleton}, and
+ * {@link AnimationState} into a single easy-to-use class.
  *
- * Use the {@link AndroidSkeletonDrawable#fromAsset(String, String, Context)}, {@link AndroidSkeletonDrawable#fromFile(File, File)},
- * or {@link AndroidSkeletonDrawable#fromHttp(URL, URL, File)} methods to construct a {@link AndroidSkeletonDrawable}. To have
- * multiple skeleton drawable instances share the same {@link AndroidTextureAtlas} and {@link SkeletonData}, use the constructor.
+ * Use the {@link AndroidSkeletonDrawable#fromAsset(String, String, Context)},
+ * {@link AndroidSkeletonDrawable#fromFile(File, File)}, or {@link AndroidSkeletonDrawable#fromHttp(URL, URL, File)} methods to
+ * construct a {@link AndroidSkeletonDrawable}. To have multiple skeleton drawable instances share the same
+ * {@link AndroidTextureAtlas} and {@link SkeletonData}, use the constructor.
  *
  * You can then directly access the {@link AndroidSkeletonDrawable#getAtlas()}, {@link AndroidSkeletonDrawable#getSkeletonData()},
- * {@link AndroidSkeletonDrawable#getSkeleton()}, {@link AndroidSkeletonDrawable#getAnimationStateData()}, and {@link AndroidSkeletonDrawable#getAnimationState()}
- * to query and animate the skeleton. Use the {@link AnimationState} to queue animations on one or more tracks
- * via {@link AnimationState#setAnimation(int, Animation, boolean)} or {@link AnimationState#addAnimation(int, Animation, boolean, float)}.
+ * {@link AndroidSkeletonDrawable#getSkeleton()}, {@link AndroidSkeletonDrawable#getAnimationStateData()}, and
+ * {@link AndroidSkeletonDrawable#getAnimationState()} to query and animate the skeleton. Use the {@link AnimationState} to queue
+ * animations on one or more tracks via {@link AnimationState#setAnimation(int, Animation, boolean)} or
+ * {@link AnimationState#addAnimation(int, Animation, boolean, float)}.
  *
- * To update the {@link AnimationState} and apply it to the {@link Skeleton}, call the {@link AndroidSkeletonDrawable#update(float)} function, providing it
- * a delta time in seconds to advance the animations.
+ * To update the {@link AnimationState} and apply it to the {@link Skeleton}, call the
+ * {@link AndroidSkeletonDrawable#update(float)} function, providing it a delta time in seconds to advance the animations.
  *
- * To render the current pose of the {@link Skeleton}, use {@link SkeletonRenderer#render(Skeleton)}, {@link SkeletonRenderer#renderToCanvas(Canvas, Array)},
- * {@link SkeletonRenderer#renderToBitmap(float, float, int, Skeleton)}, depending on your needs.
- */
+ * To render the current pose of the {@link Skeleton}, use {@link SkeletonRenderer#render(Skeleton)},
+ * {@link SkeletonRenderer#renderToCanvas(Canvas, Array)}, {@link SkeletonRenderer#renderToBitmap(float, float, int, Skeleton)},
+ * depending on your needs. */
 public class AndroidSkeletonDrawable {
 
-    private final AndroidTextureAtlas atlas;
-
-    private final SkeletonData skeletonData;
-
-    private final Skeleton skeleton;
-
-    private final AnimationStateData animationStateData;
-
-    private final AnimationState animationState;
-
-    /**
-     * Constructs a new skeleton drawable from the given (possibly shared) {@link AndroidTextureAtlas} and {@link SkeletonData}.
-     */
-    public AndroidSkeletonDrawable(AndroidTextureAtlas atlas, SkeletonData skeletonData) {
-        this.atlas = atlas;
-        this.skeletonData = skeletonData;
-
-        skeleton = new Skeleton(skeletonData);
-        animationStateData = new AnimationStateData(skeletonData);
-        animationState = new AnimationState(animationStateData);
-
-        skeleton.updateWorldTransform(Skeleton.Physics.none);
-    }
-
-    /**
-     * Updates the {@link AnimationState} using the {@code delta} time given in seconds, applies the
-     * animation state to the {@link Skeleton} and updates the world transforms of the skeleton
-     * to calculate its current pose.
-     */
-    public void update(float delta) {
-        animationState.update(delta);
-        animationState.apply(skeleton);
-
-        skeleton.update(delta);
-        skeleton.updateWorldTransform(Skeleton.Physics.update);
-    }
-
-    /**
-     * Get the {@link AndroidTextureAtlas}
-     */
-    public AndroidTextureAtlas getAtlas() {
-        return atlas;
-    }
-
-    /**
-     * Get the {@link Skeleton}
-     */
-    public Skeleton getSkeleton() {
-        return skeleton;
-    }
-
-    /**
-     * Get the {@link SkeletonData}
-     */
-    public SkeletonData getSkeletonData() {
-        return skeletonData;
-    }
-
-    /**
-     * Get the {@link AnimationStateData}
-     */
-    public AnimationStateData getAnimationStateData() {
-        return animationStateData;
-    }
-
-    /**
-     * Get the {@link AnimationState}
-     */
-    public AnimationState getAnimationState() {
-        return animationState;
-    }
-
-    /**
-     * Constructs a new skeleton drawable from the {@code atlasFileName} and {@code skeletonFileName} from the the apps resources using {@link Context}.
-     *
-     * Throws an exception in case the data could not be loaded.
-     */
-    public static AndroidSkeletonDrawable fromAsset (String atlasFileName, String skeletonFileName, Context context) {
-        AndroidTextureAtlas atlas = AndroidTextureAtlas.fromAsset(atlasFileName, context);
-        SkeletonData skeletonData = SkeletonDataUtils.fromAsset(atlas, skeletonFileName, context);
-        return new AndroidSkeletonDrawable(atlas, skeletonData);
-    }
-
-    /**
-     * Constructs a new skeleton drawable from the {@code atlasFile} and {@code skeletonFile}.
-     *
-     * Throws an exception in case the data could not be loaded.
-     */
-    public static AndroidSkeletonDrawable fromFile (File atlasFile, File skeletonFile) {
-        AndroidTextureAtlas atlas = AndroidTextureAtlas.fromFile(atlasFile);
-        SkeletonData skeletonData = SkeletonDataUtils.fromFile(atlas, skeletonFile);
-        return new AndroidSkeletonDrawable(atlas, skeletonData);
-    }
-
-    /**
-     * Constructs a new skeleton drawable from the {@code atlasUrl} and {@code skeletonUrl}.
-     *
-     * Throws an exception in case the data could not be loaded.
-     */
-    public static AndroidSkeletonDrawable fromHttp (URL atlasUrl, URL skeletonUrl, File targetDirectory) {
-        AndroidTextureAtlas atlas = AndroidTextureAtlas.fromHttp(atlasUrl, targetDirectory);
-        SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl, targetDirectory);
-        return new AndroidSkeletonDrawable(atlas, skeletonData);
-    }
+	private final AndroidTextureAtlas atlas;
+
+	private final SkeletonData skeletonData;
+
+	private final Skeleton skeleton;
+
+	private final AnimationStateData animationStateData;
+
+	private final AnimationState animationState;
+
+	/** Constructs a new skeleton drawable from the given (possibly shared) {@link AndroidTextureAtlas} and
+	 * {@link SkeletonData}. */
+	public AndroidSkeletonDrawable (AndroidTextureAtlas atlas, SkeletonData skeletonData) {
+		this.atlas = atlas;
+		this.skeletonData = skeletonData;
+
+		skeleton = new Skeleton(skeletonData);
+		animationStateData = new AnimationStateData(skeletonData);
+		animationState = new AnimationState(animationStateData);
+
+		skeleton.updateWorldTransform(Skeleton.Physics.none);
+	}
+
+	/** Updates the {@link AnimationState} using the {@code delta} time given in seconds, applies the animation state to the
+	 * {@link Skeleton} and updates the world transforms of the skeleton to calculate its current pose. */
+	public void update (float delta) {
+		animationState.update(delta);
+		animationState.apply(skeleton);
+
+		skeleton.update(delta);
+		skeleton.updateWorldTransform(Skeleton.Physics.update);
+	}
+
+	/** Get the {@link AndroidTextureAtlas} */
+	public AndroidTextureAtlas getAtlas () {
+		return atlas;
+	}
+
+	/** Get the {@link Skeleton} */
+	public Skeleton getSkeleton () {
+		return skeleton;
+	}
+
+	/** Get the {@link SkeletonData} */
+	public SkeletonData getSkeletonData () {
+		return skeletonData;
+	}
+
+	/** Get the {@link AnimationStateData} */
+	public AnimationStateData getAnimationStateData () {
+		return animationStateData;
+	}
+
+	/** Get the {@link AnimationState} */
+	public AnimationState getAnimationState () {
+		return animationState;
+	}
+
+	/** Constructs a new skeleton drawable from the {@code atlasFileName} and {@code skeletonFileName} from the the apps resources
+	 * using {@link Context}.
+	 *
+	 * Throws an exception in case the data could not be loaded. */
+	public static AndroidSkeletonDrawable fromAsset (String atlasFileName, String skeletonFileName, Context context) {
+		AndroidTextureAtlas atlas = AndroidTextureAtlas.fromAsset(atlasFileName, context);
+		SkeletonData skeletonData = SkeletonDataUtils.fromAsset(atlas, skeletonFileName, context);
+		return new AndroidSkeletonDrawable(atlas, skeletonData);
+	}
+
+	/** Constructs a new skeleton drawable from the {@code atlasFile} and {@code skeletonFile}.
+	 *
+	 * Throws an exception in case the data could not be loaded. */
+	public static AndroidSkeletonDrawable fromFile (File atlasFile, File skeletonFile) {
+		AndroidTextureAtlas atlas = AndroidTextureAtlas.fromFile(atlasFile);
+		SkeletonData skeletonData = SkeletonDataUtils.fromFile(atlas, skeletonFile);
+		return new AndroidSkeletonDrawable(atlas, skeletonData);
+	}
+
+	/** Constructs a new skeleton drawable from the {@code atlasUrl} and {@code skeletonUrl}.
+	 *
+	 * Throws an exception in case the data could not be loaded. */
+	public static AndroidSkeletonDrawable fromHttp (URL atlasUrl, URL skeletonUrl, File targetDirectory) {
+		AndroidTextureAtlas atlas = AndroidTextureAtlas.fromHttp(atlasUrl, targetDirectory);
+		SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl, targetDirectory);
+		return new AndroidSkeletonDrawable(atlas, skeletonData);
+	}
 }

+ 1 - 4
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/AndroidTexture.java

@@ -40,10 +40,7 @@ import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Shader;
 
-/**
- * A class holding an {@link Bitmap} of an {@link AndroidTextureAtlas} page image with it's associated
- * blend modes and paints.
- */
+/** A class holding an {@link Bitmap} of an {@link AndroidTextureAtlas} page image with it's associated blend modes and paints. */
 public class AndroidTexture extends Texture {
 	private Bitmap bitmap;
 	private ObjectMap<BlendMode, Paint> paints = new ObjectMap<>();

+ 26 - 37
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/AndroidTextureAtlas.java

@@ -53,14 +53,11 @@ import android.graphics.Paint;
 import android.graphics.BitmapFactory;
 import android.os.Build;
 
-/**
- * Atlas data loaded from a `.atlas` file and its corresponding `.png` files. For each atlas image,
- * a corresponding {@link Bitmap} and {@link Paint} is constructed, which are used when rendering a skeleton
- * that uses this atlas.
+/** Atlas data loaded from a `.atlas` file and its corresponding `.png` files. For each atlas image, a corresponding
+ * {@link Bitmap} and {@link Paint} is constructed, which are used when rendering a skeleton that uses this atlas.
  *
- * Use the static methods {@link AndroidTextureAtlas#fromAsset(String, Context)}, {@link AndroidTextureAtlas#fromFile(File)},
- * and {@link AndroidTextureAtlas#fromHttp(URL, File)} to load an atlas.
- */
+ * Use the static methods {@link AndroidTextureAtlas#fromAsset(String, Context)}, {@link AndroidTextureAtlas#fromFile(File)}, and
+ * {@link AndroidTextureAtlas#fromHttp(URL, File)} to load an atlas. */
 public class AndroidTextureAtlas {
 	private interface BitmapLoader {
 		Bitmap load (String path);
@@ -94,10 +91,8 @@ public class AndroidTextureAtlas {
 		}
 	}
 
-	/**
-	 * Returns the first region found with the specified name. This method uses string comparison to find the region, so the
-	 * result should be cached rather than calling this method multiple times.
-	 */
+	/** Returns the first region found with the specified name. This method uses string comparison to find the region, so the
+	 * result should be cached rather than calling this method multiple times. */
 	public @Null AtlasRegion findRegion (String name) {
 		for (int i = 0, n = regions.size; i < n; i++)
 			if (regions.get(i).name.equals(name)) return regions.get(i);
@@ -112,12 +107,10 @@ public class AndroidTextureAtlas {
 		return regions;
 	}
 
-	/**
-	 * Loads an {@link AndroidTextureAtlas} from the file {@code atlasFileName} from assets using {@link Context}.
+	/** Loads an {@link AndroidTextureAtlas} from the file {@code atlasFileName} from assets using {@link Context}.
 	 *
-	 * Throws a {@link RuntimeException} in case the atlas could not be loaded.
-	 */
-	public static AndroidTextureAtlas fromAsset(String atlasFileName, Context context) {
+	 * Throws a {@link RuntimeException} in case the atlas could not be loaded. */
+	public static AndroidTextureAtlas fromAsset (String atlasFileName, Context context) {
 		TextureAtlasData data = new TextureAtlasData();
 		AssetManager assetManager = context.getAssets();
 
@@ -138,21 +131,19 @@ public class AndroidTextureAtlas {
 		}
 
 		return new AndroidTextureAtlas(data, path -> {
-            path = path.startsWith("/") ? path.substring(1) : path;
-            try (InputStream in = new BufferedInputStream(assetManager.open(path))) {
-                return BitmapFactory.decodeStream(in);
-            } catch (Throwable t) {
-                throw new RuntimeException(t);
-            }
-        });
+			path = path.startsWith("/") ? path.substring(1) : path;
+			try (InputStream in = new BufferedInputStream(assetManager.open(path))) {
+				return BitmapFactory.decodeStream(in);
+			} catch (Throwable t) {
+				throw new RuntimeException(t);
+			}
+		});
 	}
 
-	/**
-	 * Loads an {@link AndroidTextureAtlas} from the file {@code atlasFileName}.
+	/** Loads an {@link AndroidTextureAtlas} from the file {@code atlasFileName}.
 	 *
-	 * Throws a {@link RuntimeException} in case the atlas could not be loaded.
-	 */
-	public static AndroidTextureAtlas fromFile(File atlasFile) {
+	 * Throws a {@link RuntimeException} in case the atlas could not be loaded. */
+	public static AndroidTextureAtlas fromFile (File atlasFile) {
 		TextureAtlasData data;
 		try {
 			data = loadTextureAtlasData(atlasFile);
@@ -169,12 +160,10 @@ public class AndroidTextureAtlas {
 		});
 	}
 
-	/**
-	 * Loads an {@link AndroidTextureAtlas} from the URL {@code atlasURL}.
+	/** Loads an {@link AndroidTextureAtlas} from the URL {@code atlasURL}.
 	 *
-	 * Throws a {@link Exception} in case the atlas could not be loaded.
-	 */
-	public static AndroidTextureAtlas fromHttp(URL atlasUrl, File targetDirectory) {
+	 * Throws a {@link Exception} in case the atlas could not be loaded. */
+	public static AndroidTextureAtlas fromHttp (URL atlasUrl, File targetDirectory) {
 		File atlasFile = HttpUtils.downloadFrom(atlasUrl, targetDirectory);
 		TextureAtlasData data;
 		try {
@@ -205,20 +194,20 @@ public class AndroidTextureAtlas {
 		});
 	}
 
-	private static InputStream inputStream(File file) throws Exception {
+	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
+			// noinspection IOStreamConstructor
 			return new FileInputStream(file);
 		}
 	}
 
-	private static TextureAtlasData loadTextureAtlasData(File atlasFile) {
+	private static TextureAtlasData loadTextureAtlasData (File atlasFile) {
 		TextureAtlasData data = new TextureAtlasData();
 		FileHandle inputFile = new FileHandle() {
 			@Override
-			public InputStream read() {
+			public InputStream read () {
 				try {
 					return new FileInputStream(atlasFile);
 				} catch (FileNotFoundException e) {

+ 12 - 14
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/DebugRenderer.java

@@ -36,21 +36,19 @@ import android.graphics.RectF;
 import com.badlogic.gdx.utils.Array;
 import com.esotericsoftware.spine.Bone;
 
-/**
- * Renders debug information for a {@link AndroidSkeletonDrawable}, like bone locations, to a {@link Canvas}.
- * See {@link DebugRenderer#render}.
- */
+/** Renders debug information for a {@link AndroidSkeletonDrawable}, like bone locations, to a {@link Canvas}. See
+ * {@link DebugRenderer#render}. */
 public class DebugRenderer {
 
-    public void render(AndroidSkeletonDrawable drawable, Canvas canvas, Array<SkeletonRenderer.RenderCommand> commands) {
-        Paint bonePaint = new Paint();
-        bonePaint.setColor(android.graphics.Color.BLUE);
-        bonePaint.setStyle(Paint.Style.FILL);
+	public void render (AndroidSkeletonDrawable drawable, Canvas canvas, Array<SkeletonRenderer.RenderCommand> commands) {
+		Paint bonePaint = new Paint();
+		bonePaint.setColor(android.graphics.Color.BLUE);
+		bonePaint.setStyle(Paint.Style.FILL);
 
-        for (Bone bone : drawable.getSkeleton().getBones()) {
-            float x = bone.getWorldX();
-            float y = bone.getWorldY();
-            canvas.drawRect(new RectF(x - 2.5f, y - 2.5f, x + 2.5f, y + 2.5f), bonePaint);
-        }
-    }
+		for (Bone bone : drawable.getSkeleton().getBones()) {
+			float x = bone.getWorldX();
+			float y = bone.getWorldY();
+			canvas.drawRect(new RectF(x - 2.5f, y - 2.5f, x + 2.5f, y + 2.5f), bonePaint);
+		}
+	}
 }

+ 17 - 27
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SkeletonRenderer.java

@@ -50,17 +50,13 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.RectF;
 
-/**
- * Is responsible to transform the {@link Skeleton} with its current pose to {@link SkeletonRenderer.RenderCommand} commands
- * and render them to a {@link Canvas}.
- */
+/** Is responsible to transform the {@link Skeleton} with its current pose to {@link SkeletonRenderer.RenderCommand} commands and
+ * render them to a {@link Canvas}. */
 public class SkeletonRenderer {
 
-	/**
-	 * Stores the vertices, indices, and atlas page index to be used for rendering one or more attachments
-	 * of a {@link Skeleton} to a {@link Canvas}. See the implementation of {@link SkeletonRenderer#render(Skeleton)} and
-	 * {@link SkeletonRenderer#renderToCanvas(Canvas, Array)} on how to use this data to render it to a {@link Canvas}.
-	 */
+	/** Stores the vertices, indices, and atlas page index to be used for rendering one or more attachments of a {@link Skeleton}
+	 * to a {@link Canvas}. See the implementation of {@link SkeletonRenderer#render(Skeleton)} and
+	 * {@link SkeletonRenderer#renderToCanvas(Canvas, Array)} on how to use this data to render it to a {@link Canvas}. */
 	public static class RenderCommand implements Pool.Poolable {
 		FloatArray vertices = new FloatArray(32);
 		FloatArray uvs = new FloatArray(32);
@@ -90,10 +86,8 @@ public class SkeletonRenderer {
 	};
 	private final Array<RenderCommand> commandList = new Array<RenderCommand>();
 
-	/**
-	 * Created the {@link RenderCommand} commands from the skeletons current pose.
-	 */
-	public Array<RenderCommand> render(Skeleton skeleton) {
+	/** Created the {@link RenderCommand} commands from the skeletons current pose. */
+	public Array<RenderCommand> render (Skeleton skeleton) {
 		Color color = null, skeletonColor = skeleton.getColor();
 		float r = skeletonColor.r, g = skeletonColor.g, b = skeletonColor.b, a = skeletonColor.a;
 
@@ -228,11 +222,9 @@ public class SkeletonRenderer {
 		return commandList;
 	}
 
-	/**
-	 * Renders the {@link RenderCommand} commands created from the skeleton current pose to the given {@link Canvas}.
-	 * Does not perform any scaling or fitting.
-	 */
-	public void renderToCanvas(Canvas canvas, Array<RenderCommand> commands) {
+	/** Renders the {@link RenderCommand} commands created from the skeleton current pose to the given {@link Canvas}. Does not
+	 * perform any scaling or fitting. */
+	public void renderToCanvas (Canvas canvas, Array<RenderCommand> commands) {
 		for (int i = 0; i < commands.size; i++) {
 			RenderCommand command = commands.get(i);
 
@@ -241,15 +233,13 @@ public class SkeletonRenderer {
 		}
 	}
 
-	/**
-	 * Renders the {@link Skeleton} with its current pose to a {@link Bitmap}.
+	/** Renders the {@link Skeleton} with its current pose to a {@link Bitmap}.
 	 *
-	 * @param width    The width of the bitmap in pixels.
-	 * @param height   The height of the bitmap in pixels.
-	 * @param bgColor  The background color.
-	 * @param skeleton The skeleton to render.
-	 */
-	public Bitmap renderToBitmap(float width, float height, int bgColor, Skeleton skeleton) {
+	 * @param width The width of the bitmap in pixels.
+	 * @param height The height of the bitmap in pixels.
+	 * @param bgColor The background color.
+	 * @param skeleton The skeleton to render. */
+	public Bitmap renderToBitmap (float width, float height, int bgColor, Skeleton skeleton) {
 		Vector2 offset = new Vector2(0, 0);
 		Vector2 size = new Vector2(0, 0);
 		FloatArray floatArray = new FloatArray();
@@ -259,7 +249,7 @@ public class SkeletonRenderer {
 		RectF bounds = new RectF(offset.x, offset.y, offset.x + size.x, offset.y + size.y);
 		float scale = (1 / (bounds.width() > bounds.height() ? bounds.width() / width : bounds.height() / height));
 
-		Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
+		Bitmap bitmap = Bitmap.createBitmap((int)width, (int)height, Bitmap.Config.ARGB_8888);
 		Canvas canvas = new Canvas(bitmap);
 
 		Paint paint = new Paint();

+ 234 - 278
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineController.java

@@ -43,289 +43,245 @@ import com.esotericsoftware.spine.android.callbacks.SpineControllerAfterPaintCal
 import com.esotericsoftware.spine.android.callbacks.SpineControllerBeforePaintCallback;
 import com.esotericsoftware.spine.android.callbacks.SpineControllerCallback;
 
-/**
- * Controls how the skeleton of a {@link SpineView} is animated and rendered.
+/** Controls how the skeleton of a {@link SpineView} is animated and rendered.
  *
- * Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback method is called once. This method can be used
- * to set up the initial animation(s) of the skeleton, among other things.
+ * Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback method is called once. This method can
+ * be used to set up the initial animation(s) of the skeleton, among other things.
  *
- * After initialization is complete, the {@link SpineView} is rendered at the screen refresh rate. In each frame,
- * the {@link AnimationState} is updated and applied to the {@link Skeleton}.
+ * After initialization is complete, the {@link SpineView} is rendered at the screen refresh rate. In each frame, the
+ * {@link AnimationState} is updated and applied to the {@link Skeleton}.
  *
- * Next, the optionally provided method {@code onBeforeUpdateWorldTransforms} is called, which can modify the
- * skeleton before its current pose is calculated using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. After
- * {@link Skeleton#updateWorldTransform(Skeleton.Physics)} has completed, the optional {@code onAfterUpdateWorldTransforms} method is
- * called, which can modify the current pose before rendering the skeleton.
+ * Next, the optionally provided method {@code onBeforeUpdateWorldTransforms} is called, which can modify the skeleton before its
+ * current pose is calculated using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. After
+ * {@link Skeleton#updateWorldTransform(Skeleton.Physics)} has completed, the optional {@code onAfterUpdateWorldTransforms} method
+ * is called, which can modify the current pose before rendering the skeleton.
  *
- * Before the skeleton's current pose is rendered by the {@link SpineView}, the optional {@code onBeforePaint} is called,
- * which allows rendering backgrounds or other objects that should go behind the skeleton on the {@link Canvas}. The
- * {@link SpineView} then renders the skeleton's current pose and finally calls the optional {@code onAfterPaint}, which
- * can render additional objects on top of the skeleton.
+ * Before the skeleton's current pose is rendered by the {@link SpineView}, the optional {@code onBeforePaint} is called, which
+ * allows rendering backgrounds or other objects that should go behind the skeleton on the {@link Canvas}. The {@link SpineView}
+ * then renders the skeleton's current pose and finally calls the optional {@code onAfterPaint}, which can render additional
+ * objects on top of the skeleton.
  *
- * The underlying {@link AndroidTextureAtlas}, {@link SkeletonData}, {@link Skeleton}, {@link AnimationStateData}, {@link AnimationState}, and {@link AndroidSkeletonDrawable}
- * can be accessed through their respective getters to inspect and/or modify the skeleton and its associated data. Accessing
- * this data is only allowed if the {@link SpineView} and its data have been initialized and have not been disposed of yet.
+ * The underlying {@link AndroidTextureAtlas}, {@link SkeletonData}, {@link Skeleton}, {@link AnimationStateData},
+ * {@link AnimationState}, and {@link AndroidSkeletonDrawable} can be accessed through their respective getters to inspect and/or
+ * modify the skeleton and its associated data. Accessing this data is only allowed if the {@link SpineView} and its data have
+ * been initialized and have not been disposed of yet.
  *
- * By default, the widget updates and renders the skeleton every frame. The {@code pause} method can be used to pause updating
- * and rendering the skeleton. The {@link SpineController#resume()} method resumes updating and rendering the skeleton. The {@link SpineController#isPlaying()} getter
- * reports the current state.
- */
+ * By default, the widget updates and renders the skeleton every frame. The {@code pause} method can be used to pause updating and
+ * rendering the skeleton. The {@link SpineController#resume()} method resumes updating and rendering the skeleton. The
+ * {@link SpineController#isPlaying()} getter reports the current state. */
 public class SpineController {
-    /**
-     * Used to build {@link SpineController} instances.
-     * */
-    public static class Builder {
-        private final SpineControllerCallback onInitialized;
-        private SpineControllerCallback onBeforeUpdateWorldTransforms;
-        private SpineControllerCallback onAfterUpdateWorldTransforms;
-        private SpineControllerBeforePaintCallback onBeforePaint;
-        private SpineControllerAfterPaintCallback onAfterPaint;
-
-        /**
-         * Instantiate a {@link Builder} used to build a {@link SpineController}, which controls how the skeleton of a {@link SpineView}
-         * is animated and rendered. Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback
-         * method is called once. This method can be used to set up the initial animation(s) of the skeleton, among other things.
-         *
-         * @param onInitialized Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback
-         *                      method is called once. This method can be used to set up the initial animation(s) of the skeleton,
-         *                      among other things.
-         */
-        public Builder(SpineControllerCallback onInitialized) {
-            this.onInitialized = onInitialized;
-        }
-
-        /**
-         * Sets the {@code onBeforeUpdateWorldTransforms} callback. It is called before the skeleton's current pose is calculated
-         * using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the skeleton before the pose calculation.
-         */
-        public Builder setOnBeforeUpdateWorldTransforms(SpineControllerCallback onBeforeUpdateWorldTransforms) {
-            this.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
-            return this;
-        }
-
-        /**
-         * Sets the {@code onAfterUpdateWorldTransforms} callback. This method is called after the skeleton's current pose is calculated using
-         * {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the current pose before rendering the skeleton.
-         */
-        public Builder setOnAfterUpdateWorldTransforms(SpineControllerCallback onAfterUpdateWorldTransforms) {
-            this.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
-            return this;
-        }
-
-        /**
-         * Sets the {@code onBeforePaint} callback. It is called before the skeleton's current pose is rendered by the
-         * {@link SpineView}. It allows rendering backgrounds or other objects that should go behind the skeleton on the
-         * {@link Canvas}.
-         */
-        public Builder setOnBeforePaint(SpineControllerBeforePaintCallback onBeforePaint) {
-            this.onBeforePaint = onBeforePaint;
-            return this;
-        }
-
-        /**
-         * Sets the {@code onAfterPaint} callback. It is called after the skeleton's current pose is rendered by the
-         * {@link SpineView}. It allows rendering additional objects on top of the skeleton.
-         */
-        public Builder setOnAfterPaint(SpineControllerAfterPaintCallback onAfterPaint) {
-            this.onAfterPaint = onAfterPaint;
-            return this;
-        }
-
-        public SpineController build() {
-            SpineController spineController = new SpineController(onInitialized);
-            spineController.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
-            spineController.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
-            spineController.onBeforePaint = onBeforePaint;
-            spineController.onAfterPaint = onAfterPaint;
-            return spineController;
-        }
-    }
-
-    private final SpineControllerCallback onInitialized;
-    private @Nullable SpineControllerCallback onBeforeUpdateWorldTransforms;
-    private @Nullable SpineControllerCallback onAfterUpdateWorldTransforms;
-    private @Nullable SpineControllerBeforePaintCallback onBeforePaint;
-    private @Nullable SpineControllerAfterPaintCallback onAfterPaint;
-    private AndroidSkeletonDrawable drawable;
-    private boolean playing = true;
-    private double offsetX = 0;
-    private double offsetY = 0;
-    private double scaleX = 1;
-    private double scaleY = 1;
-
-    /**
-     * Instantiate a {@link SpineController}, which controls how the skeleton of a {@link SpineView} is animated and rendered.
-     * Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback method is called once.
-     * This method can be used to set up the initial animation(s) of the skeleton, among other things.
-     *
-     * @param onInitialized Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback
-     *                      method is called once. This method can be used to set up the initial animation(s) of the skeleton,
-     *                      among other things.
-     */
-    public SpineController(SpineControllerCallback onInitialized) {
-        this.onInitialized = onInitialized;
-    }
-
-    protected void init(AndroidSkeletonDrawable drawable) {
-        this.drawable = drawable;
-        if (onInitialized != null) {
-            onInitialized.execute(this);
-        }
-    }
-
-    /**
-     * The {@link AndroidTextureAtlas} from which images to render the skeleton are sourced.
-     */
-    public AndroidTextureAtlas getAtlas() {
-        if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
-        return drawable.getAtlas();
-    }
-
-    /**
-     * The setup-pose data used by the skeleton.
-     */
-    public SkeletonData getSkeletonDate() {
-        if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
-        return drawable.getSkeletonData();
-    }
-
-    /**
-     * The {@link Skeleton}.
-     */
-    public Skeleton getSkeleton() {
-        if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
-        return drawable.getSkeleton();
-    }
-
-    /**
-     * The mixing information used by the {@link AnimationState}.
-     */
-    public AnimationStateData getAnimationStateData() {
-        if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
-        return drawable.getAnimationStateData();
-    }
-
-    /**
-     * The {@link AnimationState} used to manage animations that are being applied to the
-     * skeleton.
-     */
-    public AnimationState getAnimationState() {
-        if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
-        return drawable.getAnimationState();
-    }
-
-    /**
-     * The {@link AndroidSkeletonDrawable}.
-     */
-    public AndroidSkeletonDrawable getDrawable() {
-        if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
-        return drawable;
-    }
-
-    /**
-     * Checks if the {@link  SpineView} is initialized.
-     */
-    public boolean isInitialized() {
-        return drawable != null;
-    }
-
-    /**
-     * Checks if the animation is currently playing.
-     */
-    public boolean isPlaying() {
-        return playing;
-    }
-
-    /**
-     * Pauses updating and rendering the skeleton.
-     */
-    public void pause() {
-        if (playing) {
-            playing = false;
-        }
-    }
-
-    /**
-     * Resumes updating and rendering the skeleton.
-     */
-    public void resume() {
-        if (!playing) {
-            playing = true;
-        }
-    }
-
-    /**
-     * Transforms the coordinates given in the {@link SpineView} coordinate system in {@code position} to
-     * the skeleton coordinate system. See the {@code IKFollowing.kt} example for how to use this
-     * to move a bone based on user touch input.
-     */
-    public Point toSkeletonCoordinates(Point position) {
-        int x = position.x;
-        int y = position.y;
-        return new Point((int) (x / scaleX - offsetX), (int) (y / scaleY - offsetY));
-    }
-
-    /**
-     * Sets the {@code onBeforeUpdateWorldTransforms} callback. It is called before the skeleton's current pose is calculated
-     * using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the skeleton before the pose calculation.
-     */
-    public void setOnBeforeUpdateWorldTransforms(@Nullable SpineControllerCallback onBeforeUpdateWorldTransforms) {
-        this.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
-    }
-
-    /**
-     * Sets the {@code onAfterUpdateWorldTransforms} callback. This method is called after the skeleton's current pose is calculated using
-     * {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the current pose before rendering the skeleton.
-     */
-    public void setOnAfterUpdateWorldTransforms(@Nullable SpineControllerCallback onAfterUpdateWorldTransforms) {
-        this.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
-    }
-
-    /**
-     * Sets the {@code onBeforePaint} callback. It is called before the skeleton's current pose is rendered by the
-     * {@link SpineView}. It allows rendering backgrounds or other objects that should go behind the skeleton on the
-     * {@link Canvas}.
-     */
-    public void setOnBeforePaint(@Nullable SpineControllerBeforePaintCallback onBeforePaint) {
-        this.onBeforePaint = onBeforePaint;
-    }
-
-    /**
-     * Sets the {@code onAfterPaint} callback. It is called after the skeleton's current pose is rendered by the
-     * {@link SpineView}. It allows rendering additional objects on top of the skeleton.
-     */
-    public void setOnAfterPaint(@Nullable SpineControllerAfterPaintCallback onAfterPaint) {
-        this.onAfterPaint = onAfterPaint;
-    }
-
-    protected void setCoordinateTransform(double offsetX, double offsetY, double scaleX, double scaleY) {
-        this.offsetX = offsetX;
-        this.offsetY = offsetY;
-        this.scaleX = scaleX;
-        this.scaleY = scaleY;
-    }
-
-    protected void callOnBeforeUpdateWorldTransforms() {
-        if (onBeforeUpdateWorldTransforms != null) {
-            onBeforeUpdateWorldTransforms.execute(this);
-        }
-    }
-
-    protected void callOnAfterUpdateWorldTransforms() {
-        if (onAfterUpdateWorldTransforms != null) {
-            onAfterUpdateWorldTransforms.execute(this);
-        }
-    }
-
-    protected void callOnBeforePaint(Canvas canvas) {
-        if (onBeforePaint != null) {
-            onBeforePaint.execute(this, canvas);
-        }
-    }
-
-    protected void callOnAfterPaint(Canvas canvas, Array<SkeletonRenderer.RenderCommand> renderCommands) {
-        if (onAfterPaint != null) {
-            onAfterPaint.execute(this, canvas, renderCommands);
-        }
-    }
+	/** Used to build {@link SpineController} instances. */
+	public static class Builder {
+		private final SpineControllerCallback onInitialized;
+		private SpineControllerCallback onBeforeUpdateWorldTransforms;
+		private SpineControllerCallback onAfterUpdateWorldTransforms;
+		private SpineControllerBeforePaintCallback onBeforePaint;
+		private SpineControllerAfterPaintCallback onAfterPaint;
+
+		/** Instantiate a {@link Builder} used to build a {@link SpineController}, which controls how the skeleton of a
+		 * {@link SpineView} is animated and rendered. Upon initialization of a {@link SpineView}, the provided
+		 * {@code onInitialized} callback method is called once. This method can be used to set up the initial animation(s) of the
+		 * skeleton, among other things.
+		 *
+		 * @param onInitialized Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback method is
+		 *           called once. This method can be used to set up the initial animation(s) of the skeleton, among other things. */
+		public Builder (SpineControllerCallback onInitialized) {
+			this.onInitialized = onInitialized;
+		}
+
+		/** Sets the {@code onBeforeUpdateWorldTransforms} callback. It is called before the skeleton's current pose is calculated
+		 * using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the skeleton before the pose
+		 * calculation. */
+		public Builder setOnBeforeUpdateWorldTransforms (SpineControllerCallback onBeforeUpdateWorldTransforms) {
+			this.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
+			return this;
+		}
+
+		/** Sets the {@code onAfterUpdateWorldTransforms} callback. This method is called after the skeleton's current pose is
+		 * calculated using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the current pose
+		 * before rendering the skeleton. */
+		public Builder setOnAfterUpdateWorldTransforms (SpineControllerCallback onAfterUpdateWorldTransforms) {
+			this.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
+			return this;
+		}
+
+		/** Sets the {@code onBeforePaint} callback. It is called before the skeleton's current pose is rendered by the
+		 * {@link SpineView}. It allows rendering backgrounds or other objects that should go behind the skeleton on the
+		 * {@link Canvas}. */
+		public Builder setOnBeforePaint (SpineControllerBeforePaintCallback onBeforePaint) {
+			this.onBeforePaint = onBeforePaint;
+			return this;
+		}
+
+		/** Sets the {@code onAfterPaint} callback. It is called after the skeleton's current pose is rendered by the
+		 * {@link SpineView}. It allows rendering additional objects on top of the skeleton. */
+		public Builder setOnAfterPaint (SpineControllerAfterPaintCallback onAfterPaint) {
+			this.onAfterPaint = onAfterPaint;
+			return this;
+		}
+
+		public SpineController build () {
+			SpineController spineController = new SpineController(onInitialized);
+			spineController.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
+			spineController.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
+			spineController.onBeforePaint = onBeforePaint;
+			spineController.onAfterPaint = onAfterPaint;
+			return spineController;
+		}
+	}
+
+	private final SpineControllerCallback onInitialized;
+	private @Nullable SpineControllerCallback onBeforeUpdateWorldTransforms;
+	private @Nullable SpineControllerCallback onAfterUpdateWorldTransforms;
+	private @Nullable SpineControllerBeforePaintCallback onBeforePaint;
+	private @Nullable SpineControllerAfterPaintCallback onAfterPaint;
+	private AndroidSkeletonDrawable drawable;
+	private boolean playing = true;
+	private double offsetX = 0;
+	private double offsetY = 0;
+	private double scaleX = 1;
+	private double scaleY = 1;
+
+	/** Instantiate a {@link SpineController}, which controls how the skeleton of a {@link SpineView} is animated and rendered.
+	 * Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback method is called once. This method
+	 * can be used to set up the initial animation(s) of the skeleton, among other things.
+	 *
+	 * @param onInitialized Upon initialization of a {@link SpineView}, the provided {@code onInitialized} callback method is
+	 *           called once. This method can be used to set up the initial animation(s) of the skeleton, among other things. */
+	public SpineController (SpineControllerCallback onInitialized) {
+		this.onInitialized = onInitialized;
+	}
+
+	protected void init (AndroidSkeletonDrawable drawable) {
+		this.drawable = drawable;
+		if (onInitialized != null) {
+			onInitialized.execute(this);
+		}
+	}
+
+	/** The {@link AndroidTextureAtlas} from which images to render the skeleton are sourced. */
+	public AndroidTextureAtlas getAtlas () {
+		if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
+		return drawable.getAtlas();
+	}
+
+	/** The setup-pose data used by the skeleton. */
+	public SkeletonData getSkeletonDate () {
+		if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
+		return drawable.getSkeletonData();
+	}
+
+	/** The {@link Skeleton}. */
+	public Skeleton getSkeleton () {
+		if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
+		return drawable.getSkeleton();
+	}
+
+	/** The mixing information used by the {@link AnimationState}. */
+	public AnimationStateData getAnimationStateData () {
+		if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
+		return drawable.getAnimationStateData();
+	}
+
+	/** The {@link AnimationState} used to manage animations that are being applied to the skeleton. */
+	public AnimationState getAnimationState () {
+		if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
+		return drawable.getAnimationState();
+	}
+
+	/** The {@link AndroidSkeletonDrawable}. */
+	public AndroidSkeletonDrawable getDrawable () {
+		if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
+		return drawable;
+	}
+
+	/** Checks if the {@link SpineView} is initialized. */
+	public boolean isInitialized () {
+		return drawable != null;
+	}
+
+	/** Checks if the animation is currently playing. */
+	public boolean isPlaying () {
+		return playing;
+	}
+
+	/** Pauses updating and rendering the skeleton. */
+	public void pause () {
+		if (playing) {
+			playing = false;
+		}
+	}
+
+	/** Resumes updating and rendering the skeleton. */
+	public void resume () {
+		if (!playing) {
+			playing = true;
+		}
+	}
+
+	/** Transforms the coordinates given in the {@link SpineView} coordinate system in {@code position} to the skeleton coordinate
+	 * system. See the {@code IKFollowing.kt} example for how to use this to move a bone based on user touch input. */
+	public Point toSkeletonCoordinates (Point position) {
+		int x = position.x;
+		int y = position.y;
+		return new Point((int)(x / scaleX - offsetX), (int)(y / scaleY - offsetY));
+	}
+
+	/** Sets the {@code onBeforeUpdateWorldTransforms} callback. It is called before the skeleton's current pose is calculated
+	 * using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the skeleton before the pose
+	 * calculation. */
+	public void setOnBeforeUpdateWorldTransforms (@Nullable SpineControllerCallback onBeforeUpdateWorldTransforms) {
+		this.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
+	}
+
+	/** Sets the {@code onAfterUpdateWorldTransforms} callback. This method is called after the skeleton's current pose is
+	 * calculated using {@link Skeleton#updateWorldTransform(Skeleton.Physics)}. It can be used to modify the current pose before
+	 * rendering the skeleton. */
+	public void setOnAfterUpdateWorldTransforms (@Nullable SpineControllerCallback onAfterUpdateWorldTransforms) {
+		this.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
+	}
+
+	/** Sets the {@code onBeforePaint} callback. It is called before the skeleton's current pose is rendered by the
+	 * {@link SpineView}. It allows rendering backgrounds or other objects that should go behind the skeleton on the
+	 * {@link Canvas}. */
+	public void setOnBeforePaint (@Nullable SpineControllerBeforePaintCallback onBeforePaint) {
+		this.onBeforePaint = onBeforePaint;
+	}
+
+	/** Sets the {@code onAfterPaint} callback. It is called after the skeleton's current pose is rendered by the
+	 * {@link SpineView}. It allows rendering additional objects on top of the skeleton. */
+	public void setOnAfterPaint (@Nullable SpineControllerAfterPaintCallback onAfterPaint) {
+		this.onAfterPaint = onAfterPaint;
+	}
+
+	protected void setCoordinateTransform (double offsetX, double offsetY, double scaleX, double scaleY) {
+		this.offsetX = offsetX;
+		this.offsetY = offsetY;
+		this.scaleX = scaleX;
+		this.scaleY = scaleY;
+	}
+
+	protected void callOnBeforeUpdateWorldTransforms () {
+		if (onBeforeUpdateWorldTransforms != null) {
+			onBeforeUpdateWorldTransforms.execute(this);
+		}
+	}
+
+	protected void callOnAfterUpdateWorldTransforms () {
+		if (onAfterUpdateWorldTransforms != null) {
+			onAfterUpdateWorldTransforms.execute(this);
+		}
+	}
+
+	protected void callOnBeforePaint (Canvas canvas) {
+		if (onBeforePaint != null) {
+			onBeforePaint.execute(this, canvas);
+		}
+	}
+
+	protected void callOnAfterPaint (Canvas canvas, Array<SkeletonRenderer.RenderCommand> renderCommands) {
+		if (onAfterPaint != null) {
+			onAfterPaint.execute(this, canvas, renderCommands);
+		}
+	}
 }

+ 123 - 178
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/SpineView.java

@@ -51,20 +51,20 @@ import androidx.annotation.NonNull;
 import java.io.File;
 import java.net.URL;
 
-/**
- * A {@link View} to display a Spine skeleton. The skeleton can be loaded from an asset bundle ({@link SpineView#loadFromAssets(String, String, Context, SpineController)}),
- * local files ({@link SpineView#loadFromFile(File, File, Context, SpineController)}), URLs ({@link SpineView#loadFromHttp(URL, URL, File, Context, SpineController)}), or a pre-loaded {@link AndroidSkeletonDrawable} using ({@link SpineView#loadFromDrawable(AndroidSkeletonDrawable, Context, SpineController)}).
+/** A {@link View} to display a Spine skeleton. The skeleton can be loaded from an asset bundle
+ * ({@link SpineView#loadFromAssets(String, String, Context, SpineController)}), local files
+ * ({@link SpineView#loadFromFile(File, File, Context, SpineController)}), URLs
+ * ({@link SpineView#loadFromHttp(URL, URL, File, Context, SpineController)}), or a pre-loaded {@link AndroidSkeletonDrawable}
+ * using ({@link SpineView#loadFromDrawable(AndroidSkeletonDrawable, Context, SpineController)}).
  *
  * The skeleton displayed by a {@link SpineView} can be controlled via a {@link SpineController}.
  *
- * The size of the widget can be derived from the bounds provided by a {@link BoundsProvider}. If the widget is not sized by the bounds
- * computed by the {@link BoundsProvider}, the widget will use the computed bounds to fit the skeleton inside the widget's dimensions.
- */
+ * The size of the widget can be derived from the bounds provided by a {@link BoundsProvider}. If the widget is not sized by the
+ * bounds computed by the {@link BoundsProvider}, the widget will use the computed bounds to fit the skeleton inside the widget's
+ * dimensions. */
 public class SpineView extends View implements Choreographer.FrameCallback {
 
-	/**
-	 * Used to build {@link SpineView} instances.
-	 * */
+	/** Used to build {@link SpineView} instances. */
 	public static class Builder {
 		private final Context context;
 		private final SpineController controller;
@@ -80,90 +80,72 @@ public class SpineView extends View implements Choreographer.FrameCallback {
 		private Alignment alignment = Alignment.CENTER;
 		private ContentMode contentMode = ContentMode.FIT;
 
-		/**
-		 * Instantiate a {@link Builder} used to build a {@link SpineView}, which is a {@link View} to display a Spine skeleton.
+		/** Instantiate a {@link Builder} used to build a {@link SpineView}, which is a {@link View} to display a Spine skeleton.
 		 *
-		 * @param controller The skeleton displayed by a {@link SpineView} can be controlled via a {@link SpineController}.
-		 */
-		public Builder(Context context, SpineController controller) {
+		 * @param controller The skeleton displayed by a {@link SpineView} can be controlled via a {@link SpineController}. */
+		public Builder (Context context, SpineController controller) {
 			this.context = context;
 			this.controller = controller;
 		}
 
-		/**
-		 * Loads assets from your app assets for the {@link SpineView} if set. The {@code atlasFileName} specifies the
-		 * `.atlas` file to be loaded for the images used to render the skeleton. The {@code skeletonFileName} specifies either a Skeleton `.json` or
-		 * `.skel` file containing the skeleton data.
-		 */
-		public Builder setLoadFromAssets(String atlasFileName, String skeletonFileName) {
+		/** Loads assets from your app assets for the {@link SpineView} if set. The {@code atlasFileName} specifies the `.atlas`
+		 * file to be loaded for the images used to render the skeleton. The {@code skeletonFileName} specifies either a Skeleton
+		 * `.json` or `.skel` file containing the skeleton data. */
+		public Builder setLoadFromAssets (String atlasFileName, String skeletonFileName) {
 			this.atlasFileName = atlasFileName;
 			this.skeletonFileName = skeletonFileName;
 			return this;
 		}
 
-		/**
-		 * Loads assets from files for the {@link SpineView} if set. The {@code atlasFile} specifies the `.atlas` file to be loaded for the images used to render
-		 * the skeleton. The {@code skeletonFile} specifies either a Skeleton `.json` or `.skel` file containing the skeleton data.
-		 */
-		public Builder setLoadFromFile(File atlasFile, File skeletonFile) {
+		/** Loads assets from files for the {@link SpineView} if set. The {@code atlasFile} specifies the `.atlas` file to be loaded
+		 * for the images used to render the skeleton. The {@code skeletonFile} specifies either a Skeleton `.json` or `.skel` file
+		 * containing the skeleton data. */
+		public Builder setLoadFromFile (File atlasFile, File skeletonFile) {
 			this.atlasFile = atlasFile;
 			this.skeletonFile = skeletonFile;
 			return this;
 		}
 
-		/**
-		 * Loads assets from http for the {@link SpineView} if set. The {@code atlasUrl} specifies the `.atlas` url to be loaded for the images used to render
-		 * the skeleton. The {@code skeletonUrl} specifies either a Skeleton `.json` or `.skel` url containing the skeleton data.
-		 */
-		public Builder setLoadFromHttp(URL atlasUrl, URL skeletonUrl, File targetDirectory) {
+		/** Loads assets from http for the {@link SpineView} if set. The {@code atlasUrl} specifies the `.atlas` url to be loaded
+		 * for the images used to render the skeleton. The {@code skeletonUrl} specifies either a Skeleton `.json` or `.skel` url
+		 * containing the skeleton data. */
+		public Builder setLoadFromHttp (URL atlasUrl, URL skeletonUrl, File targetDirectory) {
 			this.atlasUrl = atlasUrl;
 			this.skeletonUrl = skeletonUrl;
 			this.targetDirectory = targetDirectory;
 			return this;
 		}
 
-		/**
-		 * Uses the {@link AndroidSkeletonDrawable} for the {@link SpineView} if set.
-		 */
-		public Builder setLoadFromDrawable(AndroidSkeletonDrawable drawable) {
+		/** Uses the {@link AndroidSkeletonDrawable} for the {@link SpineView} if set. */
+		public Builder setLoadFromDrawable (AndroidSkeletonDrawable drawable) {
 			this.drawable = drawable;
 			return this;
 		}
 
-		/**
-		 * Get the {@link BoundsProvider} used to compute the bounds of the {@link Skeleton} inside the view.
-		 * The default is {@link SetupPoseBounds}.
-		 */
-		public Builder setBoundsProvider(BoundsProvider boundsProvider) {
+		/** Get the {@link BoundsProvider} used to compute the bounds of the {@link Skeleton} inside the view. The default is
+		 * {@link SetupPoseBounds}. */
+		public Builder setBoundsProvider (BoundsProvider boundsProvider) {
 			this.boundsProvider = boundsProvider;
 			return this;
 		}
 
-		/**
-		 * Get the {@link  ContentMode} used to fit the {@link Skeleton} inside the view.
-		 * The default is {@link  ContentMode#FIT}.
-		 */
-		public Builder setContentMode(ContentMode contentMode) {
+		/** Get the {@link ContentMode} used to fit the {@link Skeleton} inside the view. The default is {@link ContentMode#FIT}. */
+		public Builder setContentMode (ContentMode contentMode) {
 			this.contentMode = contentMode;
 			return this;
 		}
 
-		/**
-		 * Set the {@link  Alignment} used to align the {@link Skeleton} inside the view.
-		 * The default is {@link  Alignment#CENTER}
-		 */
-		public Builder setAlignment(Alignment alignment) {
+		/** Set the {@link Alignment} used to align the {@link Skeleton} inside the view. The default is {@link Alignment#CENTER} */
+		public Builder setAlignment (Alignment alignment) {
 			this.alignment = alignment;
 			return this;
 		}
 
-		/**
-		 * Builds a new {@link SpineView}.
+		/** Builds a new {@link SpineView}.
 		 *
-		 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController} semantics, to allow
-		 * modifying how the skeleton inside the widget is animated and rendered.
-		 */
-		public SpineView build() {
+		 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController}
+		 * semantics, to allow modifying how the skeleton inside the widget is animated and rendered. */
+		public SpineView build () {
 			SpineView spineView = new SpineView(context, controller);
 			spineView.boundsProvider = boundsProvider;
 			spineView.alignment = alignment;
@@ -198,201 +180,162 @@ public class SpineView extends View implements Choreographer.FrameCallback {
 	private Alignment alignment = Alignment.CENTER;
 	private ContentMode contentMode = ContentMode.FIT;
 
-	/**
-	 * Constructs a new {@link SpineView}.
+	/** Constructs a new {@link SpineView}.
 	 *
-	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController} semantics, to allow
-	 * modifying how the skeleton inside the widget is animated and rendered.
-	 */
+	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController}
+	 * semantics, to allow modifying how the skeleton inside the widget is animated and rendered. */
 	public SpineView (Context context, SpineController controller) {
 		super(context);
 		this.controller = controller;
 	}
 
-	/**
-	 * Constructs a new {@link SpineView} without providing a {@link SpineController}, which you need to provide using
-	 * {@link SpineView#setController(SpineController)}.
-	 */
+	/** Constructs a new {@link SpineView} without providing a {@link SpineController}, which you need to provide using
+	 * {@link SpineView#setController(SpineController)}. */
 	public SpineView (Context context, AttributeSet attrs) {
 		super(context, attrs);
 		// Set properties by view id
 	}
 
-	/**
-	 * Constructs a new {@link SpineView} without providing a {@link SpineController}, which you need to provide using
-	 * {@link SpineView#setController(SpineController)}.
-	 */
+	/** Constructs a new {@link SpineView} without providing a {@link SpineController}, which you need to provide using
+	 * {@link SpineView#setController(SpineController)}. */
 	public SpineView (Context context, AttributeSet attrs, int defStyle) {
 		super(context, attrs, defStyle);
 		// Set properties by view id
 	}
 
-	/**
-	 * Constructs a new {@link SpineView} from files in your app assets. The {@code atlasFileName} specifies the
-	 * `.atlas` file to be loaded for the images used to render the skeleton. The {@code skeletonFileName} specifies either a Skeleton `.json` or
+	/** Constructs a new {@link SpineView} from files in your app assets. The {@code atlasFileName} specifies the `.atlas` file to
+	 * be loaded for the images used to render the skeleton. The {@code skeletonFileName} specifies either a Skeleton `.json` or
 	 * `.skel` file containing the skeleton data.
 	 *
-	 * After initialization is complete, the provided {@code controller} is invoked as per the {@link SpineController} semantics, to allow
-	 * modifying how the skeleton inside the widget is animated and rendered.
-	 */
-	public static SpineView loadFromAssets(String atlasFileName, String skeletonFileName, Context context, SpineController controller) {
+	 * After initialization is complete, the provided {@code controller} is invoked as per the {@link SpineController} semantics,
+	 * to allow modifying how the skeleton inside the widget is animated and rendered. */
+	public static SpineView loadFromAssets (String atlasFileName, String skeletonFileName, Context context,
+		SpineController controller) {
 		SpineView spineView = new SpineView(context, controller);
 		spineView.loadFromAsset(atlasFileName, skeletonFileName);
 		return spineView;
 	}
 
-	/**
-	 * Constructs a new {@link SpineView} from files. The {@code atlasFile} specifies the `.atlas` file to be loaded for the images used to render
-	 * the skeleton. The {@code skeletonFile} specifies either a Skeleton `.json` or `.skel` file containing the skeleton data.
+	/** Constructs a new {@link SpineView} from files. The {@code atlasFile} specifies the `.atlas` file to be loaded for the
+	 * images used to render the skeleton. The {@code skeletonFile} specifies either a Skeleton `.json` or `.skel` file containing
+	 * the skeleton data.
 	 *
-	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController} semantics, to allow
-	 * modifying how the skeleton inside the widget is animated and rendered.
-	 */
-	public static SpineView loadFromFile(File atlasFile, File skeletonFile, Context context, SpineController controller) {
+	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController}
+	 * semantics, to allow modifying how the skeleton inside the widget is animated and rendered. */
+	public static SpineView loadFromFile (File atlasFile, File skeletonFile, Context context, SpineController controller) {
 		SpineView spineView = new SpineView(context, controller);
 		spineView.loadFromFile(atlasFile, skeletonFile);
 		return spineView;
 	}
 
-	/**
-	 * Constructs a new {@link SpineView} from HTTP URLs. The {@code atlasUrl} specifies the `.atlas` url to be loaded for the images used to render
-	 * the skeleton. The {@code skeletonUrl} specifies either a Skeleton `.json` or `.skel` url containing the skeleton data.
+	/** Constructs a new {@link SpineView} from HTTP URLs. The {@code atlasUrl} specifies the `.atlas` url to be loaded for the
+	 * images used to render the skeleton. The {@code skeletonUrl} specifies either a Skeleton `.json` or `.skel` url containing
+	 * the skeleton data.
 	 *
-	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController} semantics, to allow
-	 * modifying how the skeleton inside the widget is animated and rendered.
-	 */
-	public static SpineView loadFromHttp(URL atlasUrl, URL skeletonUrl, File targetDirectory, Context context, SpineController controller) {
+	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController}
+	 * semantics, to allow modifying how the skeleton inside the widget is animated and rendered. */
+	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;
 	}
 
-	/**
-	 * Constructs a new {@link SpineView} from a {@link AndroidSkeletonDrawable}.
+	/** Constructs a new {@link SpineView} from a {@link AndroidSkeletonDrawable}.
 	 *
-	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController} semantics, to allow
-	 * modifying how the skeleton inside the widget is animated and rendered.
-	 */
-	public static SpineView loadFromDrawable(AndroidSkeletonDrawable drawable, Context context, SpineController controller) {
+	 * After initialization is complete, the provided {@code SpineController} is invoked as per the {@link SpineController}
+	 * semantics, to allow modifying how the skeleton inside the widget is animated and rendered. */
+	public static SpineView loadFromDrawable (AndroidSkeletonDrawable drawable, Context context, SpineController controller) {
 		SpineView spineView = new SpineView(context, controller);
 		spineView.loadFromDrawable(drawable);
 		return spineView;
 	}
 
-	/**
-	 * The same as {@link SpineView#loadFromAssets(String, String, Context, SpineController)}, but can be used after
-	 * instantiating the view via {@link SpineView#SpineView(Context, SpineController)}.
-	 */
-	public void loadFromAsset(String atlasFileName, String skeletonFileName) {
-		loadFrom(() -> AndroidSkeletonDrawable.fromAsset(atlasFileName, skeletonFileName, getContext()));
+	/** The same as {@link SpineView#loadFromAssets(String, String, Context, SpineController)}, but can be used after instantiating
+	 * the view via {@link SpineView#SpineView(Context, SpineController)}. */
+	public void loadFromAsset (String atlasFileName, String skeletonFileName) {
+		loadFrom( () -> AndroidSkeletonDrawable.fromAsset(atlasFileName, skeletonFileName, getContext()));
 	}
 
-	/**
-	 * The same as {@link SpineView#loadFromFile(File, File, Context, SpineController)}, but can be used after
-	 * instantiating the view via {@link SpineView#SpineView(Context, SpineController)}.
-	 */
-	public void loadFromFile(File atlasFile, File skeletonFile) {
-		loadFrom(() -> AndroidSkeletonDrawable.fromFile(atlasFile, skeletonFile));
+	/** The same as {@link SpineView#loadFromFile(File, File, Context, SpineController)}, but can be used after instantiating the
+	 * view via {@link SpineView#SpineView(Context, SpineController)}. */
+	public void loadFromFile (File atlasFile, File skeletonFile) {
+		loadFrom( () -> AndroidSkeletonDrawable.fromFile(atlasFile, skeletonFile));
 	}
 
-	/**
-	 * The same as {@link SpineView#loadFromHttp(URL, URL, File, Context, SpineController)}, but can be used after
-	 * instantiating the view via {@link SpineView#SpineView(Context, SpineController)}.
-	 */
-	public void loadFromHttp(URL atlasUrl, URL skeletonUrl, File targetDirectory) {
-		loadFrom(() -> AndroidSkeletonDrawable.fromHttp(atlasUrl, skeletonUrl, targetDirectory));
+	/** The same as {@link SpineView#loadFromHttp(URL, URL, File, Context, SpineController)}, but can be used after instantiating
+	 * the view via {@link SpineView#SpineView(Context, SpineController)}. */
+	public void loadFromHttp (URL atlasUrl, URL skeletonUrl, File targetDirectory) {
+		loadFrom( () -> AndroidSkeletonDrawable.fromHttp(atlasUrl, skeletonUrl, targetDirectory));
 	}
 
-	/**
-	 * The same as {@link SpineView#loadFromDrawable(AndroidSkeletonDrawable, Context, SpineController)}, but can be used after
-	 * instantiating the view via {@link SpineView#SpineView(Context, SpineController)}.
-	 */
-	public void loadFromDrawable(AndroidSkeletonDrawable drawable) {
-		loadFrom(() -> drawable);
+	/** The same as {@link SpineView#loadFromDrawable(AndroidSkeletonDrawable, Context, SpineController)}, but can be used after
+	 * instantiating the view via {@link SpineView#SpineView(Context, SpineController)}. */
+	public void loadFromDrawable (AndroidSkeletonDrawable drawable) {
+		loadFrom( () -> drawable);
 	}
 
-	/**
-	 * Get the {@link  SpineController}
-	 */
-	public SpineController getController() {
+	/** Get the {@link SpineController} */
+	public SpineController getController () {
 		return controller;
 	}
 
-	/**
-	 * Set the {@link  SpineController}. Only do this if you use {@link SpineView#SpineView(Context, AttributeSet)},
-	 * {@link SpineView#SpineView(Context, AttributeSet, int)}, or create the {@link SpineView} in an XML layout.
-	 */
-	public void setController(SpineController controller) {
+	/** Set the {@link SpineController}. Only do this if you use {@link SpineView#SpineView(Context, AttributeSet)},
+	 * {@link SpineView#SpineView(Context, AttributeSet, int)}, or create the {@link SpineView} in an XML layout. */
+	public void setController (SpineController controller) {
 		this.controller = controller;
 	}
 
-	/**
-	 * Get the {@link  Alignment} used to align the {@link Skeleton} inside the view.
-	 * The default is {@link  Alignment#CENTER}
-	 */
-	public Alignment getAlignment() {
+	/** Get the {@link Alignment} used to align the {@link Skeleton} inside the view. The default is {@link Alignment#CENTER} */
+	public Alignment getAlignment () {
 		return alignment;
 	}
 
-	/**
-	 * Set the {@link  Alignment}.
-	 */
-	public void setAlignment(Alignment alignment) {
+	/** Set the {@link Alignment}. */
+	public void setAlignment (Alignment alignment) {
 		this.alignment = alignment;
 		updateCanvasTransform();
 	}
 
-	/**
-	 * Get the {@link  ContentMode} used to fit the {@link Skeleton} inside the view.
-	 * The default is {@link  ContentMode#FIT}.
-	 */
-	public ContentMode getContentMode() {
+	/** Get the {@link ContentMode} used to fit the {@link Skeleton} inside the view. The default is {@link ContentMode#FIT}. */
+	public ContentMode getContentMode () {
 		return contentMode;
 	}
 
-	/**
-	 * Set the {@link  ContentMode}.
-	 */
-	public void setContentMode(ContentMode contentMode) {
+	/** Set the {@link ContentMode}. */
+	public void setContentMode (ContentMode contentMode) {
 		this.contentMode = contentMode;
 		updateCanvasTransform();
 	}
 
-	/**
-	 * Get the {@link BoundsProvider} used to compute the bounds of the {@link Skeleton} inside the view.
-	 * The default is {@link SetupPoseBounds}.
-	 */
-	public BoundsProvider getBoundsProvider() {
+	/** Get the {@link BoundsProvider} used to compute the bounds of the {@link Skeleton} inside the view. The default is
+	 * {@link SetupPoseBounds}. */
+	public BoundsProvider getBoundsProvider () {
 		return boundsProvider;
 	}
 
-	/**
-	 * Set the {@link  BoundsProvider}.
-	 */
-	public void setBoundsProvider(BoundsProvider boundsProvider) {
+	/** Set the {@link BoundsProvider}. */
+	public void setBoundsProvider (BoundsProvider boundsProvider) {
 		this.boundsProvider = boundsProvider;
 		updateCanvasTransform();
 	}
 
-	/**
-	 * Check if rendering is enabled.
-	 */
-	public Boolean isRendering() {
+	/** Check if rendering is enabled. */
+	public Boolean isRendering () {
 		return rendering;
 	}
 
-	/**
-	 * Set to disable or enable rendering. Disable it when the spine view is out of bounds and you want to preserve CPU/GPU resources.
-	 */
-	public void setRendering(Boolean rendering) {
+	/** Set to disable or enable rendering. Disable it when the spine view is out of bounds and you want to preserve CPU/GPU
+	 * resources. */
+	public void setRendering (Boolean rendering) {
 		this.rendering = rendering;
 	}
 
-	private void loadFrom(AndroidSkeletonDrawableLoader loader) {
+	private void loadFrom (AndroidSkeletonDrawableLoader loader) {
 		Handler mainHandler = new Handler(Looper.getMainLooper());
-		Thread backgroundThread = new Thread(() -> {
+		Thread backgroundThread = new Thread( () -> {
 			final AndroidSkeletonDrawable skeletonDrawable = loader.load();
-			mainHandler.post(() -> {
+			mainHandler.post( () -> {
 				computedBounds = boundsProvider.computeBounds(skeletonDrawable);
 				updateCanvasTransform();
 
@@ -431,28 +374,30 @@ public class SpineView extends View implements Choreographer.FrameCallback {
 	}
 
 	@Override
-	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+	protected void onSizeChanged (int w, int h, int oldw, int oldh) {
 		super.onSizeChanged(w, h, oldw, oldh);
 		updateCanvasTransform();
 	}
 
-	private void updateCanvasTransform() {
+	private void updateCanvasTransform () {
 		if (controller == null) {
 			return;
 		}
-		x = (float) (-computedBounds.getX() - computedBounds.getWidth() / 2.0 - (alignment.getX() * computedBounds.getWidth() / 2.0));
-		y = (float) (-computedBounds.getY() - computedBounds.getHeight() / 2.0 - (alignment.getY() * computedBounds.getHeight() / 2.0));
+		x = (float)(-computedBounds.getX() - computedBounds.getWidth() / 2.0
+			- (alignment.getX() * computedBounds.getWidth() / 2.0));
+		y = (float)(-computedBounds.getY() - computedBounds.getHeight() / 2.0
+			- (alignment.getY() * computedBounds.getHeight() / 2.0));
 
 		switch (contentMode) {
-			case FIT:
-				scaleX = scaleY = (float) Math.min(getWidth() / computedBounds.getWidth(), getHeight() / computedBounds.getHeight());
-				break;
-			case FILL:
-				scaleX = scaleY = (float) Math.max(getWidth() / computedBounds.getWidth(), getHeight() / computedBounds.getHeight());
-				break;
+		case FIT:
+			scaleX = scaleY = (float)Math.min(getWidth() / computedBounds.getWidth(), getHeight() / computedBounds.getHeight());
+			break;
+		case FILL:
+			scaleX = scaleY = (float)Math.max(getWidth() / computedBounds.getWidth(), getHeight() / computedBounds.getHeight());
+			break;
 		}
-		offsetX = (float) (getWidth() / 2.0 + (alignment.getX() * getWidth() / 2.0));
-		offsetY = (float) (getHeight() / 2.0 + (alignment.getY() * getHeight() / 2.0));
+		offsetX = (float)(getWidth() / 2.0 + (alignment.getX() * getWidth() / 2.0));
+		offsetY = (float)(getHeight() / 2.0 + (alignment.getY() * getHeight() / 2.0));
 
 		controller.setCoordinateTransform(x + offsetX / scaleX, y + offsetY / scaleY, scaleX, scaleY);
 	}

+ 15 - 24
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/Alignment.java

@@ -29,33 +29,24 @@
 
 package com.esotericsoftware.spine.android.bounds;
 
-/**
- * How a view should be aligned within another view.
- */
+/** How a view should be aligned within another view. */
 public enum Alignment {
-    TOP_LEFT(-1.0f, -1.0f),
-    TOP_CENTER(0.0f, -1.0f),
-    TOP_RIGHT(1.0f, -1.0f),
-    CENTER_LEFT(-1.0f, 0.0f),
-    CENTER(0.0f, 0.0f),
-    CENTER_RIGHT(1.0f, 0.0f),
-    BOTTOM_LEFT(-1.0f, 1.0f),
-    BOTTOM_CENTER(0.0f, 1.0f),
-    BOTTOM_RIGHT(1.0f, 1.0f);
+	TOP_LEFT(-1.0f, -1.0f), TOP_CENTER(0.0f, -1.0f), TOP_RIGHT(1.0f, -1.0f), CENTER_LEFT(-1.0f, 0.0f), CENTER(0.0f,
+		0.0f), CENTER_RIGHT(1.0f, 0.0f), BOTTOM_LEFT(-1.0f, 1.0f), BOTTOM_CENTER(0.0f, 1.0f), BOTTOM_RIGHT(1.0f, 1.0f);
 
-    private final float x;
-    private final float y;
+	private final float x;
+	private final float y;
 
-    Alignment(float x, float y) {
-        this.x = x;
-        this.y = y;
-    }
+	Alignment (float x, float y) {
+		this.x = x;
+		this.y = y;
+	}
 
-    public float getX() {
-        return x;
-    }
+	public float getX () {
+		return x;
+	}
 
-    public float getY() {
-        return y;
-    }
+	public float getY () {
+		return y;
+	}
 }

+ 64 - 67
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/Bounds.java

@@ -33,72 +33,69 @@ import com.badlogic.gdx.math.Vector2;
 import com.badlogic.gdx.utils.FloatArray;
 import com.esotericsoftware.spine.Skeleton;
 
-/**
- * Bounds denoted by the top left corner coordinates {@code x} and {@code y}
- * and the {@code width} and {@code height}.
- */
+/** Bounds denoted by the top left corner coordinates {@code x} and {@code y} and the {@code width} and {@code height}. */
 public class Bounds {
-    private double x;
-    private double y;
-    private double width;
-    private double height;
-
-    public Bounds() {
-        this.x = 0;
-        this.y = 0;
-        this.width = 0;
-        this.height = 0;
-    }
-
-    public Bounds(double x, double y, double width, double height) {
-        this.x = x;
-        this.y = y;
-        this.width = width;
-        this.height = height;
-    }
-
-    public Bounds(Skeleton skeleton) {
-        Vector2 offset = new Vector2(0, 0);
-        Vector2 size = new Vector2(0, 0);
-        FloatArray floatArray = new FloatArray();
-
-        skeleton.getBounds(offset, size, floatArray);
-
-        x = offset.x;
-        y = offset.y;
-        width = size.x;
-        height = size.y;
-    }
-
-    public double getX() {
-        return x;
-    }
-
-    public void setX(double x) {
-        this.x = x;
-    }
-
-    public double getY() {
-        return y;
-    }
-
-    public void setY(double y) {
-        this.y = y;
-    }
-
-    public double getWidth() {
-        return width;
-    }
-
-    public void setWidth(double width) {
-        this.width = width;
-    }
-
-    public double getHeight() {
-        return height;
-    }
-
-    public void setHeight(double height) {
-        this.height = height;
-    }
+	private double x;
+	private double y;
+	private double width;
+	private double height;
+
+	public Bounds () {
+		this.x = 0;
+		this.y = 0;
+		this.width = 0;
+		this.height = 0;
+	}
+
+	public Bounds (double x, double y, double width, double height) {
+		this.x = x;
+		this.y = y;
+		this.width = width;
+		this.height = height;
+	}
+
+	public Bounds (Skeleton skeleton) {
+		Vector2 offset = new Vector2(0, 0);
+		Vector2 size = new Vector2(0, 0);
+		FloatArray floatArray = new FloatArray();
+
+		skeleton.getBounds(offset, size, floatArray);
+
+		x = offset.x;
+		y = offset.y;
+		width = size.x;
+		height = size.y;
+	}
+
+	public double getX () {
+		return x;
+	}
+
+	public void setX (double x) {
+		this.x = x;
+	}
+
+	public double getY () {
+		return y;
+	}
+
+	public void setY (double y) {
+		this.y = y;
+	}
+
+	public double getWidth () {
+		return width;
+	}
+
+	public void setWidth (double width) {
+		this.width = width;
+	}
+
+	public double getHeight () {
+		return height;
+	}
+
+	public void setHeight (double height) {
+		this.height = height;
+	}
 }

+ 3 - 5
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/BoundsProvider.java

@@ -31,10 +31,8 @@ package com.esotericsoftware.spine.android.bounds;
 
 import com.esotericsoftware.spine.android.AndroidSkeletonDrawable;
 
-/**
- * A {@link BoundsProvider} that calculates the bounding box of the skeleton based on the visible
- * attachments in the setup pose.
- */
+/** A {@link BoundsProvider} that calculates the bounding box of the skeleton based on the visible attachments in the setup
+ * pose. */
 public interface BoundsProvider {
-    Bounds computeBounds(AndroidSkeletonDrawable drawable);
+	Bounds computeBounds (AndroidSkeletonDrawable drawable);
 }

+ 5 - 11
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/ContentMode.java

@@ -29,16 +29,10 @@
 
 package com.esotericsoftware.spine.android.bounds;
 
-/**
- * How a view should be inscribed into another view.
- */
+/** How a view should be inscribed into another view. */
 public enum ContentMode {
-    /**
-     * As large as possible while still containing the source view entirely within the target view.
-     */
-    FIT,
-    /**
-     * Fill the target view by distorting the source's aspect ratio.
-     */
-    FILL
+	/** As large as possible while still containing the source view entirely within the target view. */
+	FIT,
+	/** Fill the target view by distorting the source's aspect ratio. */
+	FILL
 }

+ 15 - 17
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/RawBounds.java

@@ -31,24 +31,22 @@ package com.esotericsoftware.spine.android.bounds;
 
 import com.esotericsoftware.spine.android.AndroidSkeletonDrawable;
 
-/**
- * A {@link BoundsProvider} that returns fixed bounds.
- */
+/** A {@link BoundsProvider} that returns fixed bounds. */
 public class RawBounds implements BoundsProvider {
-    final Double x;
-    final Double y;
-    final Double width;
-    final Double height;
+	final Double x;
+	final Double y;
+	final Double width;
+	final Double height;
 
-    public RawBounds(Double x, Double y, Double width, Double height) {
-        this.x = x;
-        this.y = y;
-        this.width = width;
-        this.height = height;
-    }
+	public RawBounds (Double x, Double y, Double width, Double height) {
+		this.x = x;
+		this.y = y;
+		this.width = width;
+		this.height = height;
+	}
 
-    @Override
-    public Bounds computeBounds(AndroidSkeletonDrawable drawable) {
-        return new Bounds(x, y, width, height);
-    }
+	@Override
+	public Bounds computeBounds (AndroidSkeletonDrawable drawable) {
+		return new Bounds(x, y, width, height);
+	}
 }

+ 6 - 8
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/SetupPoseBounds.java

@@ -31,14 +31,12 @@ package com.esotericsoftware.spine.android.bounds;
 
 import com.esotericsoftware.spine.android.AndroidSkeletonDrawable;
 
-/**
- * A {@link BoundsProvider} that calculates the bounding box of the skeleton based on the visible
- * attachments in the setup pose.
- */
+/** A {@link BoundsProvider} that calculates the bounding box of the skeleton based on the visible attachments in the setup
+ * pose. */
 public class SetupPoseBounds implements BoundsProvider {
 
-    @Override
-    public Bounds computeBounds(AndroidSkeletonDrawable drawable) {
-        return new Bounds(drawable.getSkeleton());
-    }
+	@Override
+	public Bounds computeBounds (AndroidSkeletonDrawable drawable) {
+		return new Bounds(drawable.getSkeleton());
+	}
 }

+ 64 - 75
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/bounds/SkinAndAnimationBounds.java

@@ -37,86 +37,75 @@ import com.esotericsoftware.spine.android.AndroidSkeletonDrawable;
 import java.util.Collections;
 import java.util.List;
 
-/**
- * A {@link BoundsProvider} that calculates the bounding box needed for a combination of skins
- * and an animation.
- */
+/** A {@link BoundsProvider} that calculates the bounding box needed for a combination of skins and an animation. */
 public class SkinAndAnimationBounds implements BoundsProvider {
-    private final List<String> skins;
-    private final String animation;
-    private final double stepTime;
+	private final List<String> skins;
+	private final String animation;
+	private final double stepTime;
 
-    /**
-     * Constructs a new provider that will use the given {@code skins} and {@code animation} to calculate
-     * the bounding box of the skeleton. If no skins are given, the default skin is used.
-     * The {@code stepTime}, given in seconds, defines at what interval the bounds should be sampled
-     * across the entire animation.
-     */
-    public SkinAndAnimationBounds(List<String> skins, String animation, double stepTime) {
-        this.skins = (skins == null || skins.isEmpty()) ? Collections.singletonList("default") : skins;
-        this.animation = animation;
-        this.stepTime = stepTime;
-    }
+	/** Constructs a new provider that will use the given {@code skins} and {@code animation} to calculate the bounding box of the
+	 * skeleton. If no skins are given, the default skin is used. The {@code stepTime}, given in seconds, defines at what interval
+	 * the bounds should be sampled across the entire animation. */
+	public SkinAndAnimationBounds (List<String> skins, String animation, double stepTime) {
+		this.skins = (skins == null || skins.isEmpty()) ? Collections.singletonList("default") : skins;
+		this.animation = animation;
+		this.stepTime = stepTime;
+	}
 
-    /**
-     * Constructs a new provider that will use the given {@code skins} and {@code animation} to calculate
-     * the bounding box of the skeleton. If no skins are given, the default skin is used.
-     * The {@code stepTime} has default value 0.1.
-     */
-    public SkinAndAnimationBounds(List<String> skins, String animation) {
-        this(skins, animation, 0.1);
-    }
+	/** Constructs a new provider that will use the given {@code skins} and {@code animation} to calculate the bounding box of the
+	 * skeleton. If no skins are given, the default skin is used. The {@code stepTime} has default value 0.1. */
+	public SkinAndAnimationBounds (List<String> skins, String animation) {
+		this(skins, animation, 0.1);
+	}
 
-    /**
-     * Constructs a new provider that will use the given {@code skins} and {@code animation} to calculate
-     * the bounding box of the skeleton. The default skin is used. The {@code stepTime} has default value 0.1.
-     */
-    public SkinAndAnimationBounds(String animation) {
-        this(Collections.emptyList(), animation, 0.1);
-    }
+	/** Constructs a new provider that will use the given {@code skins} and {@code animation} to calculate the bounding box of the
+	 * skeleton. The default skin is used. The {@code stepTime} has default value 0.1. */
+	public SkinAndAnimationBounds (String animation) {
+		this(Collections.emptyList(), animation, 0.1);
+	}
 
-    @Override
-    public Bounds computeBounds(AndroidSkeletonDrawable drawable) {
-        SkeletonData data = drawable.getSkeletonData();
-        Skin oldSkin = drawable.getSkeleton().getSkin();
-        Skin customSkin = new Skin("custom-skin");
-        for (String skinName : skins) {
-            Skin skin = data.findSkin(skinName);
-            if (skin == null) continue;
-            customSkin.addSkin(skin);
-        }
-        drawable.getSkeleton().setSkin(customSkin);
-        drawable.getSkeleton().setToSetupPose();
+	@Override
+	public Bounds computeBounds (AndroidSkeletonDrawable drawable) {
+		SkeletonData data = drawable.getSkeletonData();
+		Skin oldSkin = drawable.getSkeleton().getSkin();
+		Skin customSkin = new Skin("custom-skin");
+		for (String skinName : skins) {
+			Skin skin = data.findSkin(skinName);
+			if (skin == null) continue;
+			customSkin.addSkin(skin);
+		}
+		drawable.getSkeleton().setSkin(customSkin);
+		drawable.getSkeleton().setToSetupPose();
 
-        Animation animation = (this.animation != null) ? data.findAnimation(this.animation) : null;
-        double minX = Double.POSITIVE_INFINITY;
-        double minY = Double.POSITIVE_INFINITY;
-        double maxX = Double.NEGATIVE_INFINITY;
-        double maxY = Double.NEGATIVE_INFINITY;
-        if (animation == null) {
-            Bounds bounds = new Bounds(drawable.getSkeleton());
-            minX = bounds.getX();
-            minY = bounds.getY();
-            maxX = minX + bounds.getWidth();
-            maxY = minY + bounds.getHeight();
-        } else {
-            drawable.getAnimationState().setAnimation(0, animation, false);
-            int steps = (int) Math.max( (animation.getDuration() / stepTime), 1.0);
-            for (int i = 0; i < steps; i++) {
-                drawable.update(i > 0 ? (float) stepTime : 0);
-                Bounds bounds = new Bounds(drawable.getSkeleton());
-                minX = Math.min(minX, bounds.getX());
-                minY = Math.min(minY, bounds.getY());
-                maxX = Math.max(maxX, minX + bounds.getWidth());
-                maxY = Math.max(maxY, minY + bounds.getHeight());
-            }
-        }
+		Animation animation = (this.animation != null) ? data.findAnimation(this.animation) : null;
+		double minX = Double.POSITIVE_INFINITY;
+		double minY = Double.POSITIVE_INFINITY;
+		double maxX = Double.NEGATIVE_INFINITY;
+		double maxY = Double.NEGATIVE_INFINITY;
+		if (animation == null) {
+			Bounds bounds = new Bounds(drawable.getSkeleton());
+			minX = bounds.getX();
+			minY = bounds.getY();
+			maxX = minX + bounds.getWidth();
+			maxY = minY + bounds.getHeight();
+		} else {
+			drawable.getAnimationState().setAnimation(0, animation, false);
+			int steps = (int)Math.max((animation.getDuration() / stepTime), 1.0);
+			for (int i = 0; i < steps; i++) {
+				drawable.update(i > 0 ? (float)stepTime : 0);
+				Bounds bounds = new Bounds(drawable.getSkeleton());
+				minX = Math.min(minX, bounds.getX());
+				minY = Math.min(minY, bounds.getY());
+				maxX = Math.max(maxX, minX + bounds.getWidth());
+				maxY = Math.max(maxY, minY + bounds.getHeight());
+			}
+		}
 
-        drawable.getSkeleton().setSkin("default");
-        drawable.getAnimationState().clearTracks();
-        if (oldSkin != null) drawable.getSkeleton().setSkin(oldSkin);
-        drawable.getSkeleton().setToSetupPose();
-        drawable.update(0);
-        return new Bounds(minX, minY, maxX - minX, maxY - minY);
-    }
+		drawable.getSkeleton().setSkin("default");
+		drawable.getAnimationState().clearTracks();
+		if (oldSkin != null) drawable.getSkeleton().setSkin(oldSkin);
+		drawable.getSkeleton().setToSetupPose();
+		drawable.update(0);
+		return new Bounds(minX, minY, maxX - minX, maxY - minY);
+	}
 }

+ 1 - 1
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/AndroidSkeletonDrawableLoader.java

@@ -33,5 +33,5 @@ import com.esotericsoftware.spine.android.AndroidSkeletonDrawable;
 
 @FunctionalInterface
 public interface AndroidSkeletonDrawableLoader {
-    AndroidSkeletonDrawable load();
+	AndroidSkeletonDrawable load ();
 }

+ 1 - 1
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/SpineControllerAfterPaintCallback.java

@@ -39,5 +39,5 @@ import java.util.List;
 
 @FunctionalInterface
 public interface SpineControllerAfterPaintCallback {
-    void execute (SpineController controller, Canvas canvas, Array<SkeletonRenderer.RenderCommand> commands);
+	void execute (SpineController controller, Canvas canvas, Array<SkeletonRenderer.RenderCommand> commands);
 }

+ 1 - 1
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/SpineControllerBeforePaintCallback.java

@@ -38,5 +38,5 @@ import java.util.List;
 
 @FunctionalInterface
 public interface SpineControllerBeforePaintCallback {
-    void execute (SpineController controller, Canvas canvas);
+	void execute (SpineController controller, Canvas canvas);
 }

+ 1 - 1
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/callbacks/SpineControllerCallback.java

@@ -33,5 +33,5 @@ import com.esotericsoftware.spine.android.SpineController;
 
 @FunctionalInterface
 public interface SpineControllerCallback {
-    void execute (SpineController controller);
+	void execute (SpineController controller);
 }

+ 54 - 59
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/HttpUtils.java

@@ -41,73 +41,68 @@ import java.net.HttpURLConnection;
 import java.net.URL;
 import java.nio.file.Files;
 
-/**
- * Helper to load http resources.
- */
+/** Helper to load http resources. */
 public class HttpUtils {
-    /**
-     * Download a file from an url into a target directory. It keeps the name from the {@code url}.
-     * This should NOT be executed on the main run loop.
-     */
-    public static File downloadFrom(URL url, File targetDirectory) throws RuntimeException {
-        HttpURLConnection urlConnection = null;
-        InputStream inputStream = null;
-        OutputStream outputStream = null;
+	/** Download a file from an url into a target directory. It keeps the name from the {@code url}. This should NOT be executed on
+	 * the main run loop. */
+	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();
+		try {
+			urlConnection = (HttpURLConnection)url.openConnection();
+			urlConnection.connect();
 
-            if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
-                throw new RuntimeException("Failed to connect: HTTP response code " + urlConnection.getResponseCode());
-            }
+			if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+				throw new RuntimeException("Failed to connect: HTTP response code " + urlConnection.getResponseCode());
+			}
 
-            inputStream = new BufferedInputStream(urlConnection.getInputStream());
+			inputStream = new BufferedInputStream(urlConnection.getInputStream());
 
-            String atlasUrlPath = url.getPath();
-            String fileName = atlasUrlPath.substring(atlasUrlPath.lastIndexOf('/') + 1);
-            File file = new File(targetDirectory, fileName);
+			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);
-            }
+			// 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;
+			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
-                }
-            }
+			// 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 (inputStream != null) {
+				try {
+					inputStream.close();
+				} catch (IOException e) {
+					// Nothing we can do
+				}
+			}
 
-            if (urlConnection != null) {
-                urlConnection.disconnect();
-            }
-        }
-    }
+			if (urlConnection != null) {
+				urlConnection.disconnect();
+			}
+		}
+	}
 }
-

+ 44 - 50
spine-android/spine-android/src/main/java/com/esotericsoftware/spine/android/utils/SkeletonDataUtils.java

@@ -46,63 +46,57 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 
-/**
- * Helper to load {@link SkeletonData} from assets.
- */
+/** Helper to load {@link SkeletonData} from assets. */
 public class SkeletonDataUtils {
 
-    /**
-     * Loads a {@link SkeletonData} from the file {@code skeletonFile} in assets using {@link Context}.
-     * Uses the provided {@link AndroidTextureAtlas} to resolve attachment images.
-     *
-     * Throws a {@link RuntimeException} in case the skeleton data could not be loaded.
-     */
-    public static SkeletonData fromAsset(AndroidTextureAtlas atlas, String skeletonFileName, Context context) {
-        AndroidAtlasAttachmentLoader attachmentLoader = new AndroidAtlasAttachmentLoader(atlas);
+	/** Loads a {@link SkeletonData} from the file {@code skeletonFile} in assets using {@link Context}. Uses the provided
+	 * {@link AndroidTextureAtlas} to resolve attachment images.
+	 *
+	 * Throws a {@link RuntimeException} in case the skeleton data could not be loaded. */
+	public static SkeletonData fromAsset (AndroidTextureAtlas atlas, String skeletonFileName, Context context) {
+		AndroidAtlasAttachmentLoader attachmentLoader = new AndroidAtlasAttachmentLoader(atlas);
 
-        SkeletonLoader skeletonLoader;
-        if (skeletonFileName.endsWith(".json")) {
-            skeletonLoader = new SkeletonJson(attachmentLoader);
-        } else {
-            skeletonLoader = new SkeletonBinary(attachmentLoader);
-        }
+		SkeletonLoader skeletonLoader;
+		if (skeletonFileName.endsWith(".json")) {
+			skeletonLoader = new SkeletonJson(attachmentLoader);
+		} else {
+			skeletonLoader = new SkeletonBinary(attachmentLoader);
+		}
 
-        SkeletonData skeletonData;
+		SkeletonData skeletonData;
 
-        AssetManager assetManager = context.getAssets();
-        try (InputStream in = new BufferedInputStream(assetManager.open(skeletonFileName))) {
-            skeletonData = skeletonLoader.readSkeletonData(in);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        return skeletonData;
-    }
+		AssetManager assetManager = context.getAssets();
+		try (InputStream in = new BufferedInputStream(assetManager.open(skeletonFileName))) {
+			skeletonData = skeletonLoader.readSkeletonData(in);
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+		return skeletonData;
+	}
 
-    /**
-     * Loads a {@link SkeletonData} from the file {@code skeletonFile}. Uses the provided {@link AndroidTextureAtlas} to resolve attachment images.
-     *
-     * Throws a {@link RuntimeException} in case the skeleton data could not be loaded.
-     */
-    public static SkeletonData fromFile(AndroidTextureAtlas atlas, File skeletonFile) {
-        AndroidAtlasAttachmentLoader attachmentLoader = new AndroidAtlasAttachmentLoader(atlas);
+	/** Loads a {@link SkeletonData} from the file {@code skeletonFile}. Uses the provided {@link AndroidTextureAtlas} to resolve
+	 * attachment images.
+	 *
+	 * Throws a {@link RuntimeException} in case the skeleton data could not be loaded. */
+	public static SkeletonData fromFile (AndroidTextureAtlas atlas, File skeletonFile) {
+		AndroidAtlasAttachmentLoader attachmentLoader = new AndroidAtlasAttachmentLoader(atlas);
 
-        SkeletonLoader skeletonLoader;
-        if (skeletonFile.getPath().endsWith(".json")) {
-            skeletonLoader = new SkeletonJson(attachmentLoader);
-        } else {
-            skeletonLoader = new SkeletonBinary(attachmentLoader);
-        }
+		SkeletonLoader skeletonLoader;
+		if (skeletonFile.getPath().endsWith(".json")) {
+			skeletonLoader = new SkeletonJson(attachmentLoader);
+		} else {
+			skeletonLoader = new SkeletonBinary(attachmentLoader);
+		}
 
-        return skeletonLoader.readSkeletonData(new FileHandle(skeletonFile));
-    }
+		return skeletonLoader.readSkeletonData(new FileHandle(skeletonFile));
+	}
 
-    /**
-     * Loads a {@link SkeletonData} from the URL {@code skeletonURL}. Uses the provided {@link AndroidTextureAtlas} to resolve attachment images.
-     *
-     * Throws a {@link RuntimeException} in case the skeleton data could not be loaded.
-     */
-    public static SkeletonData fromHttp(AndroidTextureAtlas atlas, URL skeletonUrl, File targetDirectory) {
-        File skeletonFile = HttpUtils.downloadFrom(skeletonUrl, targetDirectory);
-        return fromFile(atlas, skeletonFile);
-    }
+	/** Loads a {@link SkeletonData} from the URL {@code skeletonURL}. Uses the provided {@link AndroidTextureAtlas} to resolve
+	 * attachment images.
+	 *
+	 * Throws a {@link RuntimeException} in case the skeleton data could not be loaded. */
+	public static SkeletonData fromHttp (AndroidTextureAtlas atlas, URL skeletonUrl, File targetDirectory) {
+		File skeletonFile = HttpUtils.downloadFrom(skeletonUrl, targetDirectory);
+		return fromFile(atlas, skeletonFile);
+	}
 }

+ 8 - 9
spine-android/spine-android/src/test/java/com/esotericsoftware/android/ExampleUnitTest.java

@@ -1,17 +1,16 @@
+
 package com.esotericsoftware.android;
 
 import org.junit.Test;
 
 import static org.junit.Assert.*;
 
-/**
- * Example local unit test, which will execute on the development machine (host).
+/** Example local unit test, which will execute on the development machine (host).
  *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> */
 public class ExampleUnitTest {
-    @Test
-    public void addition_isCorrect() {
-        assertEquals(4, 2 + 2);
-    }
-}
+	@Test
+	public void addition_isCorrect () {
+		assertEquals(4, 2 + 2);
+	}
+}

+ 1 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java

@@ -95,7 +95,7 @@ public class Slot {
 		return color;
 	}
 
-	public void setColor(Color color) {
+	public void setColor (Color color) {
 		this.color = color;
 	}