de.gruenewald.udacity.spotifystreamer.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for de.gruenewald.udacity.spotifystreamer.MainActivity.java

Source

/**
 * ****************************************************************************
 * Copyright (c) 2015 by Jan Grnewald.
 * jan.gruenewald84@googlemail.com
 * -------------------------------------------------------------------------------
 * This file is part of 'Spotify Streamer'. 'Spotify Streamer' was developed as
 * part of the Android Developer Nanodegree by Udacity. For further
 * information see:
 * https://www.udacity.com/course/android-developer-nanodegree--nd801
 * -------------------------------------------------------------------------------
 * 'Spotify Streamer' 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.
 * <p/>
 * 'Spotify Streamer' 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.
 * <p/>
 * You should have received a copy of the GNU General Public License
 * along with 'Spotify Streamer'.  If not, see <http://www.gnu.org/licenses/>.
 * ****************************************************************************
 */

package de.gruenewald.udacity.spotifystreamer;

import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import butterknife.ButterKnife;
import butterknife.OnItemClick;
import butterknife.Optional;
import de.gruenewald.udacity.spotifystreamer.controller.AppController;
import de.gruenewald.udacity.spotifystreamer.exception.MissingDependencyException;
import de.gruenewald.udacity.spotifystreamer.exception.ParameterException;
import de.gruenewald.udacity.spotifystreamer.model.ArtistListEntry;
import kaaes.spotify.webapi.android.SpotifyService;
import kaaes.spotify.webapi.android.models.Artist;
import kaaes.spotify.webapi.android.models.ArtistsPager;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;

