info.schnatterer.nusic.android.activities.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for info.schnatterer.nusic.android.activities.MainActivity.java

Source

/* Copyright (C) 2013 Johannes Schnatterer
 * 
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *  
 * This file is part of nusic.
 * 
 * nusic 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, either version 3 of the License, or
 * (at your option) any later version.
    
 * nusic 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 nusic.  If not, see <http://www.gnu.org/licenses/>.
 */
package info.schnatterer.nusic.android.activities;

import info.schnatterer.nusic.Constants;
import info.schnatterer.nusic.Constants.Loaders;
import info.schnatterer.nusic.R;
import info.schnatterer.nusic.android.LoadNewRelasesServiceBinding;
import info.schnatterer.nusic.android.application.NusicApplication;
import info.schnatterer.nusic.android.fragments.ReleaseListFragment;
import info.schnatterer.nusic.android.service.LoadNewReleasesService;
import info.schnatterer.nusic.android.util.TextUtil;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;

/**
 * The activity that is started when the app starts.
 * 
 * The tab that is shown can parameterized using the {@link #EXTRA_ACTIVE_TAB},
 * that contains a {@link TabDefinition}.
 * 
 * @author schnatterer
 *
 */
public class MainActivity extends SherlockFragmentActivity {
    /** The request code used when starting {@link PreferenceActivity}. */
    private static final int REQUEST_CODE_PREFERENCE_ACTIVITY = 0;
    /**
     * Key to the creating intent's extras that contains a {@link TabDefinition}
     * (as int) that can be passed to this activity within the bundle of an
     * intent. The corresponding value represents the Tab that is set active
     * with the intent.
     */
    public static final String EXTRA_ACTIVE_TAB = "nusic.intent.extra.main.activeTab";

    /** Start and bind the {@link LoadNewReleasesService}. */
    private static LoadNewRelasesServiceBinding loadNewRelasesServiceBinding = null;

    /**
     * <code>false</code>, if {@link #onCreate(Bundle)} has never been called.
     * Useful for not showing the welcome screen multiple times.
     */
    private static boolean onCreateCalled = false;

    /**
     * Stores the selected tab, even when the configuration changes. The tab
     * assigned here is the one that is shown when the application is started
     * for the first time.
     */
    private static TabDefinition currentTab = TabDefinition.JUST_ADDED;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Set underlying layout (view pager that captures swipes)
        setContentView(R.layout.activity_main);

