playn.android.AndroidAssetManager.java Source code

Java tutorial

Introduction

Here is the source code for playn.android.AndroidAssetManager.java

Source

/**
 * Copyright 2011 The PlayN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package playn.android;

import static playn.core.PlayN.log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;

import playn.core.AbstractAssetManager;
import playn.core.Image;
import playn.core.ResourceCallback;
import playn.core.Sound;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;

public class AndroidAssetManager extends AbstractAssetManager {

    public String pathPrefix = null;
    public AssetManager assets;

    public void setPathPrefix(String prefix) {
        pathPrefix = prefix;
    }

    /**
     * Attempts to open the asset with the given name, throwing an
     * {@link IOException} in case of failure.
     */
    private InputStream openAsset(String path) throws IOException {
        // Insert a slash to make this consistent with the Java asset manager
        InputStream is = getClass().getClassLoader().getResourceAsStream(pathPrefix + "/" + path);
        if (is == null)
            throw new IOException("Unable to loader resource: " + path);
        return is;
    }

    @Override
    protected Image doGetImage(String path) {
        return new AndroidImage(path, doGetBitmap(path));
    }

    /**
     * Decodes a resource to a bitmap. Always succeeds, returning an error
     * placeholder if something goes wrong.
     */
    Bitmap doGetBitmap(String path) {
        try {
            InputStream is = openAsset(path);
            try {
                Bitmap bitmap = decodeBitmap(is);
                return bitmap;
            } finally {
                is.close();
            }
        } catch (IOException e) {
            return createErrorBitmap(e);
        }
    }

    private Bitmap decodeBitmap(InputStream is) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        //
        options.inDither = true;
        // Prefer the bitmap config we computed from the window parameter
        options.inPreferredConfig = AndroidPlatform.instance.preferredBitmapConfig;
        // Never scale bitmaps based on device parameters
        options.inScaled = false;
        return BitmapFactory.decodeStream(is, null, options);
    }

    private Bitmap createErrorBitmap(Exception e) {
        int height = 100, width = 100;

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);

        android.graphics.Canvas c = new android.graphics.Canvas(bitmap);
        android.graphics.Paint p = new android.graphics.Paint();
        p.setColor(android.graphics.Color.RED);
        for (int yy = 0; yy <= height / 15; yy++) {
            for (int xx = 0; xx <= width / 45; xx++) {
                c.drawText("ERROR", xx * 45, yy * 15, p);
            }
        }

        return bitmap;
    }

    private class ErrorSound implements Sound {
        private final String path;

        public ErrorSound(String path) {
            this.path = path;
        }

        @Override
        public boolean play() {
            log().error("Attempted to play sound that was unable to load: " + path);
            return false;
        }

        @Override
        public void stop() {
        }

        @Override
        public void setLooping(boolean looping) {
        }

        @Override
        public void setVolume(float volume) {
        }

        @Override
        public boolean isPlaying() {
            return false;
        }
    }

    // FIXME: This should be much more intelligent than appending .wav to the path.
    // The current AndroidSound implementation using MediaPlayer (AndroidCompressedSound)
    // can already read many more formats than just .wav
    @Override
    protected Sound doGetSound(String path) {
        try {
            InputStream in = openAsset(path + ".wav");
            return AndroidPlatform.instance.audio().getSound(path + ".wav", in);
        } catch (IOException e) {
            log().error("Unable to load sound: " + path, e);
            return new ErrorSound(path);
        }
    }

    @Override
    protected void doGetText(final String path, final ResourceCallback<String> callback) {
        try {
            InputStream is = openAsset(path);
            try {
                StringBuilder fileData = new StringBuilder(1000);
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                char[] buf = new char[1024];
                int numRead = 0;
                while ((numRead = reader.read(buf)) != -1) {
                    String readData = String.valueOf(buf, 0, numRead);
                    fileData.append(readData);
                }
                reader.close();
                String text = fileData.toString();
                callback.done(text);
            } finally {
                is.close();
            }
        } catch (IOException e) {
            callback.error(e);
        }
    }

    public abstract static class DownloaderTask<T> extends AsyncTask<String, Void, T> {
        private ResourceCallback<T> callback;

        public DownloaderTask() {
        }

        public DownloaderTask(ResourceCallback<T> callback) {
            this.callback = callback;
        }

        @Override
        // Actual download method, run in the task thread
        protected T doInBackground(String... params) {
            // params comes from the execute() call: params[0] is the url.
            return download(params[0]);
        }

        public abstract T download(String url);

        @Override
        protected void onPostExecute(T data) {
            if (callback != null) {
                callback.done(data);
            }
        }
    }

    // Taken from
    // http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
    public Bitmap downloadBitmap(String url) {
        final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
        final HttpGet getRequest = new HttpGet(url);

        try {
            HttpResponse response = client.execute(getRequest);
            final int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
                return null;
            }

            final HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream inputStream = null;
                try {
                    inputStream = entity.getContent();
                    return decodeBitmap(inputStream);
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    entity.consumeContent();
                }
            }
        } catch (Exception e) {
            // Could provide a more explicit error message for IOException or
            // IllegalStateException
            getRequest.abort();
            Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e);
        } finally {
            if (client != null) {
                client.close();
            }
        }
        return null;
    }
}