public class MainActivity extends AppCompatActivity
        implements SearchView.OnQueryTextListener, DialogInterface.OnDismissListener {

    static final String LOG_TAG = MainActivity.class.getSimpleName();
    static final Handler MAIN_THREAD = new Handler(Looper.getMainLooper());

    private SearchView mSearchView;

    private ArtistFragment mArtistFragment;
    private TrackFragment mTrackFragment;
    private FrameLayout mTrackContainer;

    private MenuItem mSearchMenuItem;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //constrain orientation to portrait on smartphones
        if (getResources().getBoolean(R.bool.portrait_only)) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }

        setContentView(R.layout.activity_main);

        mArtistFragment = (ArtistFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_artist);

        if (findViewById(R.id.fragment_track_container) instanceof FrameLayout) {
            mTrackContainer = (FrameLayout) findViewById(R.id.fragment_track_container);
        }

        mTrackFragment = null;
        //if savedInstanceState != null we are re-creating a previously existing MainActivity; thus
        //there should be a TrackFragment already.
        if (savedInstanceState != null && mTrackContainer != null) {
            mTrackFragment = (TrackFragment) getSupportFragmentManager()
                    .findFragmentByTag(TrackActivity.TRACK_FRAGMENT_TAG);
        } else if (mTrackContainer != null) {
            mTrackFragment = new TrackFragment();
            getSupportFragmentManager().beginTransaction()
                    .replace(mTrackContainer.getId(), mTrackFragment, TrackActivity.TRACK_FRAGMENT_TAG).commit();
        }

        AppController.getInstance().registerMainActivity(this);
        AppController.getInstance().registerArtistFragment(mArtistFragment);
        AppController.getInstance().registerTrackFragment(mTrackFragment);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        ButterKnife.inject(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        AppController.getInstance().unregisterMainActivity();
    }

    @OnItemClick(R.id.artist_fragment_listview)
    public void onArtistItemClicked(int pArtistPosition) {
        if (mArtistFragment != null) {
            mArtistFragment.setArtlistPosition(pArtistPosition);
        }

        //TODO: Create visual error-feedback for the user
        try {
            AppController.getInstance().handleOnArtistSelected(pArtistPosition, (mTrackFragment != null));
        } catch (MissingDependencyException e) {
            Log.e(LOG_TAG, e.getMessage());
        } catch (ParameterException e) {
            Log.e(LOG_TAG, e.getMessage());
        }
    }

    @Optional
    @OnItemClick(R.id.track_fragment_listview)
    public void onTrackItemClicked(int pTrackPosition) {
        if (mTrackFragment != null) {
            mTrackFragment.setTrackListPosition(pTrackPosition);
        }
        //TODO: Create visual error-feedback for the user
        try {
            AppController.getInstance().handleOnTrackSelected(pTrackPosition, true);
        } catch (MissingDependencyException e) {
            Log.e(LOG_TAG, e.getMessage());
        } catch (ParameterException e) {
            Log.e(LOG_TAG, e.getMessage());
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);

        // setup the searchview-item and set the querylistener to be this activity
        mSearchMenuItem = menu.findItem(R.id.artist_search_actionbar_search);
        if (mSearchMenuItem != null) {
            mSearchView = (SearchView) MenuItemCompat.getActionView(mSearchMenuItem);
            mSearchView.setOnQueryTextListener(this);
        }

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * This callback method is used by the {@link #mSearchView} and called when
     * the search-query is submitted (by enter button)
     *
     * @param query The text entered in searview
     * @return Returns true if this method is the final handler for the query else false
     */
    @Override
    public boolean onQueryTextSubmit(final String query) {

        final SpotifyService mySpotifyService = AppController.SPOTIFY_API.getService();
        Map<String, Object> queryMap = new HashMap<String, Object>();
        // TODO: Read the limit from the settings
        queryMap.put("limit", 20);

        final MainActivity ref = this;
        mySpotifyService.searchArtists(query, queryMap, new Callback<ArtistsPager>() {
            /**
             * Callback Method used be retrofit when the REST-Request completes.
             *
             * @param t        The result-object containing the artists information
             * @param response The raw response object.
             */
            @Override
            public void success(final ArtistsPager t, final Response response) {
                // translate the artistlist from REST-Request into ArtistListEntry objects.
                final ArrayList<ArtistListEntry> myList = new ArrayList<ArtistListEntry>();
                for (Artist myArtist : t.artists.items) {
                    ArtistListEntry myNewEntry = new ArtistListEntry(myArtist.id);
                    myNewEntry.setArtistName(myArtist.name);
                    // seems the last image in the list is the smallest; so pick that.
                    if (myArtist.images.size() > 0) {
                        myNewEntry.setCoverUrl(myArtist.images.get(myArtist.images.size() - 1).url);
                        myNewEntry.setCoverUrlLarge(myArtist.images.get(0).url);
                    }
                    myList.add(myNewEntry);
                }

                // Seems when this method is called by retrofit we are not
                // on the MAIN_THREAD. Since we want to manipulate the UI we have
                // to post to the MAIN_THREAD (I guess runOnUiThread would also do the job but this way
                // should be preferred for more flexibility).
                MAIN_THREAD.post(new Runnable() {
                    @Override
                    public void run() {
                        //on successful search collapse the actionview
                        MenuItemCompat.collapseActionView(mSearchMenuItem);
                        // Handling the corner case: no artist found.
                        if (t.artists.total == 0) {
                            Toast.makeText(ref, R.string.artist_search_error_noresults, Toast.LENGTH_SHORT).show();
                        } else {
                            mArtistFragment.repopulateListView(myList);
                        }
                    }
                });
            }

            /**
             * Callback-Method used by retrofit to handle errors on failed
             * REST-Requests.
             *
             * @param error The error object.
             */
            @Override
            public void failure(final RetrofitError error) {
                // See success()-method why we post to MAIN_THREAD.
                MAIN_THREAD.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.e(LOG_TAG, "RetrofitError: " + error.getMessage());
                        Toast.makeText(ref, R.string.artist_search_error_default, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });

        // close the keyboard after search by taking focus from the searchview
        mSearchView.clearFocus();

        return true;
    }

    /**
     * Currently unused. Callback method used by {@link #mSearchView} which is called
     * as soon as the input in the searchview changes.
     * #
     *
     * @param newText The new text-value.
     * @return true if this method is the final handler else false.
     */
    @Override
    public boolean onQueryTextChange(String newText) {
        return false;
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        AppController.getInstance().unregisterPlaybackFragment();
        getSupportFragmentManager().popBackStack();
    }
}