        /* Init tab fragments */
        final ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Capture page swipes
        ViewPager pager = (ViewPager) findViewById(R.id.mainPager);
        ViewPager.SimpleOnPageChangeListener ViewPagerListener = new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                // Find the ViewPager Position
                actionBar.setSelectedNavigationItem(position);
            }
        };
        pager.setOnPageChangeListener(ViewPagerListener);
        // Set adapter that handles fragment (i.e. tab creation)
        TabFragmentPagerAdapter tabAdapter = new TabFragmentPagerAdapter(getSupportFragmentManager());
        pager.setAdapter(tabAdapter);

        if (getIntent().hasExtra(EXTRA_ACTIVE_TAB)) {
            currentTab = ((TabDefinition) getIntent().getExtras().get(EXTRA_ACTIVE_TAB));
        }
        // Create all tabs as defined in adapter
        TabListener tabListener = new TabListener(pager);
        for (TabDefinition tab : TabDefinition.values()) {
            actionBar.addTab(actionBar.newTab().setText(tab.titleId).setTabListener(tabListener), tab.position,
                    isTabSelected(tab));
        }

        // Handle first app start, if necessary
        if (!onCreateCalled) {
            onCreateCalled = true;
            switch (NusicApplication.getAppStart()) {
            case FIRST:
                showWelcomeDialog(TextUtil.loadTextFromAsset(this, "welcomeDialog.html", true));
                /*
                 * The initialization is finished once the user dismisses the
                 * dialog in order to avoid overlapping dialogs.
                 */
                return;
            case UPGRADE:
                showWelcomeDialog(TextUtil.loadTextFromAsset(this, "CHANGELOG.html"));
                /*
                 * The initialization is finished once the user dismisses the
                 * dialog in order to avoid overlapping dialogs.
                 */
                return;
            default:
                break;
            }
        }
        // Finish initialization, because no dialog was opened
        registerListenersAndStartLoading(false);
    }

    /**
     * Calls {@link #registerListeners()} and also
     * {@link #startLoadingReleasesFromInternet(boolean)} if necessary.
     */
    private void registerListenersAndStartLoading(boolean forceUpdate) {
        if (loadNewRelasesServiceBinding == null) {
            loadNewRelasesServiceBinding = new LoadNewRelasesServiceBinding();
            registerListeners();
            if (forceUpdate) {
                startLoadingReleasesFromInternet(false);
            } else {
                // Update ony if necessary
                startLoadingReleasesFromInternet(true);
            }

        } else {
            registerListeners();
        }
    }

    public boolean isFirstStart() {
        switch (NusicApplication.getAppStart()) {
        case FIRST:
            // Fall through is intended
        case UPGRADE:
            return true;
        default:
            break;
        }

        /*
         * TODO check if there are new artists on the device and refresh if
         * neccessary. Then update interface comment for this method!
         */
        return false;
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        /*
         * This is needed for Android 2.x when clicking a notification an the
         * task is already running, the notification is not delivered, but
         * instead this method is called. getIntent() seems to always return the
         * first intent that started the app.
         * 
         * For Android 4.4.2 this seems not to be called anymore. However, the
         * intent is delivered anyway and getIntent() always returns the last
         * intent that was delivered.
         */
        if (intent.hasExtra(EXTRA_ACTIVE_TAB)) {
            getSupportActionBar()
                    .setSelectedNavigationItem(((TabDefinition) intent.getExtras().get(EXTRA_ACTIVE_TAB)).position);
        }
    }

    /**
     * @param tab
     * @return <code>true</code> if <code>tab</code> is selected, otherwise
     *         <code>false</code>
     */
    private boolean isTabSelected(TabDefinition tab) {
        if (currentTab.equals(tab)) {
            return true;
        }
        return false;
    }

    private void registerListeners() {
        // Set activity as new context of task
        loadNewRelasesServiceBinding.updateActivity(this);
        // loadReleasesTask.bindService();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_refresh) {
            startLoadingReleasesFromInternet(false);
        } else if (item.getItemId() == R.id.action_settings) {
            if (!loadNewRelasesServiceBinding.isRunning()) {
                startActivityForResult(new Intent(this, NusicPreferencesActivity.class),
                        REQUEST_CODE_PREFERENCE_ACTIVITY);
            } else {
                /*
                 * Refreshing releases is in progress, stop user from changing
                 * service related preferences
                 */
                // TODO cancel service and restart if necessary after changing
                // of preferences?
                NusicApplication.toast(R.string.MainActivity_pleaseWaitUntilRefreshIsFinished);
                loadNewRelasesServiceBinding.showDialog();
            }
        }

        return true;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_PREFERENCE_ACTIVITY) {
            // Change in preferences require a full refresh
            if (data.getBooleanExtra(NusicPreferencesActivity.EXTRA_RESULT_IS_REFRESH_NECESSARY, false)) {
                startLoadingReleasesFromInternet(false);
            }
            if (data.getBooleanExtra(NusicPreferencesActivity.EXTRA_RESULT_IS_CONTENT_CHANGED, false)) {
                onContentChanged();
            }
        }
    }

    private void startLoadingReleasesFromInternet(boolean updateOnlyIfNeccesary) {
        boolean wasRunning = !loadNewRelasesServiceBinding.refreshReleases(this, updateOnlyIfNeccesary);
        Log.d(Constants.LOG,
                "Explicit refresh triggered. Service was " + (wasRunning ? "" : " not ") + "running before");

        if (wasRunning && !updateOnlyIfNeccesary) {
            // Task is already running, just show dialog
            NusicApplication.toast(R.string.MainActivity_refreshAlreadyInProgress);
            loadNewRelasesServiceBinding.showDialog();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (loadNewRelasesServiceBinding != null) {
            registerListeners();
            if (loadNewRelasesServiceBinding.checkDataChanged()) {
                onContentChanged();
            }
        }
    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        final ViewPager pager = (ViewPager) findViewById(R.id.mainPager);
        final TabFragmentPagerAdapter adapter = (TabFragmentPagerAdapter) pager.getAdapter();
        if (adapter != null) {
            runOnUiThread(new Runnable() {
                public void run() {
                    for (TabDefinition tabDefinition : TabDefinition.values()) {
                        Loader<Object> loader = getSupportLoaderManager().getLoader(tabDefinition.loaderId);
                        if (loader != null) {
                            loader.onContentChanged();
                        }
                    }
                }
            });
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (loadNewRelasesServiceBinding != null) {
            unregisterListeners();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (loadNewRelasesServiceBinding != null) {
            unregisterListeners();
        }
    }

    private void unregisterListeners() {
        loadNewRelasesServiceBinding.updateActivity(null);
        loadNewRelasesServiceBinding.unbindService();
    }

    /**
     * Shows an alert dialog displaying some text. Useful for welcome messages.
     * Calls {@link #registerListenersAndStartLoading()} when the dialog is
     * dismissed.
     * 
     * @param text
     *            text to display. If loading from an asset, consider using
     *            {@link TextUtil#loadTextFromAsset(android.content.Context, String)}
     */
    @SuppressLint("InflateParams")
    // See http://www.doubleencore.com/2013/05/layout-inflation-as-intended/
    private void showWelcomeDialog(CharSequence text) {
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
        View layout = getLayoutInflater().inflate(R.layout.simple_textview_layout, null, false);
        TextView textView = (TextView) layout.findViewById(R.id.renderRawHtmlTextView);
        textView.setText(text);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
        alertDialogBuilder
                .setTitle(getString(R.string.WelcomeScreenTitle, NusicApplication.getCurrentVersionName()))
                .setIcon(R.drawable.ic_launcher).setOnCancelListener(new OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        registerListenersAndStartLoading(true);
                    }
                }).setPositiveButton(android.R.string.ok, new OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        registerListenersAndStartLoading(true);
                    }
                }).setView(layout).show();
    }

    /**
     * Holds the basic information for each tab.
     * 
     * @author schnatterer
     */
    public static enum TabDefinition {
        /** First tab: Just added */
        JUST_ADDED(R.string.MainActivity_TabJustAdded, Loaders.RELEASE_LOADER_JUST_ADDED),
        /** Second tab: Available releases */
        AVAILABLE(R.string.MainActivity_TabAvailable, Loaders.RELEASE_LOADER_AVAILABLE),
        /** Third tab: Announced releases */
        ANNOUNCED(R.string.MainActivity_TabAnnounced, Loaders.RELEASE_LOADER_ANNOUNCED),
        /** Fourth tab: All releases */
        ALL(R.string.MainActivity_TabAll, Loaders.RELEASE_LOADER_ALL);

        private final int position;
        private final int titleId;
        private final int loaderId;

        private TabDefinition(int titleId, int loaderId) {
            this.position = ordinal();
            this.titleId = titleId;
            this.loaderId = loaderId;
        }
    }

    /**
     * A fragment pager adapter that defines the content of the tabs and manages
     * the fragments that basically show the content of the tabs.
     * 
     * @author schnatterer
     * 
     */
    public class TabFragmentPagerAdapter extends FragmentPagerAdapter {
        ReleaseListFragment[] fragments = new ReleaseListFragment[TabDefinition.values().length];

        public TabFragmentPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            int actualPos = position;
            if (fragments.length <= position) {
                // Default tab
                actualPos = 0;
            }
            ReleaseListFragment fragment = createTabFragment(TabDefinition.values()[actualPos]);
            if (position < fragments.length) {
                fragments[position] = fragment;
            }
            return fragment;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            super.destroyItem(container, position, object);
            if (position < fragments.length) {
                fragments[position] = null;
            }
        }

        private ReleaseListFragment createTabFragment(TabDefinition tab) {
            // Create fragment
            Bundle bundle = new Bundle();
            bundle.putInt(ReleaseListFragment.EXTRA_LOADER_ID, tab.loaderId);
            return (ReleaseListFragment) Fragment.instantiate(MainActivity.this,
                    ReleaseListFragment.class.getName(), bundle);
        }

        @Override
        public int getCount() {
            return fragments.length;
        }
    }

    /**
     * Tab listener that reports the current position to a view pager.
     * 
     * @author schnatterer
     */
    public class TabListener implements ActionBar.TabListener {
        private ViewPager pager;

        public TabListener(ViewPager pager) {
            this.pager = pager;
        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            // Pass the position on tab click to ViewPager
            pager.setCurrentItem(tab.getPosition());
            currentTab = TabDefinition.values()[tab.getPosition()];
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        }

        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
        }
    };
}