Android Open Source - coloring-book-android Color G F X






From Project

Back to project page coloring-book-android.

License

The source code is released under:

GNU General Public License

If you think the Android project coloring-book-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (c) 2013 Byron Sanchez (hackbytes.com)
 * www.chompix.com//  ww  w.j  a v a  2s .c o m
 *
 * This file is part of "Coloring Book for Android."
 *
 * "Coloring Book for Android" is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, version 2 of the
 * license.
 *
 * "Coloring Book for Android" is distributed in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with "Coloring Book for Android."  If not, see
 * <http://www.gnu.org/licenses/>.
 */
/*
 * NOTE: The Android Open Source Project holds the copyright to the
 * code ranging from lines 451-486. I hold the copyright to the rest
 * of the code.
 *
 * For all code copyrighted by The Android Open Source Project, the
 * subsequent license notice applies. The license notice remains
 * untouched and intact as per the license agreement. For the rest of
 * the code, the COPYING file in the root directory of this application
 * applies.
 */
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * 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 net.globide.coloring_book_08;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.MaskFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

/**
 * Handles all canvas rendering actions.
 */

public class ColorGFX extends SurfaceView implements Runnable {

    // A thread, that will be used to execute code in parallel with the UI
    // thread.
    public Thread mThread;
    // Create a thread handler to queue code execution on a thread.
    private Handler mHandler;
    // Boolean to determine whether a thread should continue processing or if it
    // should stop.
    public boolean isThreadBroken = false;
    // Runnable counter for progress bar.
    private int mRunnableCounter = 0;

    // The id of the currently selected color.
    public int selectedColor = Color.BLACK;

    // Define whether or not fill is occuring.
    public boolean isFillEnabled = false;

    // Define whether or not fill mode is enabled (if not, then the mode is
    // considered draw mode).
    public boolean isFillModeEnabled = false;
    // Define whether ot not erase mode is enabled (this check is necessary to
    // take precedence over fillMode.
    public boolean isEraseModeEnabled = false;

    // Set the SurfaceView Thread properties.
    private SurfaceHolder mOurHolder;
    private Thread mOurThread = null;

    // Image metrics
    public int imageWidth;
    public int imageHeight;

    // Set the GFX properties.

    // Boolean for determining whether or not the canvas should be cleared for
    // the next image load.
    public boolean isNextImage = false;
    // The picture bitmap.
    public Bitmap pictureBitmap;

    public String paintBitmapName;

    // Bitmap buffers used when loading a new bitmap.
    // The picture bitmap.
    public Bitmap pictureBitmapBuffer;

    // The path bitmap.
    public Bitmap bitmap;
    // The canvas to draw on.
    public Canvas pathCanvas;
    // The canvas to fill on.
    public Canvas fillCanvas;
    // The canvas for all draws.
    public Canvas canvas;

    // The path buffers.
    private boolean mIsDrawn = false;

    // Set brush properties
    // Set a brush emboss.
    public MaskFilter emboss;
    // Set a brush blur.
    public MaskFilter blur;
    // The color of the path.
    public Paint paint;
    public Paint bitmapPaint;
    // The path to draw in draw mode.
    public Path mPath;

    // Cehck to see if data is being restored.
    public boolean isSavedData;

    // A list of points to floodfill whenever this tool is used.
    private boolean[][] mFloodfillList;
    // A list of points to floodfill whenever this tool is used.
    private boolean[][] mStrokefillList;

    private Context mContext;

    /**
     * Constructor.
     */
    public ColorGFX(Context context, int width, int height) {
        super(context);
        this.mContext = context;
        this.imageWidth = width;
        this.imageHeight = height;

        // Create the stroke path object.
        mPath = new Path();

        // Set a clear dithered background.
        bitmapPaint = new Paint(Paint.DITHER_FLAG);

        mOurHolder = getHolder();
    }

    /**
     * Constructor for restoring data.
     */
    public ColorGFX(Context context, int width, int height, boolean isSavedData,
            Bitmap savedPathBitmap) {
        super(context);
        this.mContext = context;
        this.imageWidth = width;
        this.imageHeight = height;
        this.isSavedData = isSavedData;
        this.bitmap = savedPathBitmap;

        // Create the stroke path object.
        mPath = new Path();

        // Set a clear dithered background.
        bitmapPaint = new Paint(Paint.DITHER_FLAG);

        mOurHolder = getHolder();
    }

    /**
     * Constructor.
     */
    public ColorGFX(Context context) {
        super(context);
        this.mContext = context;

        // Create the stroke path object.
        mPath = new Path();

        // Set a clear dithered background.
        bitmapPaint = new Paint(Paint.DITHER_FLAG);

        mOurHolder = getHolder();
    }

