Java tutorial
/* * Copyright 2015 sourcestream GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.sourcestream.movieDB.controller; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ProgressBar; import android.widget.Toast; import com.google.android.gms.analytics.HitBuilders; import com.google.android.gms.analytics.Tracker; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import de.sourcestream.movieDB.MainActivity; import de.sourcestream.movieDB.MovieDB; import de.sourcestream.movieDB.R; import de.sourcestream.movieDB.adapter.GenresAdapter; import de.sourcestream.movieDB.model.GenresModel; /** * Genres list. */ public class GenresList extends Fragment implements AdapterView.OnItemClickListener { private MainActivity activity; private ProgressBar spinner; private AbsListView listView; private ArrayList<GenresModel> genresList; private GenresAdapter genresAdapter; private MovieList movieList; private int backState; private View rootView; private HttpURLConnection conn; private Bundle save; public GenresList() { } /** * Called to do initial creation of a fragment. * This is called after onAttach(Activity) and before onCreateView(LayoutInflater, ViewGroup, Bundle). * * @param savedInstanceState If the fragment is being re-created from a previous saved state, this is the state. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) save = savedInstanceState.getBundle("save"); } /** * Called to have the fragment instantiate its user interface view. * * @param inflater sets the layout for the current view. * @param container the container which holds the current view. * @param savedInstanceState If non-null, this fragment is being re-constructed from a previous saved state as given here. * Return the View for the fragment's UI, or null. */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); rootView = inflater.inflate(R.layout.genreslist, container, false); activity = ((MainActivity) getActivity()); spinner = (ProgressBar) rootView.findViewById(R.id.progressBar); listView = (AbsListView) rootView.findViewById(R.id.genresList); Tracker t = ((MovieDB) activity.getApplication()).getTracker(); t.setScreenName("Genres"); t.send(new HitBuilders.ScreenViewBuilder().build()); return rootView; } /** * @param savedInstanceState if the fragment is being re-created from a previous saved state, this is the state. */ @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); movieList = new MovieList(); if (save != null) { backState = save.getInt("backState"); if (backState == 1) { genresList = save.getParcelableArrayList("listData"); genresAdapter = new GenresAdapter(getActivity(), R.layout.genresrow, genresList); } } if (backState == 0) { updateList(); } else { listView.setAdapter(genresAdapter); } getActivity().setTitle(getResources().getString(R.string.genresTitle)); listView.setOnItemClickListener(this); } /** * Callback method to be invoked when an item in this AdapterView has been clicked. * * @param parent The AdapterView where the click happened. * @param view The view within the AdapterView that was clicked (this will be a view provided by the adapter) * @param position The position of the view in the adapter. * @param id The row id of the item that was clicked. */ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (movieList.getCurrentList().equals("genre/" + genresList.get(position).getId() + "/movies")) movieList.setBackState(1); else { movieList.setCurrentList("genre/" + genresList.get(position).getId() + "/movies"); movieList.setBackState(0); } movieList.setTitle(genresList.get(position).getName()); FragmentManager manager = getFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); Bundle args = new Bundle(); args.putString("currentList", "genresList"); movieList.setArguments(args); transaction.replace(R.id.frame_container, movieList); // add the current transaction to the back stack: transaction.addToBackStack("genresList"); transaction.commit(); backState = 1; } /** * This class handles the connection to our backend server. * If the connection is successful we set the list data. */ class JSONAsyncTask extends AsyncTask<String, Void, Boolean> { @Override protected void onPreExecute() { super.onPreExecute(); activity.showView(spinner); } @Override protected Boolean doInBackground(String... urls) { try { URL url = new URL(urls[0]); conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000 /* milliseconds */); conn.setConnectTimeout(10000 /* milliseconds */); conn.setRequestMethod("GET"); conn.setDoInput(true); conn.connect(); int status = conn.getResponseCode(); if (status == 200) { BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { sb.append(line).append("\n"); } br.close(); JSONObject jsonData = new JSONObject(sb.toString()); JSONArray genresArray = jsonData.getJSONArray("genres"); for (int i = 0; i < genresArray.length(); i++) { JSONObject object = genresArray.getJSONObject(i); GenresModel genre = new GenresModel(); genre.setName(object.getString("name")); genre.setId(object.getInt("id")); genresList.add(genre); } return true; } } catch (IOException | JSONException e) { if (conn != null) conn.disconnect(); } finally { if (conn != null) conn.disconnect(); } return false; } protected void onPostExecute(Boolean result) { activity.hideView(spinner); if (!result) { if (getResources() != null) { Toast.makeText(getActivity(), getResources().getString(R.string.noConnection), Toast.LENGTH_LONG).show(); backState = 0; } } else { genresAdapter.notifyDataSetChanged(); backState = 1; } } } /** * Fired from the main activity. Makes a new request to the server. * Sets list, adapter, timeout. */ public void updateList() { if (getActivity() != null) { listView = (AbsListView) rootView.findViewById(R.id.genresList); genresList = new ArrayList<>(); genresAdapter = new GenresAdapter(getActivity(), R.layout.genresrow, genresList); listView.setAdapter(genresAdapter); final JSONAsyncTask request = new JSONAsyncTask(); new Thread(new Runnable() { public void run() { try { request.execute(MovieDB.url + "genre/movie/list?&api_key=" + MovieDB.key).get(10000, TimeUnit.MILLISECONDS); } catch (TimeoutException | ExecutionException | InterruptedException e) { request.cancel(true); // we abort the http request, else it will cause problems and slow connection later if (conn != null) conn.disconnect(); if (spinner != null) activity.hideView(spinner); if (getActivity() != null) { getActivity().runOnUiThread(new Runnable() { public void run() { Toast.makeText(getActivity(), getResources().getString(R.string.timeout), Toast.LENGTH_SHORT).show(); } }); } backState = 0; } } }).start(); } } /** * Called to ask the fragment to save its current dynamic state, * so it can later be reconstructed in a new instance of its process is restarted. * * @param outState Bundle in which to place your saved state. */ @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Used to avoid bug where we add item in the back stack // and if we change orientation twice the item from the back stack has null values if (save != null) outState.putBundle("save", save); else { Bundle send = new Bundle(); send.putInt("backState", backState); if (backState == 1) { send.putParcelableArrayList("listData", genresList); // used to restore the scroll listener variables // Save scroll position if (listView != null) { Parcelable listState = listView.onSaveInstanceState(); send.putParcelable("listViewScroll", listState); } } outState.putBundle("save", send); } } /** * If our connection was successful we want to save our list data, * so when we click back it will be retained. */ public int getBackState() { return backState; } public MovieList getMovieListView() { return movieList; } /** * Set empty adapter to free memory when this fragment is inactive */ public void onDestroyView() { super.onDestroyView(); } }