com.cachirulop.moneybox.fragment.MoneyboxFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.cachirulop.moneybox.fragment.MoneyboxFragment.java

Source

/*******************************************************************************
 *   Copyright (c) 2012 David Magro Martin.
 *   All rights reserved. This program and the accompanying materials
 *   are made available under the terms of the GNU Public License v3.0
 *   which accompanies this distribution, and is available at
 *   http://www.gnu.org/licenses/gpl.html
 *   
 * Contributors:
 *       David Magro Martin
 *******************************************************************************/

package com.cachirulop.moneybox.fragment;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.AlphaAnimation;
import android.view.animation.AnimationSet;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.cachirulop.moneybox.R;
import com.cachirulop.moneybox.activity.IMoneyboxListener;
import com.cachirulop.moneybox.activity.MainActivity;
import com.cachirulop.moneybox.entity.CurrencyValueDef;
import com.cachirulop.moneybox.entity.Movement;
import com.cachirulop.moneybox.manager.CurrencyManager;
import com.cachirulop.moneybox.manager.MovementsManager;
import com.cachirulop.moneybox.manager.SoundsManager;
import com.cachirulop.moneybox.manager.VibratorManager;

public class MoneyboxFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.moneybox_tab, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        initActivity();
        updateTotal();

        registerLayoutListener();

        super.onActivityCreated(savedInstanceState);
    }

    /**
     * Register the event OnGlobalLayoutListener to fill the moneybox when the
     * layout is created.
     */
    private void registerLayoutListener() {
        final View v;
        final ViewTreeObserver vto;
        Activity parent;

        parent = getActivity();

        v = parent.findViewById(R.id.moneyDropLayout);
        vto = v.getViewTreeObserver();
        if (vto.isAlive()) {
            vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                public void onGlobalLayout() {
                    initMoneybox();

                    v.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                }
            });
        }
    }

    /**
     * Initialize the activity creating the list of buttons with the coins and
     * bills.
     */
    private void initActivity() {
        String currencyName;

        currencyName = getResources().getString(R.string.currency_name);

        CurrencyManager.initCurrencyDefList(currencyName);
        addButtons();
    }

    /**
     * Launched when a coin or a bill is clicked to be inserted in the moneybox.
     * 
     * Drop the clicked element inside the moneybox and updates the total
     * amount.
     * 
     * @param v
     *            View that launch the event.
     */
    protected void onMoneyClicked(View v) {
        CurrencyValueDef value;

        value = (CurrencyValueDef) v.getTag();
        if (value != null) {
            MainActivity parent;
            Movement result;

            parent = (MainActivity) getActivity();

            result = MovementsManager.insertMovement(parent.getCurrentMoneybox(), value.getAmount());
            dropMoney(v, result);

            updateTotal();
            refreshMovements();
        }
    }

    /**
     * Move an image of the money like it was dropping inside the money box
     * 
     * @param src
     *            Image of the money to drop
     * @param m
     *            Movement with the value of the money to drop
     */

    private void dropMoney(View src, Movement m) {
        HorizontalScrollView scroll;

        scroll = (HorizontalScrollView) getActivity().findViewById(R.id.scrollButtonsView);

        dropMoney(src.getLeft() - scroll.getScrollX(), src.getRight(), m);
    }

    /**
     * Drop money from the top of the layout to the bottom simulating that a
     * coin or bill is inserted in the moneybox.
     * 
     * @param leftMargin
     *            Left side of the coin/bill
     * @param width
     *            Width of the image to slide down
     * @param m
     *            Movement with the value of the money to drop
     */
    protected void dropMoney(int leftMargin, int width, Movement m) {
        ImageView money;
        AnimationSet moneyDrop;
        RelativeLayout layout;
        RelativeLayout.LayoutParams lpParams;
        Rect r;
        Activity parent;
        CurrencyValueDef curr;

        parent = getActivity();

        curr = CurrencyManager.getCurrencyDef(Math.abs(m.getAmount()));
        r = curr.getDrawable().getBounds();

        money = new ImageView(parent);
        money.setVisibility(View.INVISIBLE);
        money.setImageDrawable(curr.getDrawable().getConstantState().newDrawable());
        money.setTag(curr);
        money.setId((int) m.getIdMovement());

        layout = findLayout();

        lpParams = new RelativeLayout.LayoutParams(r.width(), r.height());
        lpParams.leftMargin = leftMargin;
        lpParams.rightMargin = layout.getWidth() - (leftMargin + width);
        lpParams.topMargin = 0;
        lpParams.bottomMargin = r.height();

        layout.addView(money, lpParams);

        moneyDrop = createDropAnimation(money, layout, curr);
        money.setVisibility(View.VISIBLE);

        SoundsManager.playMoneySound(curr.getType());
        VibratorManager.vibrateMoneyDrop(curr.getType());

        money.startAnimation(moneyDrop);
    }

    /**
     * Create dynamically an android animation for a coin or a bill droping into
     * the moneybox.
     * 
     * @param img
     *            ImageView to receive the animation
     * @param layout
     *            Layout that paint the image
     * @param curr
     *            Currency value of the image
     * @return Set of animations to apply to the image
     */
    private AnimationSet createDropAnimation(ImageView img, View layout, CurrencyValueDef curr) {
        AnimationSet result;

        result = new AnimationSet(false);
        result.setFillAfter(true);

        // Fade in
        AlphaAnimation fadeIn;

        fadeIn = new AlphaAnimation(0.0f, 1.0f);
        fadeIn.setDuration(300);
        result.addAnimation(fadeIn);

        // drop
        TranslateAnimation drop;
        int bottom;

        bottom = Math.abs(layout.getHeight() - img.getLayoutParams().height);
        drop = new TranslateAnimation(1.0f, 1.0f, 1.0f, bottom);
        drop.setStartOffset(300);
        drop.setDuration(1500);

        if (curr.getType() == CurrencyValueDef.MoneyType.COIN) {
            drop.setInterpolator(new BounceInterpolator());
        } else {
            // drop.setInterpolator(new DecelerateInterpolator(0.7f));
            drop.setInterpolator(new AnticipateOvershootInterpolator());
        }

        result.addAnimation(drop);

        return result;
    }

    /**
     * Create dynamically an android animation for a coin or a bill getting from
     * the moneybox.
     * 
     * @param img
     *            ImageView to receive the animation
     * @param layout
     *            Layout that paint the image
     * @return Set of animations to apply to the image
     */
    private AnimationSet createGetAnimation(ImageView img, View layout) {
        AnimationSet result;

        result = new AnimationSet(false);
        result.setFillAfter(true);

        // get
        TranslateAnimation get;
        int bottom;

        bottom = Math.abs(layout.getHeight() - img.getLayoutParams().height);
        get = new TranslateAnimation(1.0f, 1.0f, bottom, 1.0f);
        get.setDuration(1500);
        result.addAnimation(get);

        // Fade out
        AlphaAnimation fadeOut;

        fadeOut = new AlphaAnimation(1.0f, 0.0f);
        fadeOut.setDuration(300);
        fadeOut.setStartOffset(1500);

        result.addAnimation(fadeOut);

        return result;
    }

    /**
     * Create dynamically an android animation for a coin or a bill deleted from
     * the moneybox.
     * 
     * @param img
     *            ImageView to receive the animation
     * @param layout
     *            Layout that paint the image
     * @return Set of animations to apply to the image
     */
    private AnimationSet createDeleteAnimation(ImageView img, View layout) {
        AnimationSet result;

        result = new AnimationSet(false);
        result.setFillAfter(true);

        // Fade out
        AlphaAnimation fadeOut;

        fadeOut = new AlphaAnimation(1.0f, 0.0f);
        fadeOut.setStartOffset(300);
        fadeOut.setDuration(300);

        result.addAnimation(fadeOut);

        return result;
    }

    /**
     * Update the total amount using the main tab activity.
     */
    private void updateTotal() {
        ((IMoneyboxListener) getActivity()).onUpdateTotal();
    }

    /**
     * Send a request to the activity to refresh the movement list
     */
    private void refreshMovements() {
        ((IMoneyboxListener) getActivity()).onUpdateMovements();
    }

    /**
     * Set a value to the total field using the main tab activity.
     * 
     * @param val
     *            Value to be painted in the total.
     */
    protected void setTotal(double val) {
        ((IMoneyboxListener) getActivity()).onSetTotal(val);
    }

    /**
     * Add the currency buttons dynamically from the money_defs.xml file
     */
    private void addButtons() {
        ArrayList<CurrencyValueDef> currencies;
        LinearLayout buttons;
        Activity parent;

        parent = getActivity();

        buttons = (LinearLayout) parent.findViewById(R.id.moneyButtonsLayout);

        currencies = CurrencyManager.getCurrencyDefList();

        View.OnClickListener listener;

        listener = new View.OnClickListener() {
            public void onClick(View v) {
                onMoneyClicked(v);
            }
        };

        for (CurrencyValueDef c : currencies) {
            ImageView v;

            v = new ImageView(parent);
            v.setOnClickListener(listener);
            v.setImageDrawable(c.getDrawable());
            v.setLongClickable(true);
            v.setTag(c);

            buttons.addView(v);
        }
    }

    /**
     * Fill the moneybox with all the movements dropping coins randomly
     */
    public void fillMoneybox() {
        RelativeLayout layout;
        int maxWidth;
        ArrayList<Movement> lstMoney;
        Random rndLeft;
        double total;
        int i;
        MainActivity parent;

        parent = (MainActivity) getActivity();

        layout = findLayout();
        maxWidth = layout.getWidth();
        if (maxWidth == 0) {
            // The layout is not initialized
            return;
        }

        total = 0.0;
        i = 0;

        rndLeft = new Random();
        lstMoney = MovementsManager.getActiveMovements(parent.getCurrentMoneybox());
        for (Movement m : lstMoney) {
            Rect r;
            CurrencyValueDef curr;
            int left;

            curr = CurrencyManager.getCurrencyDef(Math.abs(m.getAmount()));
            if (curr != null) {
                r = curr.getDrawable().getBounds();

                left = rndLeft.nextInt(maxWidth - r.width());

                total += m.getAmount();

                MoneyTimerTask task;

                task = new MoneyTimerTask(this, m, left, r.width(), total);

                layout.postDelayed(task, 400 * i);
            }

            i++;
        }
    }

    /**
     * Initialize the activity filling the window with the coins and bills that
     * are inside the moneybox. Also center the coins and bills list to show the
     * middle item.
     */
    public void initMoneybox() {
        fillMoneybox();

        HorizontalScrollView scroll;
        int offsetX;
        List<CurrencyValueDef> currList;
        int elemWidth;

        currList = CurrencyManager.getCurrencyDefList();
        scroll = (HorizontalScrollView) getActivity().findViewById(R.id.scrollButtonsView);

        elemWidth = currList.get(0).getDrawable().getBounds().right;
        offsetX = ((currList.size() * elemWidth) / 2) - (elemWidth / 2);
        scroll.scrollTo(offsetX, 0);
    }

    /**
     * Remove the coins and bills inside the moneybox and refill it.
     */
    public void refresh() {
        findLayout().removeAllViews();
        fillMoneybox();
    }

    /**
     * Get a money from the moneybox making an animation.
     * 
     * @param m
     *            Movement to be got
     */
    public void getMovement(Movement m) {
        ImageView money;

        money = findMovementImage(m);
        if (money != null) {
            AnimationSet moneyGet;
            moneyGet = createGetAnimation(money, findLayout());

            money.startAnimation(moneyGet);
        }
    }

    /**
     * Delete a movement from the moneybox making an animation.
     * 
     * @param m
     *            Movement to be deleted.
     */
    public void deleteMovement(Movement m) {
        ImageView money;

        money = findMovementImage(m);
        if (money != null) {
            AnimationSet moneyDelete;

            moneyDelete = createDeleteAnimation(money, findLayout());

            money.startAnimation(moneyDelete);
        }
    }

    /**
     * Drop again a movement in the moneybox making an animation.
     * 
     * @param m
     *            Movement to be dropped.
     */
    public void dropAgainMovement(Movement m) {
        ImageView v;
        CurrencyValueDef c;

        // TODO: This doesn't work!!!!!!

        c = CurrencyManager.getCurrencyDef(Math.abs(m.getAmount()));

        v = new ImageView(getActivity());
        v.setLeft(findLayout().getWidth() / 2);
        v.setTop(0);
        v.setImageDrawable(c.getDrawable());
        v.setTag(c);

        dropMoney(v, m);
    }

    /**
     * Finds the image of a specific movement in the images created when the
     * movement is inserted.
     * 
     * @param m
     *            Movement to find
     * 
     * @return The ImageView that represents the movement in the moneybox or
     *         null if the movement doesn't exist.
     */
    private ImageView findMovementImage(Movement m) {
        return (ImageView) findLayout().findViewById((int) m.getIdMovement());
    }

    /**
     * Find the main Layout of the fragment
     * 
     * @return The main layout of the fragment
     */
    private RelativeLayout findLayout() {
        return (RelativeLayout) getActivity().findViewById(R.id.moneyDropLayout);
    }
}

/**
 * Class that implements a task that drop the coin or bill inside the moneybox
 * in an independent thread.
 * 
 * @author dmagro
 */
final class MoneyTimerTask implements Runnable {
    MoneyboxFragment _parent;
    Movement _movement;
    int _left;
    int _width;
    double _total;

    /**
     * Creates new object with then necessary values to launch the coin or the
     * bill inside the moneybox.
     * 
     * @param parent
     *            Activity to drop the money
     * @param currency
     *            Currency to be dropped
     * @param left
     *            Left coordinate of the money inside the layout
     * @param width
     *            With of the image that paint the money
     * @param total
     *            Total to be painted in the total layout.
     */
    public MoneyTimerTask(MoneyboxFragment parent, Movement movement, int left, int width, double total) {
        _parent = parent;
        _movement = movement;
        _left = left;
        _width = width;
        _total = total;
    }

    /**
     * Drop the money in the moneybox and update the total amount.
     */
    public void run() {
        _parent.getActivity().runOnUiThread(new Runnable() {
            public void run() {
                _parent.dropMoney(_left, _width, _movement);
                _parent.setTotal(_total);
            }
        });
    }

}