    /**
     * Pauses the thread.
     */
    public void pause() {

        // If a fill op is happening, kill it.
        if (mThread != null && mThread.isAlive()) {
            isThreadBroken = true;
        }

        // Kill the main canvas thread.
        mOurThread.interrupt();
    }

    /**
     * Resumes a paused thread. This is called as soon as the view is created.
     */
    public void resume() {
        // If a bitmap has been restored from a saved state, don't create a new
        // bitmap.
        if (bitmap == null && !isSavedData) {
            bitmap = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888);
        }
        pathCanvas = new Canvas(bitmap);
        mOurThread = new Thread(this);
        mOurThread.start();
    }

    /**
     * Executes while the thread is running.
     */
    public void run() {

        // While the thread is not paused...
        while ( !mOurThread.isInterrupted() ) {

            // If we don't have access to the surface (as another application is
            // currently using it), don't do anything until we have access.
            if (!mOurHolder.getSurface().isValid()) {
                continue;
            }
            // Get the canvas and lock it so other applications cannot use it.
            canvas = mOurHolder.lockCanvas();

            // If a new image hasn't been requested via the previous/next
            // buttons...

            // mBitmapCanvas.drawColor(0xFFAAAAAA);
            canvas.drawColor(Color.WHITE);

            // Draw the backup eraser bitmap.
            if (pictureBitmap != null) {
                canvas.drawBitmap(pictureBitmap, 0, 0, null);
            }

            if (bitmap != null) {
                if (!bitmap.isRecycled()) {
                    // draw the paint bitmap on the canvas
                    canvas.drawBitmap(bitmap, 0, 0, bitmapPaint);
                }
            }

            // Only reset the path once it has been drawn to the pathCanvas.
            if (mIsDrawn) {
                mPath.reset();
                mIsDrawn = false;
            }

            canvas.drawPath(mPath, paint);

            // Draw the strokes bitmap.
            if (pictureBitmap != null) {
                canvas.drawBitmap(pictureBitmap, 0, 0, null);
            }

            if (isNextImage) {
                // If new images have been set in the buffer, load them before
                // the next draw.
                loadNewImages();

                isNextImage = false;
            }

            // Once we are done, unlock the canvas and update the display.
            mOurHolder.unlockCanvasAndPost(canvas);
        }

        if (mOurThread.isInterrupted()) {
          mOurThread = null;
        }
    }

    /**
     * Loads new images into the main bitmap caches.
     */
    public void loadNewImages() {

        if (pictureBitmap != null) {
            // Recycle the old bitmap.
            pictureBitmap.recycle();
        }

        int counter = 0;

        // Use a loop to ensure all bitmaps get loaded and prevent any potential
        // race conditions.
        while (true) {

            pictureBitmap = pictureBitmapBuffer.copy(Bitmap.Config.ARGB_8888, true);

            // If all bitmaps are loaded, break out of the loop.
            if (pictureBitmap != null) {
                break;
            }
            else if (counter > 1000) {
                // TODO: throw a timeout exception. Resource is not loading or
                // something is hanging. Right now we'll just break so we don't
                // over consume resources. The error might simply crash the
                // program for the user if this ever happens.
                break;
            }

            counter++;
        }

        // Clear the buffers for future use.
        pictureBitmapBuffer.recycle();
    }

    /**
     * Clears the path canvas of any color.
     */
    public void clear() {
        pathCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    }

    /**
     * Clears all canvases of any color.
     */
    public void clearAllCanvases() {
        pathCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        fillCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    }

    float lastX = -1;
    float lastY = -1;

    /**
     * Handles all user input events to manipulate the screen rendering.
     */
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();
        float y = event.getY();

        // Set defaults as per the bounds hit. This way, the draw limit is
        // AT the bounds and any path draws aren't interrupted just because
        // a user tries to go out of bounds. This will allow for erases and
        // draws at edges to not be interrupted producing artifacts or give
        // them a hard time trying to erase/draw near the bounds.

        // If the user exceeds any bound.
        if (x < 0) {
            // Check if a draw has previously occured within bounds.
            if (lastX == -1) {
                // If not, then use the default bound limit.
                x = 0;
            } else {
                // Else, use the last acceptable inbound position.
                x = lastX;
            }
        }
        // Do the same for the upper bound limit.
        else if (x > pathCanvas.getWidth()) {
            if (lastX == -1) {
                x = pathCanvas.getWidth();
            } else {
                x = lastX;
            }
        }
        // If a user is in bound...
        else {
            // Locally cache the bound for future out of bound handling.
            lastX = x;
        }

        // Rinse and repeat for y.
        if (y < 0) {
            if (lastY == -1) {
                y = 0;
            } else {
                y = lastY;
            }
        }
        else if (y > pathCanvas.getHeight()) {
            if (lastY == -1) {
                y = pathCanvas.getHeight();
            } else {
                y = lastY;
            }
        }
        else {
            lastY = y;
        }

        /*
         * After bounds have been processed, start drawing the path with the
         * constrained coordinates.
         */

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!isFillModeEnabled || isEraseModeEnabled) {
                    touch_start(x, y);
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (!isFillModeEnabled || isEraseModeEnabled) {
                    touch_move(x, y);
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                if (!isFillModeEnabled || isEraseModeEnabled) {
                    touch_up();
                    invalidate();
                }
                else {
                    this.isFillEnabled = true;
                    fillHandler(x, y);
                    invalidate();
                }
                break;
        }
        return true;
    }

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    /**
     * Starts a new path.
     */
    private void touch_start(float x, float y) {

        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    /**
     * Moves the path.
     */
    private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
    }

    /**
     * Completes the path.
     */
    private void touch_up() {
        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        pathCanvas.drawPath(mPath, paint);
        mIsDrawn = true;
    }

    /**
     * Handles fill operations when a fill event occurs.
     */
    private void fillHandler(float x, float y) {
        // If the user has set fill mode on...
        if (isFillModeEnabled) {
            // Check for the fill signal and ensure that a point has been set to
            // begin the flood fill algorithm.
            if (isFillEnabled) {

                // If a fill op is currently happening, do not register a new
                // op!
                if (mRunnableCounter >= 1) {
                    // Signal end of op.
                    isFillEnabled = false;
                    return;
                }

                // Get the corresponding fill map file resource.
                int resPaintId = mContext.getResources().getIdentifier(paintBitmapName,
                        "drawable", mContext.getPackageName());

                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeResource(getResources(),
                        resPaintId, options);

                int inSampleSize = 1;
                int fillImageWidth = options.outWidth;
                int fillImageHeight = options.outHeight;
                // If the scale fails, we will need to use more memory to
                // perform scaling for the layout to work on all size screens.
                boolean scaleFailed = false;
                Bitmap paintMap = null;
                float resizeRatioHeight = 1;

                // THIS IS DESIGNED FOR FITTING ON THE SCREEN WITH NO SCROLLBAR

                // Scale down if the image width exceeds the screen width.
                if (fillImageWidth > imageWidth || fillImageHeight > imageHeight) {

                    // If we need to resize the image because the width or
                    // height is too
                    // big, get the resize ratios for width and height.
                    resizeRatioHeight = (float) fillImageHeight / (float) imageHeight;

                    // Get the smaller ratio.
                    inSampleSize = (int) resizeRatioHeight;

                    if (inSampleSize <= 1) {
                        scaleFailed = true;
                    }
                }

                // Decode Bitmap with inSampleSize set
                options.inSampleSize = inSampleSize;
                options.inJustDecodeBounds = false;

                // colorGFX.isScaled = true;
                // colorGFX.inSampleSize = inSampleSize;
                Bitmap picture = BitmapFactory.decodeResource(getResources(), resPaintId, options);

                // If the scale failed, that means a scale was needed but didn't
                // happen. We need to create a scaled copy of the image by allocating
                // more memory.
                if (scaleFailed) {
                    int newWidth = (int) (picture.getWidth() / resizeRatioHeight);
                    int newHeight = (int) (picture.getHeight() / resizeRatioHeight);

                    paintMap = Bitmap.createScaledBitmap(picture, newWidth, newHeight, true);

                    // Recycle the picture bitmap.
                    picture.recycle();
                }
                else {
                    // No scaling was needed in the first place!
                    paintMap = picture;
                }

                // Convert the bitmap to a mutable bitmap.
                Bitmap mutablePaintMap = paintMap.copy(Config.ARGB_8888, true);

                // Recycle the immutable bitmap
                paintMap.recycle();

                // Get the color of the selected point.
                int targetColor = mutablePaintMap.getPixel((int) x, (int) y);

                // If the target color is not black (black means it is the
                // stroke color).
                if (targetColor == Color.TRANSPARENT) {

                    // Fill all unbounded pixels of that color.
                    Point node = new Point((int) x, (int) y);

                    mRunnableCounter++;

                    ((ColorActivity) mContext).pbFloodFill.setVisibility(View.VISIBLE);

                    // Runnable Flood fill
                    LoadViewTask floodfillTask = new LoadViewTask();
                    floodfillTask.image = mutablePaintMap;
                    floodfillTask.point = node;
                    floodfillTask.target = targetColor;
                    floodfillTask.replacementColor = selectedColor;

                    // Initialize the handler
                    mHandler = new Handler();
                    // Initialize the thread
                    mThread = new Thread(floodfillTask, "FloodFillThread");
                    // start the thread.
                    mThread.start();
                }
            }
        }
    }

    /**
     * Colors all pixels from the flood fill algorithm.
     */
    public void colorPixels(Bitmap picture, int replacementColor) {
        // Both arrays are the same size, so just choose one to control the
        // iteration.
        for (int i=0; i < mFloodfillList.length; i++) {
            for (int j=0; j < mFloodfillList[i].length; j++) {
                if (mFloodfillList[i][j] != false) {
                    picture.setPixel(i, j, replacementColor);
                }
                if (mStrokefillList[i][j] != false) {
                    picture.setPixel(i, j, replacementColor);
                }
            }
        }
    }

    /**
     * Clears the stroke and floodfill pixel lists.
     */
    public void clearPixelLists() {
        mStrokefillList = null;
        mFloodfillList = null;
    }

    /**
     * Class encapsulating the floodfill algorithm task.
     */

    private class LoadViewTask implements Runnable {

        public Point point;
        public int target;
        public int replacementColor;
        public Bitmap image;

        // Define the array primitives. This is faster and will ultimately use
        // less memory than array lists that can dynamically allocate memory.
        public boolean[][] list;
        public boolean[][] strokeList;

        /**
         * Implements run().
         */
        @Override
        public void run() {

            // Get the current thread's token
            synchronized (mThread) {

                floodFill(point, target, replacementColor, image);

                // Update the changes to the UI thread
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        // Set the current Progress
                    }
                });
            }

            // Similar to onPostExecute
            mHandler.post(new Runnable() {
                @Override
                public void run() {

                    if (!isThreadBroken) {

                        // Now that the lists have been generated, we can
                        // garbage collect the paint bitmap.
                        image.recycle();

                        // Pass the generated fill and stroke list data back to
                        // the UI thread class.
                        mFloodfillList = list;
                        mStrokefillList = strokeList;

                        // Create the bitmap that will be added to the paint
                        // layer.
                        Bitmap fillPicture = Bitmap.createBitmap(imageWidth, imageHeight,
                                Bitmap.Config.ARGB_8888);

                        // Color the list of pixels generated from the flood
                        // fill algorithm.
                        colorPixels(fillPicture, replacementColor);

                        // Paint filter to draw the paint over what is already
                        // on the paint layer.
                        Paint addFilter = new Paint();
                        addFilter
                                .setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));

                        // Save the flood filled image so it can persist in
                        // future flood fills and path draws.
                        // mPaintBitmap = picture;

                        // Draw the newly filled image onto the path canvas.
                        pathCanvas.drawBitmap(fillPicture, 0, 0, addFilter);

                    }

                    clearPixelLists();
                    // Close the progress dialog
                    // progressDialog.dismiss();
                    mRunnableCounter--;

                    // If there are no more runnables running a flood fill job,
                    // remove the progress bar view from the display.
                    if (mRunnableCounter == 0) {
                        ((ColorActivity) mContext).pbFloodFill.setVisibility(View.GONE);
                        // Once all threads have ended, we can set the boolean
                        // check for threads back to false so they work again.
                        isThreadBroken = false;
                    }
                }
            });

            // Try to kill the thread, by interrupting its execution
            synchronized (mThread) {
                mThread.interrupt();
            }
        }

        /**
         * Runs a typical floodfill algorithm, replacing all pixels of one color
         * with another.
         */
        public void floodFill(Point node, int targetColor,
                int replacementColor, Bitmap picture) {

            // Define the bitmap width and height.
            int width = picture.getWidth();
            int height = picture.getHeight();

            // Initialize the arrays according to the image metrics.
            list = new boolean[width][height];
            strokeList = new boolean[width][height];

            // Define the target and replacement color.
            int target = targetColor;
            int replacement = replacementColor;

            // Start the flood fill algorithm.
            if (target != replacement) {
                // Set the empty queue and run the algorithm at least once (or
                // alternatively, set the point to the end of queue and run a
                // while loop that performs this algorithm so long as the Queue is not
                // empty).
                Queue<Point> queue = new LinkedList<Point>();

                // Run the loop at least once for the selected pixel.
                do {

                    if (isThreadBroken) {
                        break;
                    }

                    // Store the current pixel in local variables.
                    int x = node.x;
                    int y = node.y;

                    // while x is not at the origin AND the color of it's West
                    // neighboring pixel is changeable.
                    while (x > 0 && picture.getPixel(x - 1, y) == target) {
                        // Continuously decrement x (AKA bring x as far to the
                        // west as possible given the color constraints).
                        x--;
                    }

                    // Given the above while loop, we are now as far West as we
                    // can be and are currently at a pixel we will need to replace.

                    // Set directional booleans.
                    boolean spanUp = false;
                    boolean spanDown = false;

                    // While x has not reached as far East as it can in the
                    // bitmap (AKA hasn't hit the end of the image and hasn't
                    // reached a color different than the replacement color)...
                    while (x < width && picture.getPixel(x, y) == target) {

                        // Replace the current pixel color.
                        picture.setPixel(x, y, replacement);
                        // Add the pixel to the flood fill list.
                        list[x][y] = true;

                        // If we don't take the stroke paths into consideration
                        // and color them where necessary, we WILL have reduced
                        // aliasing, but not perfect anti-aliasing. By coloring
                        // the paths, we have virtually ZERO aliasing.

                        // TOP

                        if (y + 1 < height - 1 && picture.getPixel(x, y + 1) != target) {
                            strokeList[x][y + 1] = true;
                        }

                        // RIGHT
                        if (x + 1 < width - 1 && picture.getPixel(x + 1, y) != target) {
                            strokeList[x + 1][y] = true;
                        }

                        // LEFT
                        if (x - 1 > 0 && picture.getPixel(x - 1, y) != target) {
                            strokeList[x - 1][y] = true;
                        }

                        // BOTTOM
                        if (y - 1 > 0 && picture.getPixel(x, y - 1) != target) {
                            strokeList[x][y - 1] = true;
                        }

                        // Add one SOUTH point to the queue if it is replaceable
                        // (this will be the next relative point to check from)
                        // and we have not previously moved down.
                        if (!spanUp && y > 0 && picture.getPixel(x, y - 1) == target) {
                            queue.add(new Point(x, y - 1));
                            spanUp = true;
                        }
                        // If the SOUTH point is unreplaceable or we have
                        // previously moved up set the boolean to false.
                        else if (spanUp && y > 0 && picture.getPixel(x, y - 1) != target) {
                            spanUp = false;
                        }

                        // Add one NORTH point to the queue if it is replaceable
                        // (this will be the next relative point to check from)
                        // and we have not previously moved up.
                        if (!spanDown && y < height - 1
                                && picture.getPixel(x, y + 1) == target) {
                            queue.add(new Point(x, y + 1));
                            spanDown = true;
                        }
                        // If the NORTH point is unreplaceable or we have
                        // previously moved up set the boolean to false.
                        else if (spanDown && y < height - 1
                                && picture.getPixel(x, y + 1) != target) {
                            spanDown = false;
                        }

                        // Increment the x-position, 1 to the east.
                        x++;
                    }
                }
                // Remove the head of this queue. Keep looping until no pixels
                // remain.
                while ((node = queue.poll()) != null);
            }

            // Once the Flood Fill Algorithm has completed, turn the action flag
            // off. We're done.
            isFillEnabled = false;
        }
    }
}




