diff --git a/app/build.gradle b/app/build.gradle
index a3525fe..0bf42a8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 22
+ compileSdkVersion 23
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "za.co.ubiquitech.vmovies"
- minSdkVersion 17
- targetSdkVersion 22
+ minSdkVersion 19
+ targetSdkVersion 23
versionCode 1
versionName "1.0"
}
@@ -24,8 +24,14 @@ repositories {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:22.2.1'
+
+ compile 'com.android.support:appcompat-v7:23.0.1'
+ compile 'com.inthecheesefactory.thecheeselibrary:stated-fragment-support-v4:0.9.3'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
-
+ compile 'com.android.support:design:23.0.1'
+ compile 'com.android.support:appcompat-v7:23.0.1'
+ compile 'com.android.support:support-v4:23.0.1'
+ compile 'com.android.support:cardview-v7:23.0.1'
+ compile 'com.facebook.stetho:stetho:1.2.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 52dcb84..1d695a5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,32 +1,43 @@
+ package="za.co.ubiquitech.vmovies" >
+
+
+ android:theme="@style/AppTheme" >
+ android:label="@string/app_name"
+ android:launchMode="singleTop"
+ android:noHistory="false">
+ android:name=".DetailActivity"
+ android:parentActivityName=".MainActivity"
+ android:label="Review"
+ android:launchMode="singleTop"
+ android:noHistory="false">
-
-
+
+
diff --git a/app/src/main/ic_calendar-web.png b/app/src/main/ic_calendar-web.png
new file mode 100644
index 0000000..43382a7
Binary files /dev/null and b/app/src/main/ic_calendar-web.png differ
diff --git a/app/src/main/ic_calender-web.png b/app/src/main/ic_calender-web.png
new file mode 100644
index 0000000..683bd88
Binary files /dev/null and b/app/src/main/ic_calender-web.png differ
diff --git a/app/src/main/ic_star-web.png b/app/src/main/ic_star-web.png
new file mode 100644
index 0000000..f84652f
Binary files /dev/null and b/app/src/main/ic_star-web.png differ
diff --git a/app/src/main/ic_white_star-web.png b/app/src/main/ic_white_star-web.png
new file mode 100644
index 0000000..a3f4e13
Binary files /dev/null and b/app/src/main/ic_white_star-web.png differ
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/DetailActivity.java b/app/src/main/java/za/co/ubiquitech/vmovies/DetailActivity.java
new file mode 100644
index 0000000..dc50c9f
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/DetailActivity.java
@@ -0,0 +1,21 @@
+package za.co.ubiquitech.vmovies;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+public class DetailActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_movie_detail);
+ if (savedInstanceState == null) {
+ Bundle arguments = new Bundle();
+ arguments.putParcelable(DetailFragment.DETAIL_MOVIE, getIntent().getParcelableExtra(DetailFragment.DETAIL_MOVIE));
+ DetailFragment fragment = new DetailFragment();
+ fragment.setArguments(arguments);
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.movie_detail_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/DetailFragment.java b/app/src/main/java/za/co/ubiquitech/vmovies/DetailFragment.java
new file mode 100644
index 0000000..6dc7204
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/DetailFragment.java
@@ -0,0 +1,562 @@
+package za.co.ubiquitech.vmovies;
+
+import android.app.ProgressDialog;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.widget.ShareActionProvider;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+
+import za.co.ubiquitech.vmovies.database.MoviesContract;
+import za.co.ubiquitech.vmovies.formObjects.MovieDetailsForm;
+import za.co.ubiquitech.vmovies.formObjects.MovieReviews;
+import za.co.ubiquitech.vmovies.formObjects.Review;
+import za.co.ubiquitech.vmovies.util.CustomListView;
+import za.co.ubiquitech.vmovies.util.CustomTrailerViewAdapter;
+import za.co.ubiquitech.vmovies.util.JsonRequests;
+
+/**
+ * A placeholder fragment containing a simple view.
+ */
+public class DetailFragment extends Fragment implements LoaderManager.LoaderCallbacks {
+ private final String LOG_TAG = DetailFragment.class.getSimpleName();
+ private CustomTrailerViewAdapter mTrailerAdapter;
+ private MovieDetailsForm selectedMovie;
+ private MovieReviews currentMovieReview;
+ private CustomListView customTrailerListView;
+ private int mPosition;
+ private Uri mUri;
+ private static final int CURSOR_LOADER = 1;
+ static final String DETAIL_MOVIE = "SELECTED_MOVIE";
+
+ private ShareActionProvider mShareActionProvider;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.menu_movie_detail, menu);
+
+ MenuItem item = menu.findItem(R.id.share_trailer);
+ mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
+ ;
+
+ /** Setting a share intent */
+ mShareActionProvider.setShareIntent(getDefaultShareIntent());
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ /**
+ * Returns a share intent
+ */
+ private Intent getDefaultShareIntent() {
+ String trailerMessage;
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ trailerMessage = "Vmovies: No movie Trailer";
+ sendIntent.putExtra(Intent.EXTRA_TEXT, trailerMessage);
+ sendIntent.setType("text/plain");
+ return sendIntent;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ //getLoaderManager().initLoader(CURSOR_LOADER, null, DetailFragment.this);
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ View rootView = inflater.inflate(R.layout.fragment_movie_detail, container, false);
+ CoordinatorLayout detailLayout = (CoordinatorLayout) rootView.findViewById(R.id.detail_layout);
+
+ Bundle arguments = getArguments();
+ if (arguments != null) {
+ selectedMovie = arguments.getParcelable(DetailFragment.DETAIL_MOVIE);
+ customTrailerListView = (CustomListView) rootView.findViewById(R.id.trailer_list_view);
+ ((TextView) rootView.findViewById(R.id.detail_movie_name)).setText(selectedMovie.getMovieName());
+ ((TextView) rootView.findViewById(R.id.detail_movie_plot)).setText(selectedMovie.getMoviePlot());
+ ((TextView) rootView.findViewById(R.id.release_date_view)).setText(selectedMovie.getMovieReleaseDate());
+ ((TextView) rootView.findViewById(R.id.rating_view)).setText(selectedMovie.getMovieRating() + "/10");
+
+ ImageView moviePoster = (ImageView) rootView.findViewById(R.id.detail_poster_image);
+ Picasso.with(getActivity())
+ .load(selectedMovie.getMovieBackdrop())
+ .into(moviePoster);
+
+ this.mPosition = Integer.parseInt(selectedMovie.getMovieId());
+ this.mUri = ContentUris.withAppendedId(MoviesContract.ReviewEntry.CONTENT_URI,
+ mPosition);
+
+ if (savedInstanceState != null) {
+ currentMovieReview = savedInstanceState.getParcelable("reviews");
+ mTrailerAdapter = new CustomTrailerViewAdapter(getActivity(), currentMovieReview.getMovieYouTubeURL());
+ customTrailerListView.setAdapter(mTrailerAdapter);
+ customTrailerListView.setExpanded(true);
+ } else {
+ getLoaderManager().restartLoader(CURSOR_LOADER, null, DetailFragment.this);
+ }
+
+ Button btn_review = (Button) rootView.findViewById(R.id.btn_reviews);
+ btn_review.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+
+ if (currentMovieReview.getReviews() != null && currentMovieReview.getReviews().size() > 0) {
+ Bundle args = new Bundle();
+ args.putParcelable("review", currentMovieReview);
+ ReviewFragment movieReviewsFragment = new ReviewFragment();
+ movieReviewsFragment.setArguments(args);
+ android.support.v4.app.FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.movie_detail_container, movieReviewsFragment);
+ transaction.addToBackStack(null);
+ transaction.commit();
+ } else {
+ showError("No reviews found for this movie");
+ }
+ }
+ });
+
+ customTrailerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ //http://intransitione.com/blog/play-a-video-on-youtube-using-an-intent/
+ String movieKey = mTrailerAdapter.getItem(position);
+ Uri uri = Uri.parse(movieKey);
+ Intent youTubeIntent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(youTubeIntent);
+ }
+ });
+ }
+
+ if (selectedMovie == null) {
+ detailLayout.setVisibility(View.GONE);
+ } else {
+ detailLayout.setVisibility(View.VISIBLE);
+ }
+
+ FloatingActionButton favButton = (FloatingActionButton) rootView.findViewById(R.id.btn_fav_submit);
+ favButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ safeImages();
+ }
+ });
+
+ return rootView;
+ }
+
+ private void showError(String message) {
+ //Display error
+ final Snackbar snackBar = Snackbar.make(getView(), message, Snackbar.LENGTH_LONG);
+ snackBar.setAction("Dismiss", new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ snackBar.dismiss();
+ }
+ });
+ snackBar.show();
+ }
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ return new CursorLoader(getActivity(),
+ mUri,
+ null,
+ null,
+ null,
+ null);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ }
+
+ // Set the cursor in our CursorAdapter once the Cursor is loaded
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data) {
+
+ int count = 0;
+ int authorIndex = data.getColumnIndex(MoviesContract.ReviewEntry.COLUMN_AUTHOR);
+ int contentIndex = data.getColumnIndex(MoviesContract.ReviewEntry.COLUMN_CONTENT);
+ int movieId = data.getColumnIndex(MoviesContract.ReviewEntry.COLUMN_MOVIE_ID);
+
+ Review[] reviews = new Review[data.getCount()];
+ if (data.getCount() > 0) {//check if cursor not empty
+ data.moveToFirst();
+ do {
+ reviews[count] = new Review(data.getString(authorIndex),
+ data.getString(contentIndex),
+ data.getString(movieId));
+ count++;
+ data.moveToNext();
+ } while (!data.isAfterLast());
+ }
+
+ currentMovieReview = new MovieReviews();
+ if (data.getCount() > 0) {
+ currentMovieReview.setReviews(Arrays.asList(reviews));
+ } else {
+ updateReviews();
+ }
+ }
+
+ // reset CursorAdapter on Loader Reset
+ @Override
+ public void onLoaderReset(Loader loader) {
+ }
+
+ private void safeImages() {
+ Picasso.with(getActivity()).load(selectedMovie.getMoviePoster()).into(target);
+ Picasso.with(getActivity()).load(selectedMovie.getMovieBackdrop()).into(target2);
+
+ ///Movie poster file name
+ String filename = "poster_" + selectedMovie.getMovieId() + ".jpg";
+ File fileDir = new File(getAppStorageDir());
+ String mFilePath = fileDir.toString();
+ File file = new File(mFilePath, filename);
+ selectedMovie.setMoviePoster("file://" + file.toString());
+
+ ///Movie backdrop file name
+ filename = "backdrop_" + selectedMovie.getMovieId() + ".jpg";
+ fileDir = new File(getAppStorageDir());
+ mFilePath = fileDir.toString();
+ file = new File(mFilePath, filename);
+ selectedMovie.setMovieBackdrop("file://" + file.toString());
+ insertData(selectedMovie);
+ }
+
+ private Target target = new Target() {
+ @Override
+ public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ File file = null;
+ try {
+ String filename = "poster_" + selectedMovie.getMovieId() + ".jpg";
+
+ File fileDir = new File(getAppStorageDir());
+ String mFilePath = fileDir.toString();
+
+ File dir = new File(mFilePath);
+ if (!dir.mkdirs()) {
+ Log.e(LOG_TAG, "Directory not created");
+ }
+ file = new File(mFilePath, filename);
+ if (file.exists())
+ file.delete();
+ file.createNewFile();
+
+ FileOutputStream ostream = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream);
+ ostream.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+
+ @Override
+ public void onBitmapFailed(Drawable errorDrawable) {
+ }
+
+ @Override
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
+ if (placeHolderDrawable != null) {
+ }
+ }
+ };
+ private Target target2 = new Target() {
+ @Override
+ public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ File file = null;
+
+ try {
+ String filename = "backdrop_" + selectedMovie.getMovieId() + ".jpg";
+
+ File fileDir = new File(getAppStorageDir());
+ String mFilePath = fileDir.toString();
+
+ File dir = new File(mFilePath);
+ if (!dir.mkdirs()) {
+ Log.e(LOG_TAG, "Directory not created");
+ }
+
+ file = new File(mFilePath, filename);
+ if (file.exists())
+ file.delete();
+ file.createNewFile();
+
+ FileOutputStream ostream = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream);
+ ostream.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+
+ @Override
+ public void onBitmapFailed(Drawable errorDrawable) {
+ }
+
+ @Override
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
+ if (placeHolderDrawable != null) {
+ }
+ }
+ };
+
+ public String getAppStorageDir() {
+ // Get the directory for the user's public pictures directory.
+ String appName = getActivity().getResources().getString(R.string.app_name);
+ File file = new File(Environment.getExternalStoragePublicDirectory(appName), "Images");
+ if (!file.mkdirs()) {
+ Log.e(LOG_TAG, "Directory not created");
+ }
+ return file.toString();
+ }
+
+ // insert data into database
+ public void insertData(MovieDetailsForm movie) {
+
+ ContentValues movieValuesArr = new ContentValues();
+ movieValuesArr.put(MoviesContract.MovieEntry.COLUMN_ID, movie.getMovieId());
+ movieValuesArr.put(MoviesContract.MovieEntry.COLUMN_NAME, movie.getMovieName());
+ movieValuesArr.put(MoviesContract.MovieEntry.COLUMN_POSTER, movie.getMoviePoster());
+ movieValuesArr.put(MoviesContract.MovieEntry.COLUMN_PLOT, movie.getMoviePlot());
+ movieValuesArr.put(MoviesContract.MovieEntry.COLUMN_BACKDROP, movie.getMovieBackdrop());
+ movieValuesArr.put(MoviesContract.MovieEntry.COLUMN_RELEASEDATE, movie.getMovieReleaseDate());
+ movieValuesArr.put(MoviesContract.MovieEntry.COLUMN_RATING, movie.getMovieRating());
+
+ // bulkInsert ContentValues array
+ Uri uri = getActivity().getContentResolver().insert(MoviesContract.MovieEntry.CONTENT_URI,
+ movieValuesArr);
+ if (uri == null) {
+ showError(selectedMovie.getMovieName() + " is already in your favourite list");
+
+ } else {
+ writeReviewsToDatabase(currentMovieReview.getReviews());
+ showError(selectedMovie.getMovieName() + " has been added to your favourite list");
+ }
+ }
+
+ private void writeReviewsToDatabase(List reviews) {
+ Vector cVVector = new Vector(reviews.size());
+
+ for (int i = 0; i < reviews.size(); i++) {
+ ContentValues reviewArr = new ContentValues();
+
+ Review movieReviews = reviews.get(i);
+
+ reviewArr.put(MoviesContract.ReviewEntry.COLUMN_MOVIE_ID, movieReviews.getMovieId());
+ reviewArr.put(MoviesContract.ReviewEntry.COLUMN_AUTHOR, movieReviews.getAuthor());
+ reviewArr.put(MoviesContract.ReviewEntry.COLUMN_CONTENT, movieReviews.getContent());
+
+ cVVector.add(reviewArr);
+ }
+ // bulkInsert reviews
+ if (cVVector.size() > 0) {
+ ContentValues[] cvArray = new ContentValues[cVVector.size()];
+ cVVector.toArray(cvArray);
+ getActivity().getContentResolver().bulkInsert(MoviesContract.ReviewEntry.CONTENT_URI,
+ cvArray);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable("reviews", currentMovieReview);
+ }
+
+ private void updateReviews() {
+ FetchReviewTask fetchReviewTask = new FetchReviewTask();
+ fetchReviewTask.execute();
+ }
+
+ public class FetchReviewTask extends AsyncTask {
+ private ProgressDialog progressDialog;
+
+ @Override
+ protected MovieReviews doInBackground(Void... params) {
+ HttpURLConnection urlConnection = null;
+ BufferedReader reader = null;
+ String moviesJsonStr = null;
+ try {
+ final String MOVIES_BASE_URL = "https://api.themoviedb.org/3/movie/";
+ final String apiKey = "/reviews?api_key=" + MainActivity.MOVIE_API_KEY;
+
+ URL url = new URL(MOVIES_BASE_URL + selectedMovie.getMovieId() + apiKey);
+ urlConnection = (HttpURLConnection) url.openConnection();
+
+ urlConnection.setRequestMethod("GET");
+ urlConnection.setConnectTimeout(10 * 1000);
+ urlConnection.connect();
+
+ InputStream inputStream = urlConnection.getInputStream();
+
+ StringBuffer buffer = new StringBuffer();
+
+ if (inputStream == null) {
+ Log.v(LOG_TAG, "Inputstream is null ");
+ return null;
+ }
+
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
+ // But it does make debugging a *lot* easier if you print out the completed
+ // buffer for debugging.
+ buffer.append(line + "\n");
+ }
+ //Stream was emplty. No need to parse
+ if (buffer.length() == 0) {
+ Log.v(LOG_TAG, "Buffer is null ");
+ return null;
+ }
+ moviesJsonStr = buffer.toString();
+
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Error ", e);
+ return null;
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (final IOException e) {
+ Log.e(LOG_TAG, "Error closing stream", e);
+ }
+ }
+ }
+
+ try {
+ return getReviewDataFromJSON(moviesJsonStr);
+ } catch (JSONException e) {
+ Log.d(LOG_TAG, e.getMessage(), e);
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private MovieReviews getReviewDataFromJSON(String movieStr) throws JSONException {
+ String author;
+ String content;
+ List movieYouTubeUrl;
+
+ JSONObject jsonObj = new JSONObject(movieStr);
+
+ //get JSON ARRAY node
+ JSONArray movieResults = jsonObj.getJSONArray("results");
+ List reviewList = new ArrayList<>();
+ //loop through all movie results
+ for (int i = 0; i < movieResults.length(); i++) {
+ JSONObject object = movieResults.getJSONObject(i);
+ author = object.getString("author");
+ content = object.getString("content");
+ reviewList.add(new Review(author, content, selectedMovie.getMovieId()));
+ }
+ movieYouTubeUrl = new JsonRequests().FetchMovieVideoKey(selectedMovie.getMovieId());
+ return new MovieReviews(movieYouTubeUrl, reviewList);
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ progressDialog = new ProgressDialog(getActivity());
+ progressDialog.setIndeterminate(true);
+ progressDialog.setCanceledOnTouchOutside(false);
+ progressDialog.setMessage("Loading Reviews...");
+ progressDialog.show();
+ }
+
+ @Override
+ protected void onPostExecute(MovieReviews result) {
+ progressDialog.dismiss();
+ if (result != null) {
+ currentMovieReview = result;
+ String trailerMessage;
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setType("text/plain");
+ if (!currentMovieReview.getMovieYouTubeURL().isEmpty()) {
+ trailerMessage = "Vmovies: " + selectedMovie.getMovieName() + " " + currentMovieReview.getMovieYouTubeURL().get(0);
+
+ } else {
+ trailerMessage = "Vmovies: " + selectedMovie.getMovieName() + " NO youTube trailer available";
+ }
+ sendIntent.putExtra(Intent.EXTRA_TEXT, trailerMessage);
+ mShareActionProvider.setShareIntent(sendIntent);
+
+ mTrailerAdapter = new CustomTrailerViewAdapter(getActivity(), result.getMovieYouTubeURL());
+ customTrailerListView.setAdapter(mTrailerAdapter);
+ customTrailerListView.setExpanded(true);
+ }
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/MainActivity.java b/app/src/main/java/za/co/ubiquitech/vmovies/MainActivity.java
index ec8fa51..916156c 100644
--- a/app/src/main/java/za/co/ubiquitech/vmovies/MainActivity.java
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/MainActivity.java
@@ -2,19 +2,35 @@
import android.content.Intent;
import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
-import za.co.ubiquitech.vmovies.util.MoviePreferenceActivity;
+import com.facebook.stetho.Stetho;
-public class MainActivity extends AppCompatActivity {
+import za.co.ubiquitech.vmovies.formObjects.MovieDetailsForm;
+
+public class MainActivity extends AppCompatActivity implements MovieFragment.Callback {
public static final String MOVIE_API_KEY = "b3d57ac00feabdc38238f51d1255f024";
+ private boolean mTwoPane = false;
+ private static final String DETAILFRAGMENT_TAG = "DFTAG";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ //http://facebook.github.io/stetho/
+ Stetho.initializeWithDefaults(this);
setContentView(R.layout.activity_main);
+ if (findViewById(R.id.movie_detail_container) != null) {
+ mTwoPane = true;
+ DetailFragment detailFragment = new DetailFragment();
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.movie_detail_container, detailFragment, DETAILFRAGMENT_TAG).commit();
+ } else {
+ mTwoPane = false;
+ getSupportActionBar().setElevation(0f);
+ }
}
@Override
@@ -24,8 +40,6 @@ public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
-
-///testing
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
@@ -33,13 +47,29 @@ public boolean onOptionsItemSelected(MenuItem item) {
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
- //noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- Intent settingIntent = new Intent(this, MoviePreferenceActivity.class);
- startActivity(settingIntent);
- return true;
- }
-
return super.onOptionsItemSelected(item);
}
+
+ @Override
+ public void onItemSelected(MovieDetailsForm selectedMovie) {
+ if (mTwoPane) {
+ // In two-pane mode, show the detail view in this activity by
+ // adding or replacing the detail fragment using a
+ // fragment transaction.
+ Bundle args = new Bundle();
+ args.putParcelable(DetailFragment.DETAIL_MOVIE, selectedMovie);
+
+ DetailFragment fragment = new DetailFragment();
+ fragment.setArguments(args);
+
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.movie_detail_container, fragment, DETAILFRAGMENT_TAG)
+ .commit();
+ } else {
+ Intent intent = new Intent(this, DetailActivity.class);
+ intent.putExtra(DetailFragment.DETAIL_MOVIE, selectedMovie);
+ startActivity(intent);
+ }
+ }
+
}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/MainActivityMoviesViewFragment.java b/app/src/main/java/za/co/ubiquitech/vmovies/MainActivityMoviesViewFragment.java
deleted file mode 100644
index 81da1eb..0000000
--- a/app/src/main/java/za/co/ubiquitech/vmovies/MainActivityMoviesViewFragment.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package za.co.ubiquitech.vmovies;
-
-import android.app.Fragment;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.GridView;
-import android.widget.Toast;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-import za.co.ubiquitech.vmovies.formObjects.MovieDetailsForm;
-import za.co.ubiquitech.vmovies.util.ImageDownloadAdapter;
-
-/**
- * @author vane
- * @since 2015/09/23.
- */
-public class MainActivityMoviesViewFragment extends Fragment {
- private final String LOG_TAG = MainActivityMoviesViewFragment.class.getSimpleName();
- JSONArray movieResults = null;
- private static ProgressDialog progressDialog;
- private ImageDownloadAdapter mMovieAdapter;
- private GridView gridView;
-
- public MainActivityMoviesViewFragment() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- updateMovies();
- setHasOptionsMenu(true);
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.moviefragment, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- int id = item.getItemId();
- if (id == R.id.action_refresh) {
- updateMoveResults();
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void updateMoveResults() {
-
- FetchMoviesTask moviesTask = new FetchMoviesTask();
- moviesTask.execute();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_main_activity_movies_view, container, false);
- gridView = (GridView) rootView.findViewById(R.id.gridview_movies);
-
-
- gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- MovieDetailsForm movieDetails = mMovieAdapter.getItem(position);
- Toast toast = Toast.makeText(getActivity(), movieDetails.getMovieName(), Toast.LENGTH_SHORT);
- toast.show();
-
- Intent intent = new Intent(getActivity(), MovieDetailActivity.class).putExtra("selected_movie", movieDetails);
- startActivity(intent);
- }
- });
-
- return rootView;
- }
-
- private void updateMovies() {
- FetchMoviesTask fetchMoviesTask = new FetchMoviesTask();
- fetchMoviesTask.execute();
- }
-
- public class FetchMoviesTask extends AsyncTask {
-
- @Override
- protected MovieDetailsForm[] doInBackground(String... params) {
- HttpURLConnection urlConnection = null;
- BufferedReader reader = null;
- String moviesJsonStr = null;
- try {
-
- SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(getActivity().getBaseContext());
- String sortBy = SP.getString("sort_by", "");
-
- final String MOVIES_BASE_URL = "http://api.themoviedb.org/3/discover/movie?api_key=" + MainActivity.MOVIE_API_KEY +
- "&" + sortBy;
-
- URL url = new URL(MOVIES_BASE_URL);
-
- urlConnection = (HttpURLConnection) url.openConnection();
-
- urlConnection.setRequestMethod("GET");
- urlConnection.connect();
-
-
- InputStream inpustStream = urlConnection.getInputStream();
-
- StringBuffer buffer = new StringBuffer();
-
- if (inpustStream == null) {
- Log.v(LOG_TAG, "Inputstream is null ");
- return null;
- }
-
- reader = new BufferedReader(new InputStreamReader(inpustStream));
-
- String line;
- while ((line = reader.readLine()) != null) {
- // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
- // But it does make debugging a *lot* easier if you print out the completed
- // buffer for debugging.
- buffer.append(line + "\n");
- }
- //Stream was emplty. No need to parse
- if (buffer.length() == 0) {
- Log.v(LOG_TAG, "Buffer is null ");
- return null;
- }
-
- moviesJsonStr = buffer.toString();
-
- } catch (IOException e) {
- Log.d(LOG_TAG, "Error ", e);
- return null;
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- if (reader != null) {
- try {
- reader.close();
- } catch (final IOException e) {
- Log.e(LOG_TAG, "Error closing stream", e);
- }
- }
- }
-
- try {
- return getMovieDataFromJSON(moviesJsonStr);
- } catch (JSONException e) {
- Log.d(LOG_TAG, e.getMessage(), e);
- e.printStackTrace();
- }
- return null;
- }
-
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- progressDialog = new ProgressDialog(getActivity());
- progressDialog.setIndeterminate(true);
- progressDialog.setMessage("Loading...");
- progressDialog.show();
- }
-
- @Override
- protected void onPostExecute(MovieDetailsForm[] result) {
- progressDialog.dismiss();
- if (result != null) {
- mMovieAdapter = new ImageDownloadAdapter(getActivity(), result);
- gridView.setAdapter(mMovieAdapter);
- }
- }
-
- private MovieDetailsForm[] getMovieDataFromJSON(String movieStr) throws JSONException {
- String moviePoster;
- String movieName;
- String moviePlot;
- Double movieRating;
- String movieRealseDate;
-
- JSONObject jsonObj = new JSONObject(movieStr);
-
- //get JSON ARRAY node
- movieResults = jsonObj.getJSONArray("results");
- MovieDetailsForm[] resultStrs = new MovieDetailsForm[movieResults.length() + 1];
- //loop through all movie results
- for (int i = 0; i < movieResults.length(); i++) {
- JSONObject object = movieResults.getJSONObject(i);
- moviePoster = "http://image.tmdb.org/t/p/w185" + object.getString("poster_path");
- movieName = object.getString("original_title");
- moviePlot = object.getString("overview");
- movieRating = object.getDouble("vote_average");
- movieRealseDate = object.getString("release_date");
- resultStrs[i] = new MovieDetailsForm(moviePoster, movieName, moviePlot, movieRating, movieRealseDate);
- }
-
- for (MovieDetailsForm s : resultStrs) {
- Log.v(LOG_TAG, "Movie Results: " + s);
- }
- return resultStrs;
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/MovieDetailActivity.java b/app/src/main/java/za/co/ubiquitech/vmovies/MovieDetailActivity.java
deleted file mode 100644
index 47d96f5..0000000
--- a/app/src/main/java/za/co/ubiquitech/vmovies/MovieDetailActivity.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package za.co.ubiquitech.vmovies;
-
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.Menu;
-import android.view.MenuItem;
-
-public class MovieDetailActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_movie_detail);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_movie_detail, menu);
- 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);
- }
-}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/MovieDetailActivityFragment.java b/app/src/main/java/za/co/ubiquitech/vmovies/MovieDetailActivityFragment.java
deleted file mode 100644
index 221cbf7..0000000
--- a/app/src/main/java/za/co/ubiquitech/vmovies/MovieDetailActivityFragment.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package za.co.ubiquitech.vmovies;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.squareup.picasso.Picasso;
-
-import za.co.ubiquitech.vmovies.formObjects.MovieDetailsForm;
-
-/**
- * A placeholder fragment containing a simple view.
- */
-public class MovieDetailActivityFragment extends Fragment {
-
- public MovieDetailActivityFragment() {
- }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment_movie_detail, container, false);
- Intent intent = getActivity().getIntent();
-
- if (intent != null && intent.hasExtra("selected_movie")) {
- MovieDetailsForm movieDetailsForm = (MovieDetailsForm) intent.getSerializableExtra("selected_movie");
- ((TextView) rootView.findViewById(R.id.detail_movie_name)).setText(movieDetailsForm.getMovieName());
- ((TextView) rootView.findViewById(R.id.detail_movie_plot)).setText(movieDetailsForm.getMoviePlot());
- ((TextView) rootView.findViewById(R.id.release_date_view)).setText(movieDetailsForm.getMovieRealseDate());
- ((TextView) rootView.findViewById(R.id.rating_view)).setText(Double.toString(movieDetailsForm.getMovieRating()) + "/10");
-
- ImageView moviePoster = (ImageView) rootView.findViewById(R.id.detail_poster_image);
- Picasso.with(getActivity())
- .load(movieDetailsForm.getMoviePoster())
- .into(moviePoster);
- }
- return rootView;
- }
-}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/MovieFragment.java b/app/src/main/java/za/co/ubiquitech/vmovies/MovieFragment.java
new file mode 100644
index 0000000..1ecb392
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/MovieFragment.java
@@ -0,0 +1,394 @@
+package za.co.ubiquitech.vmovies;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.Toast;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import za.co.ubiquitech.vmovies.database.MoviesContract;
+import za.co.ubiquitech.vmovies.formObjects.MovieDetailsForm;
+import za.co.ubiquitech.vmovies.util.ImageDownloadAdapter;
+
+/**
+ * @author vane
+ * @since 2015/09/23.
+ */
+public class MovieFragment extends Fragment implements LoaderManager.LoaderCallbacks {
+ private final String LOG_TAG = MovieFragment.class.getSimpleName();
+ JSONArray movieResults = null;
+ private ImageDownloadAdapter mMovieAdapter;
+ private GridView gridView;
+
+ private static final int CURSOR_LOADER = 0;
+
+
+ /**
+ * A callback interface that all activities containing this fragment must
+ * implement. This mechanism allows activities to be notified of item
+ * selections.
+ */
+ public interface Callback {
+ /**
+ * DetailFragmentCallback for when an item has been selected.
+ */
+ public void onItemSelected(MovieDetailsForm selectedMovie);
+ }
+
+ public MovieFragment() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.moviefragment, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+
+ int id = item.getItemId();
+
+ if (id == R.id.most_popular) {
+ updateMovies("popular");
+ }
+
+ if (id == R.id.top_rated) {
+ updateMovies("top_rated");
+ }
+
+ if (id == R.id.favourite) {
+ updateMovies("favourite");
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_main_activity_movies_view, container, false);
+ gridView = (GridView) rootView.findViewById(R.id.gridview_movies);
+ getActivity().setTitle("Favourite");
+
+ gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ MovieDetailsForm movieDetails = mMovieAdapter.getItem(position);
+ Toast toast = Toast.makeText(getActivity(), movieDetails.getMovieName(), Toast.LENGTH_SHORT);
+ toast.show();
+ ((Callback) getActivity()).onItemSelected(movieDetails);
+ }
+ });
+
+ //Long press gives user option to delete a request
+ gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView> adapterView, View view, final int position, long id) {
+ if (getActivity().getTitle().equals("Favourite")) {
+ AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
+ alertDialog.setMessage("Delete Movie from list");
+ alertDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ MovieDetailsForm movieDetails = mMovieAdapter.getItem(position);
+
+ List deleteFile = new ArrayList<>();
+
+ deleteFile.add(new File(getRealPathFromURI(movieDetails.getMovieBackdrop())));
+ deleteFile.add(new File(getRealPathFromURI(movieDetails.getMoviePoster())));
+ Uri mUri = ContentUris.withAppendedId(MoviesContract.MovieEntry.CONTENT_URI,
+ position + 1);
+ getActivity().getContentResolver().delete(mUri, null, null);
+ mUri = ContentUris.withAppendedId(MoviesContract.ReviewEntry.CONTENT_URI,
+ Long.parseLong(movieDetails.getMovieId()));
+ getActivity().getContentResolver().delete(mUri, null, null);
+ deletePictures((deleteFile));
+ getLoaderManager().restartLoader(CURSOR_LOADER, null, MovieFragment.this);
+ }
+ });
+ alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+ alertDialog.show();
+ }
+ return true;
+ }
+ });
+
+ return rootView;
+ }
+
+ private String getRealPathFromURI(String contentURI) {
+ Uri contentUri = Uri.parse(contentURI);
+ Cursor cursor = getActivity().getContentResolver().query(contentUri, null, null, null, null);
+ if (cursor == null) {
+ return contentUri.getPath();
+ } else {
+ cursor.moveToFirst();
+ int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
+ String path = cursor.getString(index);
+ cursor.close();
+ return path;
+ }
+ }
+
+ //Delete downloaded pictures from phone storage
+ private void deletePictures(List fileList) {
+ File file;
+ for (int i = 0; i < fileList.size(); i++) {
+ file = fileList.get(i);
+ file.delete();
+ }
+ }
+
+ private void updateMovies(String sortBy) {
+
+ FetchMoviesTask fetchMoviesTask = new FetchMoviesTask(getActivity());
+ if (sortBy.equals("popular")) {
+ fetchMoviesTask.execute(sortBy, "Most Popular");
+ } else if (sortBy.equals("top_rated")) {
+ fetchMoviesTask.execute(sortBy, "Top Rated");
+ } else {
+ getActivity().setTitle("Favourite");
+ getLoaderManager().restartLoader(CURSOR_LOADER, null, MovieFragment.this);
+ }
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ getLoaderManager().initLoader(CURSOR_LOADER, null, MovieFragment.this);
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ return new CursorLoader(getActivity(),
+ MoviesContract.MovieEntry.CONTENT_URI,
+ null,
+ null,
+ null,
+ null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data) {
+
+ int count = 0;
+ MovieDetailsForm[] movies = new MovieDetailsForm[data.getCount()];
+ if (data.getCount() > 0) {//check if cursor not empty
+ data.moveToFirst();
+ do {
+ movies[count] = new MovieDetailsForm(
+ data.getString(1),
+ data.getString(2),
+ data.getString(3),
+ data.getString(4),
+ data.getString(5),
+ data.getString(6),
+ data.getString(7));
+ count++;
+ data.moveToNext();
+ } while (!data.isAfterLast());
+ }
+ mMovieAdapter = new ImageDownloadAdapter(getActivity(), movies);
+ gridView.setAdapter(mMovieAdapter);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ }
+
+ public class FetchMoviesTask extends AsyncTask {
+
+ private final Context mContext;
+ private String title;
+ private ProgressDialog progressDialog;
+
+ FetchMoviesTask(Activity activity) {
+ this.mContext = activity;
+ }
+
+ @Override
+ protected MovieDetailsForm[] doInBackground(String... params) {
+ HttpURLConnection urlConnection = null;
+ BufferedReader reader = null;
+ String moviesJsonStr = null;
+ title = params[1];
+
+ try {
+ String sortBy = params[0];
+ final String MOVIES_BASE_URL = "http://api.themoviedb.org/3/movie/" + sortBy + "?api_key=" + MainActivity.MOVIE_API_KEY +
+ "&" + sortBy;
+
+ URL url = new URL(MOVIES_BASE_URL);
+
+ urlConnection = (HttpURLConnection) url.openConnection();
+
+ urlConnection.setRequestMethod("GET");
+ urlConnection.setConnectTimeout(10 * 1000);
+ urlConnection.connect();
+
+
+ InputStream inputStream = urlConnection.getInputStream();
+
+ StringBuffer buffer = new StringBuffer();
+
+ if (inputStream == null) {
+ Log.v(LOG_TAG, "Inputstream is null ");
+ return null;
+ }
+
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
+ // But it does make debugging a *lot* easier if you print out the completed
+ // buffer for debugging.
+ buffer.append(line + "\n");
+ }
+ //Stream was emplty. No need to parse
+ if (buffer.length() == 0) {
+ Log.v(LOG_TAG, "Buffer is null ");
+ return null;
+ }
+
+ moviesJsonStr = buffer.toString();
+
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Error ", e);
+ return null;
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (final IOException e) {
+ Log.e(LOG_TAG, "Error closing stream", e);
+ }
+ }
+ }
+
+ try {
+
+ return getMovieDataFromJSON(moviesJsonStr);
+ } catch (JSONException e) {
+ Log.d(LOG_TAG, e.getMessage(), e);
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ progressDialog = new ProgressDialog(getActivity());
+ progressDialog.setIndeterminate(true);
+ progressDialog.setCanceledOnTouchOutside(false);
+ progressDialog.setMessage("Loading...");
+ progressDialog.show();
+ }
+
+ @Override
+ protected void onPostExecute(MovieDetailsForm[] result) {
+ progressDialog.dismiss();
+ if (result != null) {
+ getActivity().setTitle(title);
+ mMovieAdapter = new ImageDownloadAdapter(getActivity(), result);
+ gridView.setAdapter(mMovieAdapter);
+ } else {
+ showError("Error retrieving movie, please check internet connection");
+ }
+ }
+
+ private MovieDetailsForm[] getMovieDataFromJSON(String movieStr) throws JSONException {
+ String moviePoster;
+ String movieName;
+ String moviePlot;
+ String movieRating;
+ String movieReleaseDate;
+ String movieId;
+ String movieBackdrop;
+
+ JSONObject jsonObj = new JSONObject(movieStr);
+
+ //get JSON ARRAY node
+ movieResults = jsonObj.getJSONArray("results");
+ MovieDetailsForm[] resultStrs = new MovieDetailsForm[movieResults.length() + 1];
+ //loop through all movie results
+ for (int i = 0; i < movieResults.length(); i++) {
+ JSONObject object = movieResults.getJSONObject(i);
+ movieBackdrop = "http://image.tmdb.org/t/p/w500" + object.getString("backdrop_path");
+ moviePoster = "http://image.tmdb.org/t/p/w342" + object.getString("poster_path");
+ movieName = object.getString("original_title");
+ moviePlot = object.getString("overview");
+ movieRating = object.getString("vote_average");
+ movieReleaseDate = object.getString("release_date");
+ movieId = object.getString("id");
+ resultStrs[i] = new MovieDetailsForm(movieId, movieName, moviePoster, movieBackdrop, moviePlot, movieReleaseDate, movieRating);
+ }
+ return resultStrs;
+ }
+
+ }
+
+ private void showError(String message) {
+ //Display error
+ final Snackbar snackBar = Snackbar.make(getView(), message, Snackbar.LENGTH_LONG);
+ snackBar.setAction("Dismiss", new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ snackBar.dismiss();
+ }
+ });
+ snackBar.show();
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/ReviewFragment.java b/app/src/main/java/za/co/ubiquitech/vmovies/ReviewFragment.java
new file mode 100644
index 0000000..8a596d1
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/ReviewFragment.java
@@ -0,0 +1,34 @@
+package za.co.ubiquitech.vmovies;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import za.co.ubiquitech.vmovies.formObjects.MovieReviews;
+import za.co.ubiquitech.vmovies.util.CustomReviewsViewAdapter;
+
+public class ReviewFragment extends Fragment {
+ private final String LOG_TAG = ReviewFragment.class.getSimpleName();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View rootView = inflater.inflate(R.layout.fragment_movie_review, container, false);
+ ListView customReviewsListView = (ListView) rootView.findViewById(R.id.reviews_list_view);
+ Bundle args = getArguments();
+ MovieReviews currentMovieReview = args.getParcelable("review");
+ CustomReviewsViewAdapter mReviewsAdapter = new CustomReviewsViewAdapter(getActivity(), currentMovieReview.getReviews());
+ customReviewsListView.setAdapter(mReviewsAdapter);
+ return rootView;
+ }
+
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesContract.java b/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesContract.java
new file mode 100644
index 0000000..a099957
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesContract.java
@@ -0,0 +1,75 @@
+package za.co.ubiquitech.vmovies.database;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+/**
+ * @author Vane
+ * @since 21/10/2015
+ */
+public class MoviesContract {
+ public static final String CONTENT_AUTHORITY = "za.co.ubiquitech.vmovies";
+
+ public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
+
+ public static final class MovieEntry implements BaseColumns {
+ // Table Name
+ public static final String TABLE_MOVIES = "movies";
+ // Columns
+ public static final String COLUMN_ID = "id";
+ public static final String COLUMN_NAME = "name";
+ public static final String COLUMN_POSTER = "poster";
+ public static final String COLUMN_BACKDROP = "backdrop";
+ public static final String COLUMN_PLOT = "plot";
+ public static final String COLUMN_RELEASEDATE = "release_date";
+ public static final String COLUMN_RATING = "rating";
+
+ //create content uri
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
+ .appendPath(TABLE_MOVIES).build();
+
+ //create cursor for base type directory for multiple enteries
+ public static final String CONTENT_DIR_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
+ + "/" + CONTENT_AUTHORITY + "/" + TABLE_MOVIES;
+
+ // create cursor of base type item for single entry
+ public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
+ + "/" + CONTENT_AUTHORITY + "/" + TABLE_MOVIES;
+
+ //for building URIs on insertion
+ public static Uri buildMovieUri(long id) {
+ return ContentUris.withAppendedId(CONTENT_URI, id);
+ }
+
+ }
+
+ public static final class ReviewEntry implements BaseColumns {
+ // Table Name
+ public static final String TABLE_REVIEWS = "reviews";
+ // Columns
+ public static final String COLUMN_MOVIE_ID = "movie_id";
+ public static final String COLUMN_AUTHOR = "author";
+ public static final String COLUMN_CONTENT = "content";
+
+ //create content uri
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
+ .appendPath(TABLE_REVIEWS).build();
+
+ //create cursor for base type directory for multiple enteries
+ public static final String CONTENT_DIR_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
+ + "/" + CONTENT_AUTHORITY + "/" + TABLE_REVIEWS;
+
+ // create cursor of base type item for single entry
+ public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
+ + "/" + CONTENT_AUTHORITY + "/" + TABLE_REVIEWS;
+
+ //for building URIs on insertion
+ public static Uri buildReviewUri(long id) {
+ return ContentUris.withAppendedId(CONTENT_URI, id);
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesDBHelper.java b/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesDBHelper.java
new file mode 100644
index 0000000..51d4150
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesDBHelper.java
@@ -0,0 +1,72 @@
+package za.co.ubiquitech.vmovies.database;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * @author Vane
+ * @since 21/10/2015
+ */
+public class MoviesDBHelper extends SQLiteOpenHelper {
+ public static final String LOG_TAG = MoviesDBHelper.class.getSimpleName();
+
+ //name and version
+ private static final String DATABASE_NAME = "movies.db";
+ private static final int DATABASE_VERSION = 1; /// TODO: 21/10/2015
+
+ MoviesDBHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+
+ // Create database
+ @Override
+ public void onCreate(SQLiteDatabase sqLiteDatabase) {
+
+ final String SQL_CREATE_MOVIE_TABLE = "CREATE TABLE " +
+ MoviesContract.MovieEntry.TABLE_MOVIES + "(" + MoviesContract.MovieEntry._ID +
+ " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ MoviesContract.MovieEntry.COLUMN_ID + " TEXT NOT NULL, " +
+ MoviesContract.MovieEntry.COLUMN_NAME + " TEXT NOT NULL, " +
+ MoviesContract.MovieEntry.COLUMN_POSTER + " TEXT, " +
+ MoviesContract.MovieEntry.COLUMN_BACKDROP + " TEXT, " +
+ MoviesContract.MovieEntry.COLUMN_PLOT + " TEXT, " +
+ MoviesContract.MovieEntry.COLUMN_RELEASEDATE + " TEXT, " +
+ MoviesContract.MovieEntry.COLUMN_RATING + " TEXT);";
+
+
+ final String SQL_CREATE_REVIEW_TABLE = "CREATE TABLE " +
+ MoviesContract.ReviewEntry.TABLE_REVIEWS + "(" +
+ MoviesContract.ReviewEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ MoviesContract.ReviewEntry.COLUMN_MOVIE_ID + " INTEGER NOT NULL, " +
+ MoviesContract.ReviewEntry.COLUMN_AUTHOR + " TEXT NOT NULL, " +
+ MoviesContract.ReviewEntry.COLUMN_CONTENT + " TEXT NOT NULL, " +
+
+ "FOREIGN KEY (" + MoviesContract.ReviewEntry.COLUMN_MOVIE_ID + ") REFERENCES " +
+ MoviesContract.MovieEntry.TABLE_MOVIES + " (" + MoviesContract.MovieEntry._ID + ") );";
+
+ sqLiteDatabase.execSQL(SQL_CREATE_MOVIE_TABLE);
+ sqLiteDatabase.execSQL(SQL_CREATE_REVIEW_TABLE);
+ }
+
+ // Upgrade database when version is changed.
+ @Override
+ public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
+ Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " +
+ newVersion + ". OLD DATA WILL BE DESTROYED");
+ // Drop the table
+ sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + MoviesContract.MovieEntry.TABLE_MOVIES);
+ sqLiteDatabase.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
+ MoviesContract.MovieEntry.TABLE_MOVIES + "'");
+
+ // Drop the table
+ sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + MoviesContract.ReviewEntry.TABLE_REVIEWS);
+ sqLiteDatabase.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
+ MoviesContract.ReviewEntry.TABLE_REVIEWS + "'");
+
+ // re-create database
+ onCreate(sqLiteDatabase);
+ }
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesProvider.java b/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesProvider.java
new file mode 100644
index 0000000..da35220
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/database/MoviesProvider.java
@@ -0,0 +1,286 @@
+package za.co.ubiquitech.vmovies.database;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+/**
+ * @author Vane
+ * @since 21/10/2015
+ */
+public class MoviesProvider extends ContentProvider {
+ private static final String LOG_TAG = MoviesProvider.class.getSimpleName();
+ private static final UriMatcher sUriMatcher = buildUriMatcher();
+ private MoviesDBHelper mOpenHelper;
+
+ //code for the UriMatcher
+ private static final int MOVIES = 25;
+ private static final int MOVIES_WITH_ID = 50;
+ private static final int REVIEWS = 100;
+ private static final int REVIEW_WITH_ID = 150;
+
+ private static UriMatcher buildUriMatcher() {
+ // Build a UriMatcher by adding a specific code to return based on a match
+ // It's common to use NO_MATCH as the code for this case.
+
+ final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ final String authority = MoviesContract.CONTENT_AUTHORITY;
+
+ // add a code for each type of URI you need
+ matcher.addURI(authority, MoviesContract.MovieEntry.TABLE_MOVIES, MOVIES);
+ //# = number
+ matcher.addURI(authority, MoviesContract.MovieEntry.TABLE_MOVIES + "/#", MOVIES_WITH_ID);
+
+ matcher.addURI(authority, MoviesContract.ReviewEntry.TABLE_REVIEWS, REVIEWS);
+ matcher.addURI(authority, MoviesContract.ReviewEntry.TABLE_REVIEWS + "/#", REVIEW_WITH_ID);
+
+ return matcher;
+ }
+
+
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new MoviesDBHelper(getContext());
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ Cursor retCursor;
+
+ switch (sUriMatcher.match(uri)) {
+ //All Movies Selected
+ case MOVIES: {
+ retCursor = mOpenHelper.getReadableDatabase().query(
+ MoviesContract.MovieEntry.TABLE_MOVIES,
+ projection,
+ selection,
+ selectionArgs,
+ null,
+ null,
+ sortOrder);
+ return retCursor;
+ }
+ case REVIEW_WITH_ID: {
+ retCursor = mOpenHelper.getReadableDatabase().query(
+ MoviesContract.ReviewEntry.TABLE_REVIEWS,
+ projection,
+ MoviesContract.ReviewEntry.COLUMN_MOVIE_ID + " = ?",
+ new String[]{String.valueOf(ContentUris.parseId(uri))},
+ null,
+ null,
+ sortOrder);
+ return retCursor;
+ }
+ // Individual flavor based on Id selected
+ case MOVIES_WITH_ID: {
+ retCursor = mOpenHelper.getReadableDatabase().query(
+ MoviesContract.MovieEntry.TABLE_MOVIES,
+ projection,
+ MoviesContract.MovieEntry._ID + " = ?",
+ new String[]{String.valueOf(ContentUris.parseId(uri))},
+ null,
+ null,
+ sortOrder);
+ return retCursor;
+ }
+ default: {
+ // By default, we assume a bad URI
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+
+ }
+
+ @Nullable
+ @Override
+ public String getType(Uri uri) {
+
+ final int match = sUriMatcher.match(uri);
+
+ switch (match) {
+ case MOVIES: {
+ return MoviesContract.MovieEntry.CONTENT_DIR_TYPE;
+ }
+ case REVIEWS: {
+ return MoviesContract.ReviewEntry.CONTENT_DIR_TYPE;
+ }
+ case MOVIES_WITH_ID: {
+ return MoviesContract.MovieEntry.CONTENT_ITEM_TYPE;
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown Uri: " + uri);
+ }
+ }
+
+ }
+
+ @Nullable
+ @Override
+ public Uri insert(Uri uri, ContentValues contentValues) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ Uri returnUri = null;
+
+ String[] projection = {MoviesContract.MovieEntry.COLUMN_ID};
+ String[] selection = {contentValues.get(MoviesContract.MovieEntry.COLUMN_ID).toString()};
+ switch (sUriMatcher.match(uri)) {
+ case MOVIES: {
+
+ Cursor cursor = mOpenHelper.getReadableDatabase().query(
+ MoviesContract.MovieEntry.TABLE_MOVIES,
+ projection,
+ MoviesContract.MovieEntry.COLUMN_ID + " = ?",
+ selection,
+ null,
+ null,
+ null);
+ if (cursor.moveToFirst()) {
+ break;
+ } else {
+ long _id = db.insert(MoviesContract.MovieEntry.TABLE_MOVIES, null, contentValues);
+ // insert unless it is already contained in the database
+ if (_id > 0) {
+ returnUri = MoviesContract.MovieEntry.buildMovieUri(_id);
+ } else {
+ throw new android.database.SQLException("Failed to insert row into: " + uri);
+ }
+ }
+ break;
+ }
+
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+
+ }
+ }
+ getContext().getContentResolver().notifyChange(uri, null);
+ return returnUri;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final int match = sUriMatcher.match(uri);
+ int numDeleted;
+ switch (match) {
+ case MOVIES:
+ numDeleted = db.delete(
+ MoviesContract.MovieEntry.TABLE_MOVIES, selection, selectionArgs);
+ // reset _ID
+ db.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
+ MoviesContract.MovieEntry.TABLE_MOVIES + "'");
+ break;
+ case MOVIES_WITH_ID:
+ numDeleted = db.delete(MoviesContract.MovieEntry.TABLE_MOVIES,
+ MoviesContract.MovieEntry._ID + " = ?",
+ new String[]{String.valueOf(ContentUris.parseId(uri))});
+ // reset _ID
+ db.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
+ MoviesContract.MovieEntry.TABLE_MOVIES + "'");
+ break;
+ case REVIEW_WITH_ID:
+ numDeleted = db.delete(MoviesContract.ReviewEntry.TABLE_REVIEWS,
+ MoviesContract.ReviewEntry.COLUMN_MOVIE_ID + " = ?",
+ new String[]{String.valueOf(ContentUris.parseId(uri))});
+ // reset _ID
+ db.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" +
+ MoviesContract.ReviewEntry.TABLE_REVIEWS + "'");
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+
+ return numDeleted;
+ }
+
+ @Override
+ public int bulkInsert(Uri uri, ContentValues[] values) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case REVIEWS:
+ // allows for multiple transactions
+ db.beginTransaction();
+
+ // keep track of successful inserts
+ int numInserted = 0;
+ try {
+ for (ContentValues value : values) {
+ if (value == null) {
+ throw new IllegalArgumentException("Cannot have null content values");
+ }
+ long _id = -1;
+ try {
+ _id = db.insertOrThrow(MoviesContract.ReviewEntry.TABLE_REVIEWS,
+ null, value);
+ } catch (SQLiteConstraintException e) {
+ e.printStackTrace();
+ }
+ if (_id != -1) {
+ numInserted++;
+ }
+ }
+ if (numInserted > 0) {
+ // If no errors, declare a successful transaction.
+ // database will not populate if this is not called
+ db.setTransactionSuccessful();
+ }
+ } finally {
+ // all transactions occur at once
+ db.endTransaction();
+ }
+ if (numInserted > 0) {
+ // if there was successful insertion, notify the content resolver that there
+ // was a change
+ getContext().getContentResolver().notifyChange(uri, null);
+ }
+ return numInserted;
+ default:
+ return super.bulkInsert(uri, values);
+ }
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ int numUpdated = 0;
+
+ if (contentValues == null) {
+ throw new IllegalArgumentException("Cannot have null content values");
+ }
+
+ switch (sUriMatcher.match(uri)) {
+ case MOVIES: {
+ numUpdated = db.update(MoviesContract.MovieEntry.TABLE_MOVIES,
+ contentValues,
+ selection,
+ selectionArgs);
+ break;
+ }
+ case MOVIES_WITH_ID: {
+ numUpdated = db.update(MoviesContract.MovieEntry.TABLE_MOVIES,
+ contentValues,
+ MoviesContract.MovieEntry._ID + " = ?",
+ new String[]{String.valueOf(ContentUris.parseId(uri))});
+ break;
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+
+ if (numUpdated > 0) {
+ getContext().getContentResolver().notifyChange(uri, null);
+ }
+
+ return numUpdated;
+ }
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/MovieDetailsForm.java b/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/MovieDetailsForm.java
index 6a19b1b..1ee4e80 100644
--- a/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/MovieDetailsForm.java
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/MovieDetailsForm.java
@@ -1,64 +1,105 @@
package za.co.ubiquitech.vmovies.formObjects;
-import java.io.Serializable;
+import android.os.Parcel;
+import android.os.Parcelable;
/**
* @author vane
* @since 8/21/2015.
*/
-public class MovieDetailsForm implements Serializable {
- String moviePoster;
- String movieName;
- String moviePlot;
- Double movieRating;
- String movieRealseDate;
-
- public MovieDetailsForm(String moviePoster, String movieName, String moviePlot, Double movieRating, String movieRealseDate) {
+public class MovieDetailsForm implements Parcelable {
+ private String moviePoster;
+ private String movieName;
+ private String moviePlot;
+ private String movieRating;
+ private String movieId;
+ private String movieReleaseDate;
+ private String movieBackdrop;
+
+
+ public MovieDetailsForm(String movieId, String movieName, String moviePoster, String movieBackdrop,
+ String moviePlot, String movieReleaseDate, String movieRating) {
+ this.movieBackdrop = movieBackdrop;
this.moviePoster = moviePoster;
this.movieName = movieName;
this.moviePlot = moviePlot;
this.movieRating = movieRating;
- this.movieRealseDate = movieRealseDate;
+ this.movieReleaseDate = movieReleaseDate;
+ this.movieId = movieId;
+
}
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public MovieDetailsForm createFromParcel(Parcel in) {
+ return new MovieDetailsForm(in);
+ }
+
+ @Override
+ public MovieDetailsForm[] newArray(int size) {
+ return new MovieDetailsForm[size];
+ }
+ };
+
public String getMoviePoster() {
return moviePoster;
}
- public void setMoviePoster(String moviePoster) {
- this.moviePoster = moviePoster;
- }
-
public String getMovieName() {
return movieName;
}
- public void setMovieName(String movieName) {
- this.movieName = movieName;
- }
-
public String getMoviePlot() {
return moviePlot;
}
- public void setMoviePlot(String moviePlot) {
- this.moviePlot = moviePlot;
+ public String getMovieRating() {
+ return movieRating;
}
- public Double getMovieRating() {
- return movieRating;
+ public String getMovieId() {
+ return movieId;
}
- public void setMovieRating(Double movieRating) {
- this.movieRating = movieRating;
+ public String getMovieBackdrop() {
+ return movieBackdrop;
+ }
+
+ public String getMovieReleaseDate() {
+ return movieReleaseDate;
+ }
+
+ public void setMovieBackdrop(String movieBackdrop) {
+ this.movieBackdrop = movieBackdrop;
+ }
+
+ public void setMoviePoster(String moviePoster) {
+ this.moviePoster = moviePoster;
}
- public String getMovieRealseDate() {
- return movieRealseDate;
+ @Override
+ public int describeContents() {
+ return 0;
}
- public void setMovieRealseDate(String movieRealseDate) {
- this.movieRealseDate = movieRealseDate;
+ protected MovieDetailsForm(Parcel in) {
+ this.moviePoster = in.readString();
+ this.movieName = in.readString();
+ this.moviePlot = in.readString();
+ this.movieReleaseDate = in.readString();
+ this.movieId = in.readString();
+ this.movieRating = in.readString();
+ this.movieBackdrop = in.readString();
}
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeString(moviePoster);
+ parcel.writeString(movieName);
+ parcel.writeString(moviePlot);
+ parcel.writeString(movieReleaseDate);
+ parcel.writeString(movieId);
+ parcel.writeString(movieRating);
+ parcel.writeString(movieBackdrop);
+ }
}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/MovieReviews.java b/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/MovieReviews.java
new file mode 100644
index 0000000..1cf60d0
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/MovieReviews.java
@@ -0,0 +1,72 @@
+package za.co.ubiquitech.vmovies.formObjects;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Vane
+ * @since 18/10/2015
+ */
+public class MovieReviews implements Parcelable {
+ private List reviews = new ArrayList();
+ ;
+ private List movieYouTubeURL = new ArrayList();
+
+ public MovieReviews(List youTubeURL, List reviews) {
+ this.movieYouTubeURL = youTubeURL;
+ this.reviews = reviews;
+ }
+
+ protected MovieReviews(Parcel in) {
+ this.movieYouTubeURL = new ArrayList();
+ in.readList(movieYouTubeURL, null);
+ this.reviews = new ArrayList();
+ in.readList(reviews, MovieReviews.class.getClassLoader());
+
+ }
+
+ public MovieReviews() {
+ }
+
+ public List getReviews() {
+ return reviews;
+ }
+
+ public void setReviews(List reviews) {
+ this.reviews = reviews;
+ }
+
+ public List getMovieYouTubeURL() {
+ return movieYouTubeURL;
+ }
+
+ public void setMovieYouTubeURL(List movieYouTubeURL) {
+ this.movieYouTubeURL = movieYouTubeURL;
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public MovieReviews createFromParcel(Parcel in) {
+ return new MovieReviews(in);
+ }
+
+ @Override
+ public MovieReviews[] newArray(int size) {
+ return new MovieReviews[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeList(movieYouTubeURL);
+ parcel.writeList(reviews);
+ }
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/Review.java b/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/Review.java
new file mode 100644
index 0000000..7e2690b
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/formObjects/Review.java
@@ -0,0 +1,76 @@
+package za.co.ubiquitech.vmovies.formObjects;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @author Vane
+ * @since 22/10/2015
+ */
+public class Review implements Parcelable {
+
+ private String author;
+ private String content;
+ private String movieId;
+
+ public Review(String author, String content, String movieId) {
+ this.author = author;
+ this.content = content;
+ this.movieId = movieId;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getMovieId() {
+ return movieId;
+ }
+
+ public void setMovieId(String movieId) {
+ this.movieId = movieId;
+ }
+
+ protected Review(Parcel in) {
+ author = in.readString();
+ content = in.readString();
+ movieId = in.readString();
+
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public Review createFromParcel(Parcel in) {
+ return new Review(in);
+ }
+
+ @Override
+ public Review[] newArray(int size) {
+ return new Review[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeString(author);
+ parcel.writeString(content);
+ parcel.writeString(movieId);
+ }
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomListView.java b/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomListView.java
new file mode 100644
index 0000000..b709d0c
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomListView.java
@@ -0,0 +1,56 @@
+package za.co.ubiquitech.vmovies.util;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.GridView;
+import android.widget.ListView;
+
+import org.w3c.dom.Text;
+
+/**
+ * @author Vane
+ * @since 06/10/2015
+ */
+public class CustomListView extends GridView{
+
+boolean expanded = false;
+
+ public CustomListView(Context context) {
+ super(context);
+ }
+
+ public CustomListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CustomListView(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public boolean isExpanded() {
+ return expanded;
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // HACK! TAKE THAT ANDROID!
+ if (isExpanded()) {
+ // Calculate entire height by providing a very large height hint.
+ // View.MEASURED_SIZE_MASK represents the largest height possible.
+ int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK,
+ MeasureSpec.AT_MOST);
+ super.onMeasure(widthMeasureSpec, expandSpec);
+
+ ViewGroup.LayoutParams params = getLayoutParams();
+ params.height = getMeasuredHeight();
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ public void setExpanded(boolean expanded) {
+ this.expanded = expanded;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomReviewsViewAdapter.java b/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomReviewsViewAdapter.java
new file mode 100644
index 0000000..d8b73f1
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomReviewsViewAdapter.java
@@ -0,0 +1,72 @@
+package za.co.ubiquitech.vmovies.util;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import za.co.ubiquitech.vmovies.R;
+import za.co.ubiquitech.vmovies.formObjects.MovieReviews;
+import za.co.ubiquitech.vmovies.formObjects.Review;
+
+/**
+ * @author Vane
+ * @since 18/10/2015
+ */
+public class CustomReviewsViewAdapter extends BaseAdapter {
+ private final Context context;
+ private List movieReviews = new ArrayList();
+
+ public CustomReviewsViewAdapter(Context context, List reviews) {
+ this.context = context;
+ movieReviews = reviews;
+ }
+
+ @Override
+ public int getCount() {
+ return movieReviews.size();
+ }
+
+ @Override
+ public Review getItem(int i) {
+
+ return movieReviews.get(i);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup viewGroup) {
+
+ ViewHolder viewHolder;
+
+ if (convertView == null) {
+ convertView = LayoutInflater.from(context).inflate(R.layout.list_reviews, viewGroup, false);
+ viewHolder = new ViewHolder();
+ viewHolder.authorTextView = (TextView) convertView.findViewById(R.id.list_item_reviews_author_textview);
+ viewHolder.contentTextView = (TextView) convertView.findViewById(R.id.list_item_reviews_content_textview);
+ convertView.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) convertView.getTag();
+ }
+
+ viewHolder.authorTextView.setText(getItem(position).getAuthor());
+ viewHolder.contentTextView.setText(getItem(position).getContent());
+
+ return convertView;
+ }
+
+ static class ViewHolder {
+ TextView authorTextView;
+ TextView contentTextView;
+ }
+
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomTrailerViewAdapter.java b/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomTrailerViewAdapter.java
new file mode 100644
index 0000000..1ef00a5
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/util/CustomTrailerViewAdapter.java
@@ -0,0 +1,72 @@
+package za.co.ubiquitech.vmovies.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import za.co.ubiquitech.vmovies.R;
+import za.co.ubiquitech.vmovies.formObjects.MovieDetailsForm;
+import za.co.ubiquitech.vmovies.formObjects.MovieReviews;
+
+/**
+ * @author Vane
+ * @since 17/10/2015
+ */
+public class CustomTrailerViewAdapter extends BaseAdapter {
+
+ private final Context context;
+ private List youTubeKeys = new ArrayList();
+
+ public CustomTrailerViewAdapter(Context context,List videoKey) {
+ this.context = context;
+ this.youTubeKeys=videoKey;
+ }
+ @Override
+ public int getCount() {
+ return youTubeKeys.size();
+ }
+
+ @Override
+ public String getItem(int i) {
+ return youTubeKeys.get(i);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ String trailer="Trailer ";
+ ViewHolder viewHolder;
+
+ if(convertView ==null){
+ convertView = LayoutInflater.from(context).inflate(R.layout.list_trailer, parent, false);
+ viewHolder = new ViewHolder();
+ viewHolder.trailerTextView = (TextView) convertView.findViewById(R.id.textview_trailer);
+ convertView.setTag(viewHolder);
+
+ }else {
+ viewHolder = (ViewHolder) convertView.getTag();
+ }
+
+ viewHolder.trailerTextView.setText(trailer + (position + 1));
+ return convertView;
+ }
+
+ static class ViewHolder {
+ TextView trailerTextView;
+ }
+}
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/util/ImageDownloadAdapter.java b/app/src/main/java/za/co/ubiquitech/vmovies/util/ImageDownloadAdapter.java
index 223103d..028afb7 100644
--- a/app/src/main/java/za/co/ubiquitech/vmovies/util/ImageDownloadAdapter.java
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/util/ImageDownloadAdapter.java
@@ -35,7 +35,7 @@ public ImageDownloadAdapter(Context context, MovieDetailsForm[] movieDetailForms
@Override
public int getCount() {
- return movies.size() - 1;
+ return movies.size();
}
@Override
diff --git a/app/src/main/java/za/co/ubiquitech/vmovies/util/JsonRequests.java b/app/src/main/java/za/co/ubiquitech/vmovies/util/JsonRequests.java
new file mode 100644
index 0000000..7200005
--- /dev/null
+++ b/app/src/main/java/za/co/ubiquitech/vmovies/util/JsonRequests.java
@@ -0,0 +1,115 @@
+package za.co.ubiquitech.vmovies.util;
+
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import za.co.ubiquitech.vmovies.MainActivity;
+
+/**
+ * @author Vane
+ * @since 20/10/2015
+ */
+public class JsonRequests {
+ private static final String LOG_TAG = JsonRequests.class.getSimpleName();
+
+ public List FetchMovieVideoKey(String id) {
+
+ HttpURLConnection urlConnection = null;
+ BufferedReader reader = null;
+ String moviesJsonStr = null;
+ try {
+
+ final String MOVIES_BASE_URL = "https://api.themoviedb.org/3/movie/";
+ final String apiKey = "/videos?api_key=" + MainActivity.MOVIE_API_KEY;
+
+ URL url = new URL(MOVIES_BASE_URL + id + apiKey);
+ urlConnection = (HttpURLConnection) url.openConnection();
+
+ urlConnection.setRequestMethod("GET");
+ urlConnection.connect();
+
+ InputStream inputStream = urlConnection.getInputStream();
+
+ StringBuilder buffer = new StringBuilder();
+
+ if (inputStream == null) {
+ Log.v(LOG_TAG, "Inputstream is null ");
+ return null;
+ }
+
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
+ // But it does make debugging a *lot* easier if you print out the completed
+ // buffer for debugging.
+ buffer.append(line).append("\n");
+ }
+ //Stream was emplty. No need to parse
+ if (buffer.length() == 0) {
+ Log.v(LOG_TAG, "Buffer is null ");
+ return null;
+ }
+ moviesJsonStr = buffer.toString();
+
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Error ", e);
+ return null;
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (final IOException e) {
+ Log.e(LOG_TAG, "Error closing stream", e);
+ }
+ }
+ }
+
+ try {
+ return getMovieDataFromJSON(moviesJsonStr);
+ } catch (JSONException e) {
+ Log.d(LOG_TAG, e.getMessage(), e);
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private List getMovieDataFromJSON(String movieStr) throws JSONException {
+ String movieKey;
+
+ JSONObject jsonObj = new JSONObject(movieStr);
+
+ //get JSON ARRAY node
+ JSONArray movieResults = jsonObj.getJSONArray("results");
+ List resultStrs = new ArrayList<>();
+ //loop through all movie results
+ for (int i = 0; i < movieResults.length(); i++) {
+ JSONObject object = movieResults.getJSONObject(i);
+ String baseYouTubeUrl = "http://www.youtube.com/watch?v=";
+ movieKey = baseYouTubeUrl + object.getString("key");
+ resultStrs.add(movieKey);
+ }
+ return resultStrs;
+ }
+
+}
+
+
+
+
diff --git a/app/src/main/res/layout-sw600dp/activity_main.xml b/app/src/main/res/layout-sw600dp/activity_main.xml
new file mode 100644
index 0000000..b5faec4
--- /dev/null
+++ b/app/src/main/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 7bb9fbf..ee1af39 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,7 +1,8 @@
+ tools:context=".MovieFragment"
+ tools:layout="@android:layout/list_content" />
diff --git a/app/src/main/res/layout/activity_movie_detail.xml b/app/src/main/res/layout/activity_movie_detail.xml
index af3b34c..2a08cc5 100644
--- a/app/src/main/res/layout/activity_movie_detail.xml
+++ b/app/src/main/res/layout/activity_movie_detail.xml
@@ -1,7 +1,9 @@
-
+ tools:context=".DetailActivity"
+ tools:ignore="MergeRootFrame"/>
+
+
diff --git a/app/src/main/res/layout/fragment_main_activity_movies_view.xml b/app/src/main/res/layout/fragment_main_activity_movies_view.xml
index 7b5a50b..e7083b8 100644
--- a/app/src/main/res/layout/fragment_main_activity_movies_view.xml
+++ b/app/src/main/res/layout/fragment_main_activity_movies_view.xml
@@ -1,17 +1,20 @@
+ tools:context=".MainActivity">
-
-
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:numColumns="auto_fit"
+ android:padding="0dp" />
diff --git a/app/src/main/res/layout/fragment_movie_detail.xml b/app/src/main/res/layout/fragment_movie_detail.xml
index d3e00db..25fd3ca 100644
--- a/app/src/main/res/layout/fragment_movie_detail.xml
+++ b/app/src/main/res/layout/fragment_movie_detail.xml
@@ -1,107 +1,198 @@
-
-
-
-
-
-
-
+ android:id="@+id/detail_layout">
+ android:fillViewport="false"
+ android:orientation="vertical">
-
+ android:layout_height="wrap_content">
-
-
-
-
-
-
+ android:layout_margin="5dp"
+ card_view:cardBackgroundColor="#9E9E9E"
+ card_view:cardCornerRadius="5dp">
+ android:orientation="vertical">
-
+ android:gravity="center"
+ android:orientation="vertical">
+
+
+ android:contentDescription="@string/movie_content"
+ android:paddingTop="5dp"
+ android:scaleType="centerCrop"
+ android:src="@mipmap/ic_launcher" />
+
+
+
-
-
-
+ android:gravity="center">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
-
-
+ android:layout_below="@+id/card_view3"
+ android:gravity="center">
+
+
+
+
+
+
-
diff --git a/app/src/main/res/layout/fragment_movie_review.xml b/app/src/main/res/layout/fragment_movie_review.xml
new file mode 100644
index 0000000..3ec47cd
--- /dev/null
+++ b/app/src/main/res/layout/fragment_movie_review.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/list_item_movie.xml b/app/src/main/res/layout/list_item_movie.xml
index 0a0bbac..4e538ad 100644
--- a/app/src/main/res/layout/list_item_movie.xml
+++ b/app/src/main/res/layout/list_item_movie.xml
@@ -2,14 +2,15 @@
+ android:layout_height="wrap_content">
+ android:padding="0dp"
+ android:scaleType="centerCrop" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_reviews.xml b/app/src/main/res/layout/list_reviews.xml
new file mode 100644
index 0000000..c958b9c
--- /dev/null
+++ b/app/src/main/res/layout/list_reviews.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_trailer.xml b/app/src/main/res/layout/list_trailer.xml
new file mode 100644
index 0000000..18c4e6c
--- /dev/null
+++ b/app/src/main/res/layout/list_trailer.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index a26a5da..397e1cd 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -1,7 +1,6 @@
diff --git a/app/src/main/res/menu/menu_movie_detail.xml b/app/src/main/res/menu/menu_movie_detail.xml
index 2706b8d..234be27 100644
--- a/app/src/main/res/menu/menu_movie_detail.xml
+++ b/app/src/main/res/menu/menu_movie_detail.xml
@@ -1,7 +1,9 @@
diff --git a/app/src/main/res/menu/menu_movie_review.xml b/app/src/main/res/menu/menu_movie_review.xml
new file mode 100644
index 0000000..bd4bf47
--- /dev/null
+++ b/app/src/main/res/menu/menu_movie_review.xml
@@ -0,0 +1,7 @@
+
diff --git a/app/src/main/res/menu/moviefragment.xml b/app/src/main/res/menu/moviefragment.xml
index 6a16d69..4a7ee7a 100644
--- a/app/src/main/res/menu/moviefragment.xml
+++ b/app/src/main/res/menu/moviefragment.xml
@@ -2,7 +2,19 @@
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_calendar.png b/app/src/main/res/mipmap-hdpi/ic_calendar.png
new file mode 100644
index 0000000..659c354
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_calendar.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_star.png b/app/src/main/res/mipmap-hdpi/ic_star.png
new file mode 100644
index 0000000..bd12430
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_star.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_white_star.png b/app/src/main/res/mipmap-hdpi/ic_white_star.png
new file mode 100644
index 0000000..ca362ac
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_white_star.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_calendar.png b/app/src/main/res/mipmap-mdpi/ic_calendar.png
new file mode 100644
index 0000000..bdfee87
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_calendar.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_star.png b/app/src/main/res/mipmap-mdpi/ic_star.png
new file mode 100644
index 0000000..492bcae
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_star.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_white_star.png b/app/src/main/res/mipmap-mdpi/ic_white_star.png
new file mode 100644
index 0000000..66bb689
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_white_star.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_calendar.png b/app/src/main/res/mipmap-xhdpi/ic_calendar.png
new file mode 100644
index 0000000..2d30f2f
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_calendar.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_star.png b/app/src/main/res/mipmap-xhdpi/ic_star.png
new file mode 100644
index 0000000..5bfe691
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_star.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_white_star.png b/app/src/main/res/mipmap-xhdpi/ic_white_star.png
new file mode 100644
index 0000000..9f6c2c8
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_white_star.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_calendar.png b/app/src/main/res/mipmap-xxhdpi/ic_calendar.png
new file mode 100644
index 0000000..0b530dd
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_calendar.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_star.png b/app/src/main/res/mipmap-xxhdpi/ic_star.png
new file mode 100644
index 0000000..bee4a62
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_star.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_white_star.png b/app/src/main/res/mipmap-xxhdpi/ic_white_star.png
new file mode 100644
index 0000000..de75c59
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_white_star.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_calendar.png b/app/src/main/res/mipmap-xxxhdpi/ic_calendar.png
new file mode 100644
index 0000000..5a26e8b
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_calendar.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_star.png b/app/src/main/res/mipmap-xxxhdpi/ic_star.png
new file mode 100644
index 0000000..a4fa9dc
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_star.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_white_star.png b/app/src/main/res/mipmap-xxxhdpi/ic_white_star.png
new file mode 100644
index 0000000..db49ac1
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_white_star.png differ
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..65d0c39
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,8 @@
+>
+
+
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index 16b2b56..23fbe16 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -1,13 +1,15 @@
- - sort_by=popularity.desc
- - sort_by=vote_average.desc
+ - popular
+ - top_rated
+ - favourite
- Most Popular
- - Highest Rating
+ - Top Rating
+ - Favourite
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 47c8224..812cb7b 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -2,4 +2,5 @@
16dp
16dp
+ 16dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2f90487..fde248b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,11 +2,17 @@
VMovies
Hello world!
Settings
+ Share
Refresh
Movie
Movie Details
sort_by
Sort Movies
Most Popular
- Highest Rating
+ favourite
+ za.co.ubiquitech.vmovies
+ Most Popular
+ Top Rating
+ Favourite
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 766ab99..35fee10 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -4,5 +4,11 @@
+
+
+
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 1a1eec7..98ce567 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -7,8 +7,9 @@
+ android:entries="@array/pref_sort_options"
+ android:summary="%s"/>
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..1b7886d
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app'