diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..41cee2f --- /dev/null +++ b/README.txt @@ -0,0 +1 @@ +Api key must be added unter app/src/main/res/values/api_key.xml \ No newline at end of file diff --git a/app/src/main/java/net/headlezz/udacityproject1/MainActivity.java b/app/src/main/java/net/headlezz/udacityproject1/MainActivity.java index 3382364..726c58d 100644 --- a/app/src/main/java/net/headlezz/udacityproject1/MainActivity.java +++ b/app/src/main/java/net/headlezz/udacityproject1/MainActivity.java @@ -1,10 +1,16 @@ package net.headlezz.udacityproject1; import android.os.Bundle; +import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; -public class MainActivity extends AppCompatActivity { +import net.headlezz.udacityproject1.tmdbapi.Movie; + +/** + * Main activity is mainly for handling fragment transactions + */ +public class MainActivity extends AppCompatActivity implements MovieNavigation, FragmentManager.OnBackStackChangedListener { @Override protected void onCreate(Bundle savedInstanceState) { @@ -15,11 +21,41 @@ public class MainActivity extends AppCompatActivity { setTitle(getString(R.string.app_name)); if(savedInstanceState == null) { - getSupportFragmentManager() - .beginTransaction() + getSupportFragmentManager().beginTransaction() .add(R.id.fragmentHolder, new MovieListFragment()) .commit(); } + + // activity needs to listen to fragment changes to adjust the home as up arrow + getSupportFragmentManager().addOnBackStackChangedListener(this); + shouldDisplayHomeUp(); } + /** + * Replaces the list with a details view showing the selected movie + * @param movie Movie to show + */ + public void showDetailsFragment(Movie movie) { + MovieDetailsFragment frag = MovieDetailsFragment.newInstance(movie); + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragmentHolder, frag) + .addToBackStack(MovieDetailsFragment.TAG) + .commit(); + } + + public void shouldDisplayHomeUp(){ + boolean hasBackstack = getSupportFragmentManager().getBackStackEntryCount()>0; + getSupportActionBar().setDisplayHomeAsUpEnabled(hasBackstack); + } + + @Override + public boolean onSupportNavigateUp() { + getSupportFragmentManager().popBackStack(); + return true; + } + + @Override + public void onBackStackChanged() { + shouldDisplayHomeUp(); + } } diff --git a/app/src/main/java/net/headlezz/udacityproject1/MovieDetailsFragment.java b/app/src/main/java/net/headlezz/udacityproject1/MovieDetailsFragment.java new file mode 100644 index 0000000..12b74c8 --- /dev/null +++ b/app/src/main/java/net/headlezz/udacityproject1/MovieDetailsFragment.java @@ -0,0 +1,75 @@ +package net.headlezz.udacityproject1; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.text.format.DateUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import net.headlezz.udacityproject1.tmdbapi.Movie; +import net.headlezz.udacityproject1.tmdbapi.TMDBApi; + +/** + * Fragment to show the details of a movie + */ +public class MovieDetailsFragment extends Fragment { + + // TODO make me pretty + + public static final String TAG = MovieDetailsFragment.class.getSimpleName(); + public static final String BUNDLE_ARG_MOVIE = "movie"; + + ImageView ivPoster; + TextView tvTitle; + TextView tvOverview; + TextView tvReleaseDate; + TextView tvRating; + + Movie mMovie; + + public static MovieDetailsFragment newInstance(Movie movie) { + MovieDetailsFragment frag = new MovieDetailsFragment(); + Bundle args = new Bundle(); + args.putParcelable(BUNDLE_ARG_MOVIE, movie); + frag.setArguments(args); + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if(getArguments().containsKey(BUNDLE_ARG_MOVIE)) { + mMovie = getArguments().getParcelable(BUNDLE_ARG_MOVIE); + Log.d(TAG, "Bundled movie: " + mMovie.getTitle()); + } else + throw new RuntimeException(TAG + " opened without bundled movie!"); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_movie_details, container, false); + ivPoster = (ImageView) view.findViewById(R.id.movie_details_ivPoster); + tvTitle = (TextView) view.findViewById(R.id.movie_details_tvTitle); + tvOverview = (TextView) view.findViewById(R.id.movie_details_tvOverview); + tvReleaseDate = (TextView) view.findViewById(R.id.movie_details_tvReleaseDate); + tvRating = (TextView) view.findViewById(R.id.movie_details_tvRating); + return view; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + TMDBApi.loadPoster(ivPoster, mMovie); + tvTitle.setText(mMovie.getTitle()); + tvOverview.setText(mMovie.getOverview()); + String formatedDate = DateUtils.formatDateTime(tvReleaseDate.getContext(), mMovie.getReleaseDate(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR); + tvReleaseDate.setText(formatedDate); + tvRating.setText(String.valueOf(mMovie.getAvRating())); + } +} diff --git a/app/src/main/java/net/headlezz/udacityproject1/MovieListAdapter.java b/app/src/main/java/net/headlezz/udacityproject1/MovieListAdapter.java index 185604e..8c64c7c 100644 --- a/app/src/main/java/net/headlezz/udacityproject1/MovieListAdapter.java +++ b/app/src/main/java/net/headlezz/udacityproject1/MovieListAdapter.java @@ -1,12 +1,10 @@ package net.headlezz.udacityproject1; -import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import android.widget.Toast; import net.headlezz.udacityproject1.tmdbapi.Movie; import net.headlezz.udacityproject1.tmdbapi.TMDBApi; @@ -15,11 +13,11 @@ import java.util.List; public class MovieListAdapter extends RecyclerView.Adapter{ - Context context; + MovieNavigation mMovieNavigation; List mMovies; - public MovieListAdapter(Context context, List movies) { - this.context = context; + public MovieListAdapter(MovieNavigation mn, List movies) { + mMovieNavigation = mn; mMovies = movies; } @@ -53,7 +51,7 @@ public class MovieListAdapter extends RecyclerView.Adapter movies) { - mMovieGridView.setAdapter(new MovieListAdapter(getActivity(), movies)); + mMovieGridView.setAdapter(new MovieListAdapter((MovieNavigation) getActivity(), movies)); } /** @@ -138,4 +140,11 @@ public class MovieListFragment extends Fragment { super.onStop(); stopLoadingMovies(); } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if(!(context instanceof MovieNavigation)) + throw new RuntimeException("Activity must implement " + MovieNavigation.class.getSimpleName()); + } } diff --git a/app/src/main/java/net/headlezz/udacityproject1/MovieNavigation.java b/app/src/main/java/net/headlezz/udacityproject1/MovieNavigation.java new file mode 100644 index 0000000..f47d954 --- /dev/null +++ b/app/src/main/java/net/headlezz/udacityproject1/MovieNavigation.java @@ -0,0 +1,9 @@ +package net.headlezz.udacityproject1; + +import net.headlezz.udacityproject1.tmdbapi.Movie; + +public interface MovieNavigation { + + void showDetailsFragment(Movie movie); + +} diff --git a/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/Movie.java b/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/Movie.java index 6861114..8a7da7f 100644 --- a/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/Movie.java +++ b/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/Movie.java @@ -1,15 +1,22 @@ package net.headlezz.udacityproject1.tmdbapi; +import android.os.Parcel; +import android.os.Parcelable; + /** * Data object to represent a single movie * Only required data from the api is stored to save memory */ -public class Movie { +public class Movie implements Parcelable { + + // empty constructor for gson/retrofit + public Movie() {} String title; String posterPath; String overview; double avRating; + long releaseDate; public String getTitle() { return title; @@ -26,4 +33,44 @@ public class Movie { public double getAvRating() { return avRating; } + + public long getReleaseDate() { + return releaseDate; + } + + /**** Parcelable stuff *****/ + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(title); + dest.writeString(posterPath); + dest.writeString(overview); + dest.writeDouble(avRating); + dest.writeLong(releaseDate); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Movie createFromParcel(Parcel src) { + return new Movie(src); + } + + public Movie[] newArray(int size) { + return new Movie[size]; + } + }; + + private Movie(Parcel src) { + title = src.readString(); + posterPath = src.readString(); + overview = src.readString(); + avRating = src.readDouble(); + releaseDate = src.readLong(); + } + + } diff --git a/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/MovieListDeserializer.java b/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/MovieListDeserializer.java index 1cef1a0..0f7be80 100644 --- a/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/MovieListDeserializer.java +++ b/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/MovieListDeserializer.java @@ -9,6 +9,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import java.lang.reflect.Type; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @@ -17,6 +18,7 @@ public class MovieListDeserializer implements JsonDeserializer { @Override public MovieList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { List movieList = new ArrayList<>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD"); for(JsonElement elem : json.getAsJsonObject().getAsJsonArray("results")) { try { // TODO fix this and remove try/catch JsonObject jsonObj = elem.getAsJsonObject(); @@ -24,8 +26,8 @@ public class MovieListDeserializer implements JsonDeserializer { m.title = jsonObj.get("title").getAsString(); m.overview = jsonObj.get("overview").getAsString(); m.avRating = jsonObj.get("vote_average").getAsDouble(); - JsonElement posterPath = jsonObj.get("poster_path"); - m.posterPath = posterPath.getAsString(); + m.posterPath = jsonObj.get("poster_path").getAsString(); + m.releaseDate = sdf.parse(jsonObj.get("release_date").getAsString()).getTime(); movieList.add(m); } catch (Exception e) { Log.e(MovieListDeserializer.class.getSimpleName(), e.toString()); diff --git a/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/TMDBApi.java b/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/TMDBApi.java index 9f6ddbd..4ffb010 100644 --- a/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/TMDBApi.java +++ b/app/src/main/java/net/headlezz/udacityproject1/tmdbapi/TMDBApi.java @@ -20,7 +20,7 @@ import retrofit.http.Query; public class TMDBApi { private static final String BASE_URL = "http://api.themoviedb.org/3/"; - private static final String IMAGE_BASE_URL = "http://image.tmdb.org/t/p/w342"; // TODO select size depending on device screen ratios + private static final String IMAGE_BASE_URL = "http://image.tmdb.org/t/p/w185"; // TODO select size depending on device screen ratios /** * These are the possible sorting orders diff --git a/app/src/main/res/drawable/chappie_ver4.jpg b/app/src/main/res/drawable/chappie_ver4.jpg new file mode 100644 index 0000000..f8166d9 Binary files /dev/null and b/app/src/main/res/drawable/chappie_ver4.jpg differ diff --git a/app/src/main/res/layout/fragment_movie_details.xml b/app/src/main/res/layout/fragment_movie_details.xml new file mode 100644 index 0000000..5002aef --- /dev/null +++ b/app/src/main/res/layout/fragment_movie_details.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file