com.radiusnetworks.museumguide.MuseumItemsActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.radiusnetworks.museumguide.MuseumItemsActivity.java

Source

/**
 *
 * Copyright (c) 2013,2014 RadiusNetworks. All rights reserved.
 * http://www.radiusnetworks.com
 *
 * @author David G. Young
 *
 * Licensed to the Attribution Assurance License (AAL)
 * (adapted from the original BSD license) See the LICENSE file
 * distributed with this work for additional information
 * regarding copyright ownership.
 *
 */
package com.radiusnetworks.museumguide;

import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.net.Uri;
import android.support.v4.app.FragmentManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.Date;
import java.util.Stack;

/**
 * This class is the main activity for the application.  It controls a swipeable list of
 * MuseumItemFragments that can be in one of two modes.  Sequential mode is a static list of all
 * items in the museum that can be browsed.  Automatic mode is a list of all the items the user has
 * visited before, and new items are put on the stack when the user taps a notification bar at the
 * top of the screen when an item is nearby, which causes a new item to be displayed and put on the
 * stack of what the user has visited.
 *
 * Created by dyoung on 3/6/14.
 */
public class MuseumItemsActivity extends FragmentActivity {
    private static final String TAG = "MuseumItemsActivity";
    private ViewPager viewPager;
    private VisitedItemCollectionPagerAdapter visitedItemCollectionPagerAdapter;
    private SequentialItemCollectionPagerAdapter sequentialItemCollectionPagerAdapter;
    private MuseumGuideApplication application;
    private Stack<String> itemStack = new Stack<String>();
    private String currentItemId = null;
    private String nextItemId = null;
    private MuseumControls museumControls;
    private boolean mSuppressPageChangeEvents = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        setContentView(R.layout.activity_museum_items);
        application = (MuseumGuideApplication) this.getApplication();

        application.setMuseumItemsActivity(this);

        if (this.getIntent().getExtras() != null) {
            currentItemId = this.getIntent().getExtras().getString("item_id");
        } else {
            // By default, show the very first item in the museum, unless told to do otherwise
            currentItemId = application.getMuseum().getItemList().get(0).getId();
        }
        viewPager = (ViewPager) findViewById(R.id.pager);
        museumControls = new MuseumControls(findViewById(R.id.activity_museum_items));
        findViewById(R.id.search_dialog).setVisibility(View.INVISIBLE);