Java Source Code List

net.globide.coloring_book_08.Category.java
net.globide.coloring_book_08.ColorActivity.java
net.globide.coloring_book_08.ColorGFX.java
net.globide.coloring_book_08.ColorGfxData.java
net.globide.coloring_book_08.ColorPalette.java
net.globide.coloring_book_08.CreditsActivity.java
net.globide.coloring_book_08.FixedSpeedScroller.java
net.globide.coloring_book_08.HelpActivity.java
net.globide.coloring_book_08.MainActivity.java
net.globide.coloring_book_08.MainPagerAdapter.java
net.globide.coloring_book_08.MainPanelFragment.java
net.globide.coloring_book_08.MusicManager.java
net.globide.coloring_book_08.NodeDatabase.java
net.globide.coloring_book_08.Node.java
net.globide.coloring_book_08.OnMainPanelTouchListener.java
net.globide.coloring_book_08.SettingsActivity.java
net.globide.coloring_book_08.ShopActivity.java
net.globide.coloring_book_08.SplashActivity.java
net.globide.coloring_book_08.util.Base64DecoderException.java
net.globide.coloring_book_08.util.Base64.java
net.globide.coloring_book_08.util.IabException.java
net.globide.coloring_book_08.util.IabHelper.java
net.globide.coloring_book_08.util.IabResult.java
net.globide.coloring_book_08.util.Inventory.java
net.globide.coloring_book_08.util.Purchase.java
net.globide.coloring_book_08.util.Security.java
net.globide.coloring_book_08.util.SkuDetails.java