com.svpino.longhorn.fragments.StockListFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.svpino.longhorn.fragments.StockListFragment.java

Source

/*
 * Copyright (C) 2012 Santiago Valdarrama
 * 
 * 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 com.svpino.longhorn.fragments;

import static com.svpino.longhorn.artifacts.Extensions.isHoneycombOrLater;
import static com.svpino.longhorn.artifacts.Extensions.isIceCreamSandwichOrLater;
import static com.svpino.longhorn.artifacts.Extensions.isPriorIceCreamSandwich;

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

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.util.SparseArray;
import android.view.ActionMode;
import android.view.DragEvent;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.RelativeLayout;
import android.widget.SearchView;
import android.widget.ShareActionProvider;
import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
import android.widget.TableLayout;
import android.widget.TableRow.LayoutParams;
import android.widget.TextView;
import android.widget.ViewFlipper;

import com.svpino.longhorn.R;
import com.svpino.longhorn.artifacts.Constants;
import com.svpino.longhorn.artifacts.Extensions;
import com.svpino.longhorn.artifacts.Extensions.PatternBackgroundColor;
import com.svpino.longhorn.artifacts.StockTileProcessor;
import com.svpino.longhorn.artifacts.StockTileProcessor.OnDragTileListener;
import com.svpino.longhorn.artifacts.StockTileViewHolder;
import com.svpino.longhorn.artifacts.TabFragment;
import com.svpino.longhorn.artifacts.back.BackStack;
import com.svpino.longhorn.artifacts.back.StockOverviewBackStackItem;
import com.svpino.longhorn.layouts.StockOverviewLayout;
import com.svpino.longhorn.model.Stock;

public class StockListFragment extends Fragment
        implements TabFragment, OnClickListener, OnLongClickListener, OnDragTileListener {

    private final static String LOG_TAG = StockListFragment.class.getName();

    private final static int SWIPE_THRESHOLD_VELOCITY = 150;
    private final static int STOCK_OVERVIEW_HEIGHT = 185;

    public enum StockOverviewState {
        HIDDEN, OVERVIEW
    }

    private TableLayout tableLayout;
    private LayoutInflater inflater;
    private View currentlySelectedTile;
    private SparseArray<View> tiles;
    private SearchView searchView;
    private View focusedTileDuringDragAndDrop = null;
    private ActionMode.Callback actionModeCallback;
    private ActionMode actionMode;
    private List<Integer> selectedTiles;
    private AlertDialog removeSelectedStocksDialog;
    private MenuItem searchMenuItem;
    private StockListFragmentCallback callback;

    private ViewFlipper viewFlipper;
    private Animation flipInNextAnimation;
    private Animation flipInPreviousAnimation;
    private Animation flipOutNextAnimation;
    private AnimateStockOverviewContentCallback animateStockOverviewContentCallback;
    private Animation flipOutPreviousAnimation;
    private StockOverviewState stockOverviewState = null;
    private GestureDetector viewFlipperGestureDetector;
    private AlertDialog tileContextualMenuDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);

        this.selectedTiles = new ArrayList<Integer>();
        this.tiles = new SparseArray<View>();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_stock_list, container, false);

        setHasOptionsMenu(true);

        if (isHoneycombOrLater()) {
            this.actionModeCallback = new ActionModeCallback();
        }

        this.tableLayout = (TableLayout) view.findViewById(R.id.tableLayout);

        this.viewFlipper = (ViewFlipper) view.findViewById(R.id.viewFlipper);

        StockOverviewLayout stockOverviewLayout1 = new StockOverviewLayout(getActivity());
        StockOverviewLayout stockOverviewLayout2 = new StockOverviewLayout(getActivity());

        this.viewFlipper.addView(stockOverviewLayout1);
        this.viewFlipper.addView(stockOverviewLayout2);

        this.animateStockOverviewContentCallback = new AnimateStockOverviewContentCallback();

        this.flipInNextAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.flip_in_next);
        this.flipInNextAnimation
                .setAnimationListener(new StockOverviewAnimationListener(this.animateStockOverviewContentCallback));

        this.flipInPreviousAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.flip_in_previous);
        this.flipInPreviousAnimation
                .setAnimationListener(new StockOverviewAnimationListener(this.animateStockOverviewContentCallback));

        this.flipOutNextAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.flip_out_next);
        this.flipOutPreviousAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.flip_out_previous);

        this.viewFlipperGestureDetector = new GestureDetector(getActivity(), new ViewFlipperGestureDetector());
        this.viewFlipper.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                if (StockListFragment.this.viewFlipperGestureDetector.onTouchEvent(event)) {
                    return false;
                }

                return true;
            }
        });

        createDashboardTiles(true);

        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedState) {
        if (this.stockOverviewState == null) {
            setStockOverviewState(StockOverviewState.HIDDEN);
        }

        if (isOverviewVisible()) {
            StockOverviewBackStackItem lastStockOverviewAddedToBackStack = this.callback.getBackStack()
                    .getLastStockOverviewAddedToBackStack();
            if (lastStockOverviewAddedToBackStack != null) {
                displayStockOverview(lastStockOverviewAddedToBackStack.getStock(), false);
            }
        }

        super.onActivityCreated(savedState);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.callback = (StockListFragmentCallback) activity;
    }

    @Override
    public void onResume() {
        if (isHoneycombOrLater() && this.actionMode != null) {
            this.actionMode = null;
            showContextualActionBar();

            for (Integer position : this.selectedTiles) {
                selectTile(this.tiles.get(position), false);
            }
        }

        super.onResume();
    }

    @Override
    public void onPause() {
        if (this.removeSelectedStocksDialog != null) {
            this.removeSelectedStocksDialog.dismiss();
        }

        if (this.tileContextualMenuDialog != null) {
            this.tileContextualMenuDialog.dismiss();
        }

        super.onPause();
    }

    @TargetApi(14)
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.fragment_stock_list, menu);
        this.searchMenuItem = menu.findItem(R.id.menu_item_add);

        if (isIceCreamSandwichOrLater()) {
            SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
            this.searchView = (SearchView) this.searchMenuItem.getActionView();
            this.searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));
            this.searchView.setSubmitButtonEnabled(false);
            this.searchView.setIconifiedByDefault(true);

            final ShareActionProvider shareActionProvider = (ShareActionProvider) menu
                    .findItem(R.id.menu_item_share).getActionProvider();
            shareActionProvider.setShareIntent(getDefaultShareIntent(null));
            shareActionProvider.setOnShareTargetSelectedListener(new OnShareTargetSelectedListener() {

                @Override
                public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
                    Intent defaultShareIntent = getDefaultShareIntent(null);
                    shareActionProvider.setShareIntent(defaultShareIntent);
                    return false;
                }
            });
        }

        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        MenuItem shareMenuItem = menu.findItem(R.id.menu_item_share);
        shareMenuItem.setVisible(isOverviewVisible());

        super.onPrepareOptionsMenu(menu);
    }

    @TargetApi(11)
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_item_add:
            requestSearch();
            return true;
        case R.id.menu_item_share: {
            if (isPriorIceCreamSandwich()) {
                showShareDialog(null);
                return true;
            }
        }
        }

        return false;
    }

    private Intent getDefaultShareIntent(Integer position) {
        Intent intent = new Intent(android.content.Intent.ACTION_SEND);
        intent.setType("text/plain");

        Stock stock = null;
        if (position == null) {
            if (isOverviewVisible() && this.viewFlipper != null && this.viewFlipper.getTag() != null) {
                stock = this.callback.getStockList().get((Integer) this.viewFlipper.getTag());
            }
        } else {
            stock = this.callback.getStockList().get(position);
        }

        if (stock != null) {
            Resources resources = getActivity().getResources();
            intent.putExtra("symbol", stock.getSymbol());
            intent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                    String.format(resources.getString(R.string.fragment_stock_overview_share_subject),
                            stock.getSymbol(), stock.getStringPrice()));

            int resourceIdentifier = resources.getIdentifier(
                    "fragment_stock_overview_share_message_" + Constants.STORE, "string",
                    getActivity().getPackageName());

            intent.putExtra(android.content.Intent.EXTRA_TEXT,
                    String.format(resources.getString(resourceIdentifier), stock.getSymbol(),
                            stock.getStringPrice(), stock.getStringChangeAndChangePercentage(),
                            resources.getString(R.string.application_name)));
        }

        return intent;
    }

    private void display(Stock stock) {
        int position = this.callback.getStockList().indexOf(stock);

        if (this.stockOverviewState == StockOverviewState.HIDDEN) {
            displayStockOverview(position, true);
        } else {
            animateStockOverviewToItem(position);
        }
    }

    private void displayStockOverview(Integer position, boolean animate) {
        if (position != null) {
            setCurrentTile(position);
            setStockOverviewState(StockOverviewState.OVERVIEW);
        }

        displayStockInViewFlipper(position);

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                Extensions.dpToPixels(getResources(), STOCK_OVERVIEW_HEIGHT));
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
        layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
        this.viewFlipper.setLayoutParams(layoutParams);

        if (animate) {
            Animation animation = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
                    R.anim.slide_in_from_bottom);
            animation.setAnimationListener(
                    new StockOverviewAnimationListener(this.animateStockOverviewContentCallback));
            this.viewFlipper.setVisibility(View.VISIBLE);
            this.viewFlipper.startAnimation(animation);
        } else {
            this.viewFlipper.setVisibility(View.VISIBLE);
        }

        collapseSearchActionView();
        invalidateOptionsMenu();
    }

    @TargetApi(14)
    private void collapseSearchActionView() {
        if (isIceCreamSandwichOrLater() && this.searchMenuItem != null
                && this.searchMenuItem.isActionViewExpanded()) {
            this.searchMenuItem.collapseActionView();
        }
    }

    public void hideStockOverview() {
        hideStockOverview(null);
    }

    public void hideStockOverview(final AnimationEndCallback animationEndCallback) {
        if (this.stockOverviewState != null && this.stockOverviewState != StockOverviewState.HIDDEN) {
            setStockOverviewState(StockOverviewState.HIDDEN);

            Animation animation = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
                    R.anim.slide_out_to_bottom);
            animation.setAnimationListener(new StockOverviewAnimationListener(new AnimationEndCallback() {

                @Override
                public void execute() {
                    StockListFragment.this.viewFlipper.setVisibility(View.GONE);
                    StockListFragment.this.viewFlipper.setInAnimation(null);
                    StockListFragment.this.viewFlipper.setOutAnimation(null);

                    setCurrentTile(null);

                    if (animationEndCallback != null) {
                        animationEndCallback.execute();
                    }
                }
            }));

            this.viewFlipper.startAnimation(animation);

            invalidateOptionsMenu();
        } else if (animationEndCallback != null) {
            animationEndCallback.execute();
        }
    }

    @TargetApi(11)
    private void invalidateOptionsMenu() {
        if (isHoneycombOrLater()) {
            getActivity().invalidateOptionsMenu();
        }
    }

    private void animateStockOverviewToNextItem() {
        this.viewFlipper.setInAnimation(this.flipInPreviousAnimation);
        this.viewFlipper.setOutAnimation(this.flipOutPreviousAnimation);

        int position = (Integer) this.viewFlipper.getTag() + 1;
        if (position >= this.callback.getStockList().size()) {
            position = 0;
        }

        if (!((Integer) this.viewFlipper.getTag()).equals(position)) {
            displayStockInViewFlipper(position);
            setCurrentTile((Integer) this.viewFlipper.getTag());
        }

        invalidateOptionsMenu();
    }

    private void animateStockOverviewToPreviousItem() {
        this.viewFlipper.setInAnimation(this.flipInNextAnimation);
        this.viewFlipper.setOutAnimation(this.flipOutNextAnimation);

        int position = (Integer) this.viewFlipper.getTag() - 1;
        if (position < 0) {
            position = this.callback.getStockList().size() - 1;
        }

        if (!((Integer) this.viewFlipper.getTag()).equals(position)) {
            displayStockInViewFlipper(position);
            setCurrentTile((Integer) this.viewFlipper.getTag());
        }

        invalidateOptionsMenu();
    }

    private void displayStockInViewFlipper(Integer position) {
        int availableFlipperChild = this.viewFlipper.getDisplayedChild() == 0 ? 1 : 0;

        StockOverviewLayout stockOverviewLayout = (StockOverviewLayout) this.viewFlipper
                .getChildAt(availableFlipperChild);

        if (position != null) {
            stockOverviewLayout.setStock(this.callback.getStockList().get(position));
        } else {
            stockOverviewLayout.setStock(null);
        }

        this.viewFlipper.setTag(position);
        this.viewFlipper.setDisplayedChild(availableFlipperChild);
    }

    private void animateStockOverviewToItem(Integer position) {
        Integer currentStock = (Integer) this.viewFlipper.getTag();

        int animateFrom = currentStock == null ? Integer.MIN_VALUE : currentStock;

        int animateTo = position == null ? Integer.MAX_VALUE : position;

        if (animateTo < animateFrom) {
            this.viewFlipper.setInAnimation(this.flipInNextAnimation);
            this.viewFlipper.setOutAnimation(this.flipOutNextAnimation);
        } else {
            this.viewFlipper.setInAnimation(this.flipInPreviousAnimation);
            this.viewFlipper.setOutAnimation(this.flipOutPreviousAnimation);
        }

        setCurrentTile(position);
        displayStockInViewFlipper(position);

        invalidateOptionsMenu();
    }

    private void setCurrentTile(Integer position) {
        if (position != null && !position.equals(Integer.MAX_VALUE)) {
            this.callback.getBackStack().add(new StockOverviewBackStackItem(position));
        } else {
            this.callback.getBackStack().removeAllStockOverviewItems();
        }

        setSelection(position);
    }

    public boolean back() {
        boolean handled = false;

        if (this.stockOverviewState != StockOverviewState.HIDDEN) {
            if (this.callback.getBackStack().getStockOverviewItemCount() == 0) {
                hideStockOverview();
                handled = true;
            } else {
                animateStockOverviewToItem(
                        this.callback.getBackStack().getLastStockOverviewAddedToBackStack().getStock());
                handled = true;
            }
        }

        return handled;
    }

    private boolean isOverviewVisible() {
        return this.stockOverviewState != null && this.stockOverviewState != StockOverviewState.HIDDEN;
    }

    @TargetApi(11)
    private void showContextualActionBar() {
        hideStockOverview();

        if (this.actionMode == null) {
            this.actionMode = ((FragmentActivity) getActivity()).startActionMode(this.actionModeCallback);
            updateContextualActionBar();
        }
    }

    @TargetApi(11)
    @Override
    public void hideContextualActionBar() {
        if (isHoneycombOrLater()) {
            if (this.actionMode != null) {
                this.actionMode.finish();
            }
        }
    }

    @Override
    public void onClick(View view) {
        StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) view.getTag();
        if (stockTileViewHolder != null) {
            if (actionMode != null) {
                selectTile(view, true);
            } else {
                if (!view.equals(this.currentlySelectedTile)) {
                    display(stockTileViewHolder.getStock());
                }
            }
        } else {
            requestSearch();
        }
    }

    @Override
    public boolean onLongClick(View view) {
        if (isHoneycombOrLater()) {
            if (this.actionMode == null) {
                showContextualActionBar();
                selectTile(view, false);
                return true;
            }

            startDraggingTile(view);
            return true;
        } else {
            selectTile(view, false);
            showTileContextualMenu(view);
            return true;
        }
    }

    private void showTileContextualMenu(final View view) {
        if (this.tileContextualMenuDialog == null || !this.tileContextualMenuDialog.isShowing()) {
            final StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) view.getTag();
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

            builder.setTitle(String.format(getString(R.string.fragment_stock_list_tile_contextual_menu_title),
                    stockTileViewHolder.getStock().getSymbol()));

            builder.setItems(
                    new CharSequence[] { getString(R.string.fragment_stock_list_tile_contextual_menu_share_item),
                            getString(R.string.fragment_stock_list_tile_contextual_menu_delete_item),
                            getString(R.string.label_cancel) },
                    new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface dialog, int item) {
                            switch (item) {
                            case 0:
                                showShareDialog(view);
                                break;
                            case 1:
                                dialog.dismiss();
                                showDeleteWarningDialog(stockTileViewHolder.getPosition());
                                break;
                            case 2:
                                dialog.dismiss();
                                break;
                            }
                        }
                    });

            this.tileContextualMenuDialog = builder.create();
            this.tileContextualMenuDialog.setOnDismissListener(new OnDismissListener() {

                @Override
                public void onDismiss(DialogInterface dialog) {
                    StockListFragment.this.selectedTiles.clear();
                    StockTileProcessor.updateTileColorBasedOnStock(StockListFragment.this, view);
                }
            });

            this.tileContextualMenuDialog.show();
        }
    }

    private void selectTile(View tile, boolean toggle) {
        StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) tile.getTag();

        if (!stockTileViewHolder.isFixed()) {
            if (toggle && this.selectedTiles.contains(stockTileViewHolder.getPosition())) {
                this.selectedTiles.remove((Integer) stockTileViewHolder.getPosition());

                if (this.selectedTiles.size() == 0 && !isHoneycombOrLater()) {
                    hideContextualActionBar();
                }
            } else if (!this.selectedTiles.contains(stockTileViewHolder.getPosition())) {
                this.selectedTiles.add(stockTileViewHolder.getPosition());
            }

            StockTileProcessor.updateTileColor(this, tile, this.selectedTiles);
            updateContextualActionBar();
        }
    }

    @TargetApi(11)
    private void updateContextualActionBar() {
        if (isHoneycombOrLater()) {
            if (this.actionMode != null) {
                this.actionMode.setTitle(String.format(getResources().getString(R.string.label_cab_selected),
                        this.selectedTiles.size()));
                this.actionMode.invalidate();
            }
        }
    }

    @TargetApi(11)
    @Override
    public boolean onDrag(View tile2, DragEvent event) {
        View tile1 = (View) event.getLocalState();

        StockTileViewHolder stockTileViewHolder1 = (StockTileViewHolder) tile1.getTag();
        StockTileViewHolder stockTileViewHolder2 = (StockTileViewHolder) tile2.getTag();

        switch (event.getAction()) {
        case DragEvent.ACTION_DRAG_STARTED:
            tile1.setVisibility(View.INVISIBLE);

            if (stockTileViewHolder1.getPosition() != stockTileViewHolder2.getPosition()) {
                return true;
            }

            return false;

        case DragEvent.ACTION_DRAG_ENTERED:
            this.focusedTileDuringDragAndDrop = tile2;
            StockTileProcessor.updateTileColorToDropReceptor(tile2);
            tile2.invalidate();

            return true;

        case DragEvent.ACTION_DRAG_LOCATION:
            return true;

        case DragEvent.ACTION_DRAG_EXITED:
            this.focusedTileDuringDragAndDrop = null;
            StockTileProcessor.updateTileColor(this, tile2, this.selectedTiles);
            tile2.invalidate();

            return true;

        case DragEvent.ACTION_DROP:
            this.focusedTileDuringDragAndDrop = null;
            swapTiles(tile1, tile2);
            return true;

        case DragEvent.ACTION_DRAG_ENDED:
            if (this.focusedTileDuringDragAndDrop != null) {
                swapTiles(tile1, this.focusedTileDuringDragAndDrop);
                this.focusedTileDuringDragAndDrop = null;

                return true;
            }

            StockTileProcessor.updateTileColor(this, tile1, this.selectedTiles);
            StockTileProcessor.updateTileColor(this, tile2, this.selectedTiles);

            tile1.setVisibility(View.VISIBLE);
            tile2.setVisibility(View.VISIBLE);

            tile1.invalidate();
            tile2.invalidate();

            return true;

        default:
            return true;
        }
    }

    @TargetApi(11)
    private void startDraggingTile(View tile) {
        StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) tile.getTag();
        this.selectedTiles.remove((Integer) stockTileViewHolder.getPosition());
        updateContextualActionBar();

        ClipData.Item item = new ClipData.Item(stockTileViewHolder.toString());
        ClipData clipData = new ClipData(stockTileViewHolder.toString(), new String[0], item);

        View.DragShadowBuilder dragShadowBuilder = new DragShadowBuilder(tile);
        tile.startDrag(clipData, dragShadowBuilder, tile, 0);
    }

    private void swapTiles(View tile1, View tile2) {
        StockTileViewHolder stockTileViewHolder1 = (StockTileViewHolder) tile1.getTag();
        StockTileViewHolder stockTileViewHolder2 = (StockTileViewHolder) tile2.getTag();

        this.callback.reorderStocks(stockTileViewHolder1.getStock(), stockTileViewHolder2.getStock());

        this.selectedTiles.remove((Integer) stockTileViewHolder1.getPosition());
        this.selectedTiles.remove((Integer) stockTileViewHolder2.getPosition());
        updateContextualActionBar();

        Stock tempStock = stockTileViewHolder1.getStock();
        stockTileViewHolder1.setStock(stockTileViewHolder2.getStock());
        stockTileViewHolder2.setStock(tempStock);

        tile1.setTag(stockTileViewHolder1);
        tile2.setTag(stockTileViewHolder2);

        this.tiles.remove(stockTileViewHolder1.getPosition());
        this.tiles.remove(stockTileViewHolder2.getPosition());

        this.tiles.put(stockTileViewHolder1.getPosition(), tile1);
        this.tiles.put(stockTileViewHolder2.getPosition(), tile2);

        stockTileViewHolder1.refresh(getResources());
        stockTileViewHolder2.refresh(getResources());

        tile1.setVisibility(View.VISIBLE);
        tile2.setVisibility(View.VISIBLE);

        tile1.invalidate();
        tile2.invalidate();
    }

    public void setSelection(Integer position) {
        if (position != null) {
            View tile = this.tiles.get(position);

            if (this.currentlySelectedTile != null) {
                StockTileProcessor.updateTileColor(this, this.currentlySelectedTile, this.selectedTiles);
            }

            this.currentlySelectedTile = tile;

            StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) tile.getTag();

            Extensions.applyPattern(getResources(), stockTileViewHolder.getTileLayout(),
                    PatternBackgroundColor.BLUE);
        } else {
            if (this.currentlySelectedTile != null) {
                StockTileProcessor.updateTileColor(this, this.currentlySelectedTile, this.selectedTiles);
            }

            this.currentlySelectedTile = null;
        }
    }

    public void refresh(boolean hardReset) {
        hideStockOverview();
        collapseSearchActionView();
        createDashboardTiles(hardReset);
    }

    private void createDashboardTiles(boolean restart) {
        StockTileProcessor.create(this, this.tableLayout, this.callback.getStockList(), this.tiles, restart);
    }

    public void refreshStockInformation(List<Stock> stocks) {
        try {
            for (Stock stock : stocks) {
                View view = findTile(stock);
                StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) view.getTag();
                if (stockTileViewHolder != null) {
                    stockTileViewHolder.refresh(getResources(), stock);
                }

                if (this.viewFlipper != null && this.viewFlipper.getTag() != null) {
                    StockOverviewLayout stockOverviewLayout = (StockOverviewLayout) this.viewFlipper
                            .getChildAt(this.viewFlipper.getDisplayedChild());
                    if (stockOverviewLayout != null
                            && stockOverviewLayout.getStock().getSymbol().equals(stock.getSymbol())) {
                        stockOverviewLayout.setStock(stock);
                    }
                }
            }
        } catch (IllegalStateException e) {
            /*
             * For some reason, some devices are reporting an IllegalStateException when trying to
             * access the method
             * getResources() from above. The full error message is the following:
             * java.lang.IllegalStateException: Fragment StockListFragment{40540d98} not attached to
             * Activity
             * 
             * Since this error happens while refreshing the stock information, I'm going to ignore
             * it.
             */

            Log.e(LOG_TAG, e.getMessage(), e);
        }
    }

    private View findTile(Stock stock) {
        for (int i = 0; i < this.tiles.size(); i++) {
            View view = this.tiles.valueAt(i);

            StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) view.getTag();
            if (stockTileViewHolder.getStock().getSymbol().equals(stock.getSymbol())) {
                return view;
            }
        }

        return null;
    }

    @TargetApi(14)
    private void requestSearch() {
        hideContextualActionBar();

        if (isIceCreamSandwichOrLater()) {
            hideStockOverview(new AnimationEndCallback() {

                @Override
                public void execute() {
                    StockListFragment.this.searchMenuItem.expandActionView();
                }
            });
        } else {
            getActivity().onSearchRequested();
        }
    }

    private void showDeleteWarningDialog(Integer position) {
        if (this.removeSelectedStocksDialog == null || !this.removeSelectedStocksDialog.isShowing()) {

            if (position == null) {
                if (this.selectedTiles.size() == 1) {
                    position = ((StockTileViewHolder) this.tiles.get(this.selectedTiles.get(0)).getTag())
                            .getPosition();
                }
            }

            final Stock stock = position != null
                    ? ((StockTileViewHolder) this.tiles.get(position).getTag()).getStock()
                    : null;

            AlertDialog.Builder removeSelectedStocksDialogBuilder = new AlertDialog.Builder(getActivity());

            String message = this.callback.getRemoveWarningDialogMessage(stock);

            View view = getLayoutInflater().inflate(R.layout.dialog_message, null);
            TextView textView = (TextView) view.findViewById(R.id.textView);
            textView.setText(message);

            removeSelectedStocksDialogBuilder.setIcon(R.drawable.ic_warning)
                    .setTitle(this.callback.getRemoveWarningDialogTitle(stock)).setView(view)
                    .setPositiveButton(R.string.label_remove, new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            removeSelectedStocks(stock);
                        }
                    }).setNegativeButton(R.string.label_cancel, null);

            this.removeSelectedStocksDialog = removeSelectedStocksDialogBuilder.create();
            this.removeSelectedStocksDialog.show();
        }
    }

    private void showShareDialog(View view) {
        Integer position = null;
        if (view != null) {
            StockTileViewHolder stockTileViewHolder = (StockTileViewHolder) view.getTag();
            position = stockTileViewHolder.getPosition();
        }

        startActivity(Intent.createChooser(getDefaultShareIntent(position),
                getResources().getString(R.string.fragment_stock_overview_share_dialog_title)));
    }

    private void removeSelectedStocks(Stock stock) {
        List<Stock> stockListToRemove = new ArrayList<Stock>();

        if (stock == null) {
            for (int position : this.selectedTiles) {
                stockListToRemove.add(((StockTileViewHolder) this.tiles.get(position).getTag()).getStock());
            }
        } else {
            stockListToRemove.add(stock);
        }

        this.callback.removeStocks(stockListToRemove);

        hideContextualActionBar();
        createDashboardTiles(false);
    }

    public StockOverviewState getStockOverviewState() {
        return this.stockOverviewState;
    }

    public void setStockOverviewState(StockOverviewState stockOverviewState) {
        this.stockOverviewState = stockOverviewState;
    }

    private LayoutInflater getLayoutInflater() {
        if (this.inflater == null) {
            this.inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        return this.inflater;
    }

    @TargetApi(11)
    private class ActionModeCallback implements ActionMode.Callback {

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.fragment_stock_list_cab, menu);

            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            MenuItem deleteMenuItem = menu.findItem(R.id.menu_item_delete);
            deleteMenuItem.setVisible(StockListFragment.this.selectedTiles.size() > 0);

            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            if (item.getItemId() == R.id.menu_item_delete) {
                showDeleteWarningDialog(null);
            }

            return false;
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            StockListFragment.this.actionMode = null;

            for (Integer position : StockListFragment.this.selectedTiles) {
                StockTileProcessor.updateTileColorBasedOnStock(StockListFragment.this,
                        StockListFragment.this.tiles.get(position));
            }

            StockListFragment.this.selectedTiles.clear();
        }
    }

    public interface StockListFragmentCallback {
        public List<Stock> getStockList();

        public void removeStocks(List<Stock> stocks);

        public void reorderStocks(Stock stock1, Stock stock2);

        public String getRemoveWarningDialogTitle(Stock stock);

        public String getRemoveWarningDialogMessage(Stock stock);

        public void onStockOverviewAddedToBackStack();

        public void onStockOverviewClearBackStack();

        public BackStack getBackStack();
    }

    private interface AnimationEndCallback {
        public void execute();
    }

    private class StockOverviewAnimationListener implements AnimationListener {

        private final AnimationEndCallback callback;

        public StockOverviewAnimationListener(AnimationEndCallback callback) {
            this.callback = callback;
        }

        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            if (this.callback != null) {
                this.callback.execute();
            }
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }

    }

    private class AnimateStockOverviewContentCallback implements AnimationEndCallback {

        @Override
        public void execute() {
            StockOverviewLayout stockOverviewLayout = (StockOverviewLayout) StockListFragment.this.viewFlipper
                    .getChildAt(StockListFragment.this.viewFlipper.getDisplayedChild());
            stockOverviewLayout.animateContent();
        }
    }

    private class ViewFlipperGestureDetector extends SimpleOnGestureListener {

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            StockOverviewLayout stockOverviewLayout = (StockOverviewLayout) StockListFragment.this.viewFlipper
                    .getChildAt(StockListFragment.this.viewFlipper.getDisplayedChild());

            int swipeMinDistance = stockOverviewLayout.getHeight() / 3;

            try {
                if (e1.getX() - e2.getX() > swipeMinDistance && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    animateStockOverviewToNextItem();
                    return true;
                }

                if (e2.getX() - e1.getX() > swipeMinDistance && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    animateStockOverviewToPreviousItem();
                    return true;
                }

                if (e2.getY() - e1.getY() > swipeMinDistance && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
                    StockListFragment.this.hideStockOverview();
                    return true;
                }

                if (e1.getY() - e2.getY() > swipeMinDistance && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
                }
            } catch (Exception e) {
            }

            return false;
        }
    }

}