        setAutomaticMode();
        hideNextItemNotification();
        Log.d(TAG, "Museum at startup:");
        for (MuseumItem item : application.getMuseum().getItemList()) {
            Log.d(TAG, "Item id " + item.getId() + " has title: " + item.getTitle());
        }
    }

    /**
     * Sets this Activity to sequential mode.  This mode is a simple sequential list of all items
     * in the museum that the user swipes through.  iBeacons are not used.
     */
    private void setSequentialMode() {
        sequentialItemCollectionPagerAdapter = new SequentialItemCollectionPagerAdapter(
                getSupportFragmentManager());
        viewPager.setAdapter(sequentialItemCollectionPagerAdapter);
        viewPager.setOnPageChangeListener(sequentialItemPageChangeListener);
        museumControls.setAutoMode(false);
        nextItemId = null;
        hideNextItemNotification();

    }

    /**
     * Sets this Activity to automatic mode.  This mode shows a swipeable history of where the user
     * has been, but new items are only shown by tapping on a notification when one becomoes nearby.
     */
    private void setAutomaticMode() {
        visitedItemCollectionPagerAdapter = new VisitedItemCollectionPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(visitedItemCollectionPagerAdapter);
        viewPager.setOnPageChangeListener(visitedItemPageChangeListener);
        museumControls.setAutoMode(true);
        nextItemId = null;
        itemStack.empty();
        hideNextItemNotification();
    }

    //------------------------------------------------------------------
    // The following section of code is only for Sequential Mode
    //------------------------------------------------------------------

    private String getItemForSequentialPage(int i) {
        MuseumItem item = application.getMuseum().getItemList().get(i);
        return item.getId();
    }

    private int getSequentialItemPageCount() {
        return application.getMuseum().getItemList().size();
    }

    /**
     * Executed when the user taps the search button on the search dialog.  It looks for an exact
     * match to the item id first, and if it doesn't find one, it tries to do a partial string match
     * to the item title.  If any match is found, the pager scrolls to that screen.
     *
     * @param text
     */
    private void search(String text) {
        // first, look for an exact match on id
        MuseumItem match = null;
        match = application.getMuseum().getItemById(text);
        if (match == null) {
            for (MuseumItem candidate : application.getMuseum().getItemList()) {
                if (candidate.getTitle().toLowerCase().indexOf(text.toLowerCase()) >= 0) {
                    match = candidate;
                    break;
                }
            }
        }
        if (match == null) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Item not found");
            builder.setMessage("No matching museum item exists.");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.show();

        } else {
            final MuseumItem matchToShow = match;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    viewPager.setCurrentItem(application.getMuseum().getItemIndexById(matchToShow.getId()));
                }
            });
        }
    }

    ViewPager.OnPageChangeListener sequentialItemPageChangeListener = new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageScrollStateChanged(int arg0) {
            Log.d(TAG, "scrollStateChanged");
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
            Log.d(TAG, "pageScrolled");
        }

        @Override
        public void onPageSelected(int pos) {
            Log.d(TAG, "We just swiped to a new page");
            Log.d(TAG, "count is " + getVisitedItemPageCount());
            Log.d(TAG, "pos is " + pos);
        }

    };

    class SequentialItemCollectionPagerAdapter extends FragmentStatePagerAdapter {
        public SequentialItemCollectionPagerAdapter(FragmentManager fm) {
            super(fm);
            Log.d(TAG, "Constructed pager adapter that will create fragments");
        }

        @Override
        public Fragment getItem(int i) {
            MuseumItemFragment fragment = new MuseumItemFragment();
            String itemId = application.getMuseum().getItemList().get(i).getId();
            Log.d(TAG, "setting up fragment with " + itemId);
            fragment.showItem(itemId);

            return fragment;
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_UNCHANGED;
        }

        @Override
        public int getCount() {
            Log.d(TAG, "Got count from  pager adapter that will create fragments");
            return application.getMuseum().getItemList().size();
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "POSITION " + (position + 1);
        }
    }

    //------------------------------------------------------------------
    // The following section of code is only for Automatic Mode
    //------------------------------------------------------------------

    private String getItemForVisitedPage(int i) {
        String itemId = null;
        if (i < itemStack.size()) {
            return itemStack.get(i);
        }
        if (i == itemStack.size()) {
            return currentItemId;
        }
        return null;
    }

    private int getVisitedItemPageCount() {
        int pageCount = itemStack.size() + 1;
        Log.d(TAG, "previously visited page count is " + pageCount);
        return pageCount;
    }

    ViewPager.OnPageChangeListener visitedItemPageChangeListener = new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageScrollStateChanged(int arg0) {
            Log.d(TAG, "scrollStateChanged visited");
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
            Log.d(TAG, "pageScrolled visited");

        }

        @Override
        public void onPageSelected(int pos) {
            // This is needed because on Android L, this event fires when we manually change the
            // page when a user taps on the button to go to the next item.  Without the suppression,
            // it was deciding that the user had swiped backwards, effectively leaving the user stuck
            // on a single page in Automatic mode.
            if (mSuppressPageChangeEvents) {
                Log.d(TAG, "*** onPageSelected event suppressed ***");
                return;
            }
            Log.d(TAG, "We just swiped to a new page");
            Log.d(TAG, "count is " + getVisitedItemPageCount());
            Log.d(TAG, "pos is " + pos);
            Log.d(TAG, "nextItemId=" + nextItemId);
            Log.d(TAG, "previously visited page count is " + itemStack.size());
            if (itemStack.size() > 0) {
                Log.d(TAG, "We seem to have gone backward");
                nextItemId = null;
                currentItemId = itemStack.pop();
                visitedItemCollectionPagerAdapter.notifyDataSetChanged(); // we just deleted the last page
            }
        }

    };

    public void recalculateNextItem(VisibleMuseumItems visibleMuseumItems) {
        String lastItemId = itemStack.empty() ? null : itemStack.peek();
        Log.d(TAG, "closest: ignoring " + lastItemId + " and " + currentItemId);
        MuseumItem nextItem = visibleMuseumItems.calculateClosestItemWithExceptions(currentItemId, lastItemId,
                nextItemId);
        showNextItem(nextItem == null ? null : nextItem.getId());
    }

    private void showNextItemNotification(final String itemId, final String title) {
        View button = findViewById(R.id.nextButtonLayout);
        Button titleButton = (Button) findViewById(R.id.nextButtonTitle);
        titleButton.setText("Next Item: " + title);
        button.setVisibility(View.VISIBLE);
        Log.d(TAG, "seting up tap listener");
        View.OnClickListener onClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d(TAG, "next item button tapped");
                if (museumControls.getAutoMode()) {
                    int currentPage = viewPager.getCurrentItem();
                    mSuppressPageChangeEvents = true;
                    itemStack.push(currentItemId);
                    currentItemId = itemId;
                    viewPager.getAdapter().notifyDataSetChanged();
                    viewPager.setCurrentItem(currentPage + 1);
                    mSuppressPageChangeEvents = false;
                    Log.d(TAG, "We should have scrolled to the next item.  Everything should be done.");
                } else {
                    // scroll to next item
                    int index = MuseumItemsActivity.this.application.getMuseum().getItemIndexById(itemId);
                    viewPager.setCurrentItem(index);
                }
                runOnUiThread(new Runnable() {
                    public void run() {
                        View button = findViewById(R.id.nextButtonLayout);
                        button.setVisibility(View.INVISIBLE);
                    }
                });
            }
        };
        ((Button) findViewById(R.id.nextButton)).setOnClickListener(onClickListener);
        ((Button) findViewById(R.id.nextButtonTitle)).setOnClickListener(onClickListener);

    }

    /*
     Called when an ibeacon detection changes the next item
     */
    private void showNextItem(final String itemId) {
        if (!museumControls.getAutoMode()) {
            return;
        } else {
            if (itemId != null && itemId.equals(nextItemId)) {
                Log.d(TAG, "Next item is unchanged.  Doing nothing");
            } else {
                // only show the next item if it has been five seconds since entering auto
                // mode.  this gives the user time to read the instructions that appear
                // underneath
                if (museumControls.getSecondsSinceModeSwitch() > 5) {
                    nextItemId = itemId;
                    runOnUiThread(new Runnable() {
                        public void run() {
                            if (itemId == null) {
                                hideNextItemNotification();
                            } else {
                                MuseumItem item = application.getMuseum().getItemById(itemId);
                                Log.d(TAG, "showing next item " + item.getId() + " with title " + item.getTitle());
                                showNextItemNotification(item.getId(), item.getTitle());
                                View headerInstructions = findViewById(R.id.headerInstructions);
                                headerInstructions.setVisibility(View.INVISIBLE);
                            }
                        }
                    });
                }
                return;
            }
        }
        runOnUiThread(new Runnable() {
            public void run() {
                if (itemId == null) {
                    nextItemId = itemId;
                    visitedItemCollectionPagerAdapter.notifyDataSetChanged(); // we just added a page!
                    return;
                } else if (itemId.equals(nextItemId)) {
                    Log.d(TAG, "Next item is unchanged.  Doing nothing");
                } else {
                    nextItemId = itemId;
                    visitedItemCollectionPagerAdapter.notifyDataSetChanged(); // we just changed a page!
                }
            }
        });

    }

    class VisitedItemCollectionPagerAdapter extends FragmentStatePagerAdapter {
        public VisitedItemCollectionPagerAdapter(FragmentManager fm) {
            super(fm);
            Log.d(TAG, "Constructed pager adapter that will create fragments");
        }

        @Override
        public Fragment getItem(int i) {
            MuseumItemFragment fragment = new MuseumItemFragment();
            String itemId = getItemForVisitedPage(i);
            Log.d(TAG, "setting up fragment with " + itemId);
            fragment.showItem(itemId);

            return fragment;
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }

        @Override
        public int getCount() {
            Log.d(TAG, "Got count from  pager adapter that will create fragments");
            return getVisitedItemPageCount();
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "POSITION " + (position + 1);
        }
    }

    //--------------------------------------------------------------

    private void hideNextItemNotification() {
        View button = findViewById(R.id.nextButtonLayout);
        button.setVisibility(View.INVISIBLE);
    }

    private class MuseumControls {
        public MuseumControls(View controlLayoutView) {
            mainView = controlLayoutView;
            autoButton = (ImageButton) mainView.findViewById(R.id.autoButton);
            sequentialButton = (ImageButton) mainView.findViewById(R.id.sequentialButton);
            autoButton.setOnClickListener(modeTapListener);
            sequentialButton.setOnClickListener(modeTapListener);
            searchButton = (ImageView) mainView.findViewById(R.id.searchButton);
            searchButton.setOnClickListener(searchTapListener);
            headerInstructions = (TextView) mainView.findViewById(R.id.headerInstructions);
        }

        private View mainView;
        private ImageButton autoButton;
        private ImageButton sequentialButton;
        private ImageView searchButton;
        private TextView headerInstructions;
        private Date modeSwitchTime = new Date();

        private boolean autoMode;

        public void setAutoMode(boolean mode) {
            if (autoMode != mode) {
                modeSwitchTime = new Date();
                autoMode = mode;
                updateDisplay();
            }
        }

        public boolean getAutoMode() {
            return autoMode;
        }

        public long getSecondsSinceModeSwitch() {
            return (new Date().getTime() - modeSwitchTime.getTime()) / 1000;
        }

        public void updateDisplay() {
            runOnUiThread(new Runnable() {
                public void run() {
                    int museumGreen = Color.parseColor("#aecc79");
                    if (autoMode) {
                        autoButton.setBackgroundColor(museumGreen);
                        sequentialButton.setBackgroundColor(Color.WHITE);
                        sequentialButton.setImageResource(R.drawable.ordered_off);
                        searchButton.setImageResource(R.drawable.search_off);
                        autoButton.setImageResource(R.drawable.auto);
                        headerInstructions.setText(
                                "Automatic mode: Tap top bar when next item is nearby.  Swipe left to go back.");
                        headerInstructions.setVisibility(View.VISIBLE);
                    } else {
                        sequentialButton.setBackgroundColor(museumGreen);
                        autoButton.setBackgroundColor(Color.WHITE);
                        sequentialButton.setImageResource(R.drawable.ordered);
                        autoButton.setImageResource(R.drawable.auto_off);
                        searchButton.setImageResource(R.drawable.search);
                        headerInstructions.setText("Browsing mode: swipe left and right to explore the museum.");
                        headerInstructions.setVisibility(View.VISIBLE);
                    }

                    // Now swtich the viewcontroller's functionality
                    if (autoMode) {
                        setAutomaticMode();
                    } else {
                        setSequentialMode();
                    }
                }
            });
        }

        public View.OnClickListener modeTapListener = new View.OnClickListener() {
            public void onClick(View view) {
                Log.d(TAG, "button tapped");
                if (autoMode) {
                    if (view == sequentialButton)
                        setAutoMode(false);
                } else if (view == autoButton) {
                    setAutoMode(true);
                }
            }
        };

        public View.OnClickListener searchTapListener = new View.OnClickListener() {
            public void onClick(View view) {
                Log.d(TAG, "search button tapped");
                if (autoMode) {
                    Log.d(TAG, "ignored in auto mode");
                    return;
                }
                if (findViewById(R.id.search_dialog).getVisibility() == View.INVISIBLE) {
                    Log.d(TAG, "search dialog not shown");
                    ((EditText) findViewById(R.id.search_text)).setText("");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            findViewById(R.id.search_dialog).setVisibility(View.VISIBLE);
                            findViewById(R.id.search_go_button).setOnClickListener(searchGoTapListener);
                            InputMethodManager imm = (InputMethodManager) getSystemService(
                                    Context.INPUT_METHOD_SERVICE);
                        }
                    });
                }

            }
        };

        public View.OnClickListener searchGoTapListener = new View.OnClickListener() {
            public void onClick(View view) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        findViewById(R.id.search_dialog).setVisibility(View.INVISIBLE);
                        InputMethodManager imm = (InputMethodManager) getSystemService(
                                Context.INPUT_METHOD_SERVICE);
                        imm.hideSoftInputFromWindow(findViewById(R.id.search_text).getWindowToken(), 0);
                    }
                });

                search(((EditText) findViewById(R.id.search_text)).getText().toString());
            }
        };

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        return new OptionsMenuCreator(this, application).onCreateOptionsMenu(menu);
    }

}