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; import de.sourcestream.movieDB.adapter.SearchDB; import de.sourcestream.movieDB.controller.CastDetails; import de.sourcestream.movieDB.controller.GalleryList; import de.sourcestream.movieDB.controller.GenresList; import de.sourcestream.movieDB.controller.MovieDetails; import de.sourcestream.movieDB.controller.MovieSlideTab; import de.sourcestream.movieDB.controller.SearchList; import de.sourcestream.movieDB.controller.TVDetails; import de.sourcestream.movieDB.controller.TVSlideTab; import de.sourcestream.movieDB.controller.TrailerList; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URL; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Locale; import java.util.concurrent.CancellationException; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.SearchManager; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Bitmap; import android.net.ParseException; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v4.view.MenuItemCompat; import android.support.v4.widget.SimpleCursorAdapter; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; import android.text.Html; import android.util.DisplayMetrics; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.RatingBar; import android.widget.TextView; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.utils.StorageUtils; import net.hockeyapp.android.CrashManager; import net.hockeyapp.android.UpdateManager; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * Main class program starts from here. */ public class MainActivity extends AppCompatActivity { private final int CacheSize = 52428800; // 50MB private final int MinFreeSpace = 2048; // 2MB private static final long maxMem = Runtime.getRuntime().maxMemory(); private DrawerLayout mDrawerLayout; private ListView mDrawerList; private ActionBarDrawerToggle mDrawerToggle; // nav drawer title private CharSequence mDrawerTitle; // used to store app title private CharSequence mTitle; // slide menu items private String[] navMenuTitles; private MovieSlideTab movieSlideTab = new MovieSlideTab(); private TVSlideTab tvSlideTab = new TVSlideTab(); private About about = new About(); private GenresList genresList = new GenresList(); private SearchList searchList = new SearchList(); private SearchView searchView; private MenuItem searchViewItem; private ImageLoader imageLoader; // Create search View listeners private SearchViewOnQueryTextListener searchViewOnQueryTextListener = new SearchViewOnQueryTextListener(); private onSearchViewItemExpand onSearchViewItemExpand = new onSearchViewItemExpand(); private SearchSuggestionListener searchSuggestionListener = new SearchSuggestionListener(); private SearchDB searchDB; private Toolbar toolbar; private int oldPos = -1; private boolean isDrawerOpen = false; private DisplayImageOptions optionsWithFade; private DisplayImageOptions optionsWithoutFade; private DisplayImageOptions backdropOptionsWithFade; private DisplayImageOptions backdropOptionsWithoutFade; private int currentMovViewPagerPos; private int currentTVViewPagerPos; private boolean reAttachMovieFragments; private boolean reAttachTVFragments; private TrailerList trailerListView; private GalleryList galleryListView; private MovieDetails movieDetailsFragment; private MovieDetails movieDetailsSimFragment; private boolean saveInMovieDetailsSimFragment; private TVDetails tvDetailsSimFragment; private boolean saveInTVDetailsSimFragment; private CastDetails castDetailsFragment; private TVDetails tvDetailsFragment; private OnDrawerBackButton onDrawerBackButton = new OnDrawerBackButton(); private Bundle movieDetailsInfoBundle; private Bundle movieDetailsCastBundle; private Bundle movieDetailsOverviewBundle; private Bundle castDetailsInfoBundle; private Bundle castDetailsCreditsBundle; private Bundle castDetailsBiographyBundle; private Bundle TVDetailsInfoBundle; private Bundle TVDetailsCastBundle; private Bundle TVDetailsOverviewBundle; private ArrayList<Bundle> movieDetailsBundle = new ArrayList<>(); private ArrayList<Bundle> castDetailsBundle = new ArrayList<>(); private ArrayList<Bundle> tvDetailsBundle = new ArrayList<>(); private boolean restoreMovieDetailsAdapterState; private boolean restoreMovieDetailsState; private int currOrientation; private boolean orientationChanged; private boolean searchViewTap; private boolean searchViewCount; private static int searchMovieDetails; private static int searchCastDetails; private static int searchTvDetails; private int lastVisitedSimMovie; private int lastVisitedSimTV; private int lastVisitedMovieInCredits; private HttpURLConnection conn; private SimpleCursorAdapter searchAdapter; private String query; private JSONAsyncTask request; private SearchImgLoadingListener searchImgLoadingListener; private int iconMarginConstant; private int iconMarginLandscape; private int iconConstantSpecialCase; private int threeIcons; private int threeIconsToolbar; private int twoIcons; private int twoIconsToolbar; private int oneIcon; private int oneIconToolbar; private boolean phone; private DateFormat dateFormat; /** * First configure the Universal Image Downloader, * then we set the main layout to be activity_main.xml * and we add the slide menu items. * * @param savedInstanceState If non-null, this activity is being re-constructed from a previous saved state as given here. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTitle = mDrawerTitle = getTitle(); // load slide menu items navMenuTitles = getResources().getStringArray(R.array.nav_drawer_items); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.list_slidermenu); ViewGroup header = (ViewGroup) getLayoutInflater().inflate(R.layout.drawer_header, null, false); ImageView drawerBackButton = (ImageView) header.findViewById(R.id.drawerBackButton); drawerBackButton.setOnClickListener(onDrawerBackButton); mDrawerList.addHeaderView(header); mDrawerList.setOnItemClickListener(new SlideMenuClickListener()); // setting the nav drawer list adapter mDrawerList.setAdapter(new ArrayAdapter<>(this, R.layout.drawer_list_item, R.id.title, navMenuTitles)); toolbar = (Toolbar) findViewById(R.id.toolbar); if (toolbar != null) { setSupportActionBar(toolbar); toolbar.bringToFront(); } mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ toolbar, R.string.app_name, // nav drawer open - description for accessibility R.string.app_name // nav drawer close - description for accessibility ) { public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); // calling onPrepareOptionsMenu() to show search view invalidateOptionsMenu(); syncState(); } public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); // calling onPrepareOptionsMenu() to hide search view invalidateOptionsMenu(); syncState(); } // updates the title, toolbar transparency and search view public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, slideOffset); if (slideOffset > .55 && !isDrawerOpen) { // opening drawer // mDrawerTitle is app title getSupportActionBar().setTitle(mDrawerTitle); invalidateOptionsMenu(); isDrawerOpen = true; } else if (slideOffset < .45 && isDrawerOpen) { // closing drawer // mTitle is title of the current view, can be movies, tv shows or movie title getSupportActionBar().setTitle(mTitle); invalidateOptionsMenu(); isDrawerOpen = false; } } }; mDrawerLayout.setDrawerListener(mDrawerToggle); // Get the action bar title to set padding TextView titleTextView = null; try { Field f = toolbar.getClass().getDeclaredField("mTitleTextView"); f.setAccessible(true); titleTextView = (TextView) f.get(toolbar); } catch (NoSuchFieldException e) { } catch (IllegalAccessException e) { } if (titleTextView != null) { float scale = getResources().getDisplayMetrics().density; titleTextView.setPadding((int) scale * 15, 0, 0, 0); } phone = getResources().getBoolean(R.bool.portrait_only); searchDB = new SearchDB(getApplicationContext()); if (savedInstanceState == null) { // Check orientation and lock to portrait if we are on phone if (phone) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } // on first time display view for first nav item displayView(1); // Use hockey module to check for updates checkForUpdates(); // Universal Loader options and configuration. DisplayImageOptions options = new DisplayImageOptions.Builder() // Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888. .bitmapConfig(Bitmap.Config.RGB_565).imageScaleType(ImageScaleType.EXACTLY).cacheInMemory(false) .showImageOnLoading(R.drawable.placeholder_default) .showImageForEmptyUri(R.drawable.placeholder_default) .showImageOnFail(R.drawable.placeholder_default).cacheOnDisk(true).build(); Context context = this; File cacheDir = StorageUtils.getCacheDirectory(context); // Create global configuration and initialize ImageLoader with this config ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this) .diskCache(new UnlimitedDiscCache(cacheDir)) // default .defaultDisplayImageOptions(options).build(); ImageLoader.getInstance().init(config); // Check cache size long size = 0; File[] filesCache = cacheDir.listFiles(); for (File file : filesCache) { size += file.length(); } if (cacheDir.getUsableSpace() < MinFreeSpace || size > CacheSize) { ImageLoader.getInstance().getDiskCache().clear(); searchDB.cleanSuggestionRecords(); } } else { oldPos = savedInstanceState.getInt("oldPos"); currentMovViewPagerPos = savedInstanceState.getInt("currentMovViewPagerPos"); currentTVViewPagerPos = savedInstanceState.getInt("currentTVViewPagerPos"); restoreMovieDetailsState = savedInstanceState.getBoolean("restoreMovieDetailsState"); restoreMovieDetailsAdapterState = savedInstanceState.getBoolean("restoreMovieDetailsAdapterState"); movieDetailsBundle = savedInstanceState.getParcelableArrayList("movieDetailsBundle"); castDetailsBundle = savedInstanceState.getParcelableArrayList("castDetailsBundle"); tvDetailsBundle = savedInstanceState.getParcelableArrayList("tvDetailsBundle"); currOrientation = savedInstanceState.getInt("currOrientation"); lastVisitedSimMovie = savedInstanceState.getInt("lastVisitedSimMovie"); lastVisitedSimTV = savedInstanceState.getInt("lastVisitedSimTV"); lastVisitedMovieInCredits = savedInstanceState.getInt("lastVisitedMovieInCredits"); saveInMovieDetailsSimFragment = savedInstanceState.getBoolean("saveInMovieDetailsSimFragment"); FragmentManager fm = getFragmentManager(); // prevent the following bug: go to gallery preview -> swap orientation -> // go to movies list -> swap orientation -> action bar bugged // so if we are not on gallery preview we show toolbar if (fm.getBackStackEntryCount() == 0 || !fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName().equals("galleryList")) { new Handler().post(new Runnable() { @Override public void run() { if (getSupportActionBar() != null && !getSupportActionBar().isShowing()) getSupportActionBar().show(); } }); } } // Get reference for the imageLoader imageLoader = ImageLoader.getInstance(); // Options used for the backdrop image in movie and tv details and gallery optionsWithFade = new DisplayImageOptions.Builder() // Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888. .bitmapConfig(Bitmap.Config.RGB_565).displayer(new FadeInBitmapDisplayer(500)) .imageScaleType(ImageScaleType.EXACTLY).cacheInMemory(false).showImageOnLoading(R.color.black) .showImageForEmptyUri(R.color.black).showImageOnFail(R.color.black).cacheOnDisk(true).build(); optionsWithoutFade = new DisplayImageOptions.Builder() // Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888. .bitmapConfig(Bitmap.Config.RGB_565).imageScaleType(ImageScaleType.EXACTLY).cacheInMemory(false) .showImageOnLoading(R.color.black).showImageForEmptyUri(R.color.black) .showImageOnFail(R.color.black).cacheOnDisk(true).build(); // Options used for the backdrop image in movie and tv details and gallery backdropOptionsWithFade = new DisplayImageOptions.Builder() // Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888. .bitmapConfig(Bitmap.Config.RGB_565).displayer(new FadeInBitmapDisplayer(500)) .imageScaleType(ImageScaleType.EXACTLY).cacheInMemory(false) .showImageOnLoading(R.drawable.placeholder_backdrop) .showImageForEmptyUri(R.drawable.placeholder_backdrop) .showImageOnFail(R.drawable.placeholder_backdrop).cacheOnDisk(true).build(); backdropOptionsWithoutFade = new DisplayImageOptions.Builder() // Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888. .bitmapConfig(Bitmap.Config.RGB_565).imageScaleType(ImageScaleType.EXACTLY).cacheInMemory(false) .showImageOnLoading(R.drawable.placeholder_backdrop) .showImageForEmptyUri(R.drawable.placeholder_backdrop) .showImageOnFail(R.drawable.placeholder_backdrop).cacheOnDisk(true).build(); trailerListView = new TrailerList(); galleryListView = new GalleryList(); if (currOrientation != getResources().getConfiguration().orientation) orientationChanged = true; currOrientation = getResources().getConfiguration().orientation; iconConstantSpecialCase = 0; if (phone) { iconMarginConstant = 0; iconMarginLandscape = 0; DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); int width = displayMetrics.widthPixels; int height = displayMetrics.heightPixels; if (width <= 480 && height <= 800) iconConstantSpecialCase = -70; // used in MovieDetails, CastDetails, TVDetails onMoreIconClick // to check whether the animation should be in up or down direction threeIcons = 128; threeIconsToolbar = 72; twoIcons = 183; twoIconsToolbar = 127; oneIcon = 238; oneIconToolbar = 182; } else { if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { iconMarginConstant = 232; iconMarginLandscape = 300; threeIcons = 361; threeIconsToolbar = 295; twoIcons = 416; twoIconsToolbar = 351; oneIcon = 469; oneIconToolbar = 407; } else { iconMarginConstant = 82; iconMarginLandscape = 0; threeIcons = 209; threeIconsToolbar = 146; twoIcons = 264; twoIconsToolbar = 200; oneIcon = 319; oneIconToolbar = 256; } } dateFormat = android.text.format.DateFormat.getDateFormat(this); } /** * Pops the last item from the back stack. * If searchView is opened it hides it. * reAttachMovieFragments re-creates our fragments this is due to a bug in the viewPager * restoreMovieDetailsState -> when we press back button we would like to restore our previous (if any) * saved state for the current fragment. We use custom backStack since the original doesn't save fragment's state. */ @Override public void onBackPressed() { FragmentManager fm = getFragmentManager(); if (mDrawerLayout.isDrawerOpen(mDrawerList)) mDrawerLayout.closeDrawer(mDrawerList); else { if (searchViewItem.isActionViewExpanded()) searchViewItem.collapseActionView(); else if (fm.getBackStackEntryCount() > 0) { String backStackEntry = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName(); if (backStackEntry.equals("movieList")) reAttachMovieFragments = true; if (backStackEntry.equals("TVList")) reAttachTVFragments = true; if (backStackEntry.equals("searchList:1")) reAttachMovieFragments = true; if (backStackEntry.equals("searchList:2")) reAttachTVFragments = true; restoreMovieDetailsState = true; restoreMovieDetailsAdapterState = false; if (orientationChanged) restoreMovieDetailsAdapterState = true; fm.popBackStack(); } else { super.onBackPressed(); } } } /** * This method is fired when the activity is paused. * For example if we minimize our app. */ @Override protected void onPause() { super.onPause(); UpdateManager.unregister(); } /** * This method is fired when the activity is resumed. */ @Override protected void onResume() { super.onResume(); checkForCrashes(); } /** * This method is used by the hockey library to check for crashes. */ private void checkForCrashes() { CrashManager.register(this, MovieDB.appId); } /** * This method is used by the hockey library to check for updates. */ private void checkForUpdates() { // Remove this for store / production builds! UpdateManager.register(this, MovieDB.appId); } /** * Slide menu item click listener. * Fired when you click on item from the slide menu. */ private class SlideMenuClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // display view for selected nav drawer item displayView(position); } } /** * Initialize the contents of the Activity's standard options menu. * This is only called once, the first time the options menu is displayed. * * @param menu the options menu in which we place our items. * @return You must return true for the menu to be displayed; if you return false it will not be shown. */ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); searchViewItem = menu.findItem(R.id.search); searchView = (SearchView) MenuItemCompat.getActionView(searchViewItem); searchView.setQueryHint(getResources().getString(R.string.search_hint)); searchView.setOnQueryTextListener(searchViewOnQueryTextListener); searchView.setOnSuggestionListener(searchSuggestionListener); // Associate searchable configuration with the SearchView SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchViewItemC = (SearchView) menu.findItem(R.id.search).getActionView(); searchViewItemC.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); String[] from = { SearchManager.SUGGEST_COLUMN_ICON_1, SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_TEXT_2 }; int[] to = { R.id.posterPath, R.id.title, R.id.info }; searchAdapter = new SimpleCursorAdapter(getApplicationContext(), R.layout.suggestionrow, null, from, to, 0) { @Override public void changeCursor(Cursor cursor) { super.swapCursor(cursor); } }; searchViewItemC.setSuggestionsAdapter(searchAdapter); MenuItemCompat.setOnActionExpandListener(searchViewItem, onSearchViewItemExpand); return true; } /** * This hook is called whenever an item in our options menu is selected. * * @param item The menu item that was selected. * @return Return false to allow normal menu processing to proceed, true to consume it here. */ @Override public boolean onOptionsItemSelected(MenuItem item) { // toggle nav drawer on selecting action bar app icon/title if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action bar actions click switch (item.getItemId()) { case R.id.search: return true; default: return super.onOptionsItemSelected(item); } } /** * Called when invalidateOptionsMenu() is triggered * Prepare the Screen's standard options menu to be displayed. * This is called right before the menu is shown, every time it is shown. * You can use this method to efficiently enable/disable items or otherwise dynamically modify the contents. * * @param menu The options menu as last shown or first initialized by onCreateOptionsMenu(). * @return You must return true for the menu to be displayed; if you return false it will not be shown. * If the navigation drawer is opened we hide the search view. * If we are on genres or about view we hide the search view. */ @Override public boolean onPrepareOptionsMenu(Menu menu) { if (isDrawerOpen) menu.findItem(R.id.search).setVisible(false); else if (oldPos == 4) menu.findItem(R.id.search).setVisible(false); else menu.findItem(R.id.search).setVisible(true); return super.onPrepareOptionsMenu(menu); } /** * Displays fragment view for the selected item from the slide menu. * * @param position is the position that we have selected. */ private void displayView(int position) { if (position != 0) { // Clear history of the back stack if any FragmentManager fm = getFragmentManager(); fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); // update the main content by replacing fragments Fragment fragment = null; searchMovieDetails = 0; searchCastDetails = 0; searchTvDetails = 0; searchViewCount = false; resetMovieDetailsBundle(); resetCastDetailsBundle(); resetTvDetailsBundle(); switch (position) { // Case 0 is the header we don't want to do anything with it. case 1: reAttachMovieFragments = true; if (oldPos == position) { mDrawerLayout.closeDrawer(mDrawerList); break; } fragment = movieSlideTab; break; case 2: reAttachTVFragments = true; if (oldPos == position) { mDrawerLayout.closeDrawer(mDrawerList); break; } fragment = tvSlideTab; break; case 3: if (oldPos == position) { mDrawerLayout.closeDrawer(mDrawerList); break; } fragment = getFragmentManager().findFragmentByTag("genres"); if (fragment == null) fragment = genresList; if (genresList.getBackState() == 0) genresList.updateList(); break; case 4: if (oldPos == position) { mDrawerLayout.closeDrawer(mDrawerList); break; } fragment = about; break; default: break; } oldPos = position; if (fragment != null) { fm.beginTransaction().replace(R.id.frame_container, fragment).commit(); // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); mDrawerList.setSelection(position); setTitle(navMenuTitles[position - 1]); mDrawerLayout.closeDrawer(mDrawerList); try { movieSlideTab.showInstantToolbar(); tvSlideTab.showInstantToolbar(); } catch (NullPointerException e) { } System.gc(); } } else { mDrawerList.setItemChecked(oldPos, true); } } /** * We use this method to update the action bar title. * * @param title the new title. * If the searchView is opened we don't call invalidateOptionsMenu() else hides the searchView. */ @Override public void setTitle(CharSequence title) { mTitle = title; if (getSupportActionBar() != null) getSupportActionBar().setTitle(mTitle); // Fixes truncating shorter titles: https://code.google.com/p/android/issues/detail?id=55256 if (!searchViewTap) { invalidateOptionsMenu(); } else searchViewTap = false; } /** * Called when activity start-up is complete * When using the ActionBarDrawerToggle, you must call it during * onPostCreate() and onConfigurationChanged()... * * @param savedInstanceState If the activity is being re-initialized after previously being shut down then * this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null. */ @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); } /** * Called by the system when the device configuration changes while your activity is running. * Note that this will only be called if you have selected configurations you would like to handle * with the configChanges attribute in your manifest. * * @param newConfig The new device configuration. */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Pass any configuration change to the drawer toggle mDrawerToggle.onConfigurationChanged(newConfig); } /** * Class which listens for SearchView events. */ private class SearchViewOnQueryTextListener implements SearchView.OnQueryTextListener { /** * Called when the query text is changed by the user. * * @param newText the new content of the query text field. * @return false if the SearchView should perform the default action of showing any suggestions if available, * true if the action was handled by the listener. */ @Override public boolean onQueryTextChange(String newText) { query = newText; query = query.replaceAll("[\\s%\"^#<>{}\\\\|`]", "%20"); if (query.length() > 1) { new Thread(new Runnable() { public void run() { try { if (request != null) request.cancel(true); if (conn != null) conn.disconnect(); request = new JSONAsyncTask(); request.execute( MovieDB.url + "search/multi?query=" + query + "?&api_key=" + MovieDB.key); request.setQuery(query); } catch (CancellationException e) { if (request != null) request.cancel(true); // we abort the http request, else it will cause problems and slow connection later if (conn != null) conn.disconnect(); } } }).start(); } else { String[] selArgs = { query }; searchDB.cleanAutoCompleteRecords(); Cursor c = searchDB.getSuggestions(selArgs); searchAdapter.changeCursor(c); } return true; } /** * Called when the user submits the query. * This could be due to a key press on the keyboard or due to pressing a submit button. * * @param query the query text that is to be submitted * @return true if the query has been handled by the listener, false to let the SearchView perform the default action. * We update the query, hide the keyboard and add the query to the suggestions. */ @Override public boolean onQueryTextSubmit(String query) { searchList.setQuery(query); searchView.clearFocus(); return true; } } /** * Class which listens when we tap on the SearchView. */ public class onSearchViewItemExpand implements MenuItemCompat.OnActionExpandListener { FragmentManager fm = getFragmentManager(); /** * Called when the searchView icon is tapped. * * @param item our searchView; * @return true if the item should expand, false if expansion should be suppressed. * If we have navigated through items from the search view result list and we tap again, * we clear our backStack. */ @Override public boolean onMenuItemActionExpand(MenuItem item) { // search view key searchViewTap = true; if (searchMovieDetails > 0) clearMovieDetailsBackStack(); if (searchCastDetails > 0) clearCastDetailsBackStack(); if (searchTvDetails > 0) clearTvDetailsBackStack(); // check if we are already in the search view to prevent double adding in the back stack if (fm.getBackStackEntryCount() == 0 || !fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1) .getName().startsWith("searchList")) { // checks if the search view has been created, if it is created this method pops it from the back stack // also this clears back stack history until the search list boolean fragmentPopped = false; if (fm.popBackStackImmediate("searchList:1", 0)) fragmentPopped = true; if (fm.popBackStackImmediate("searchList:2", 0)) fragmentPopped = true; if (fm.popBackStackImmediate("searchList:3", 0)) fragmentPopped = true; // if getId == 0 this means that the fragment has been detached // we only need the current active fragment to save its state if (!fragmentPopped) { if (movieDetailsFragment != null && movieDetailsFragment.getId() != 0) { // check if the movie is already in our backStack if (movieDetailsBundle.size() > 0) { if (!getSupportActionBar().getTitle().equals( movieDetailsBundle.get(movieDetailsBundle.size() - 1).getString("title"))) { movieDetailsFragment.setAddToBackStack(true); movieDetailsFragment.onSaveInstanceState(new Bundle()); } } else { movieDetailsFragment.setAddToBackStack(true); movieDetailsFragment.onSaveInstanceState(new Bundle()); } } if (castDetailsFragment != null && castDetailsFragment.getId() != 0) { // check if the actor is already in our backStack if (castDetailsBundle.size() > 0) { if (!getSupportActionBar().getTitle().equals( castDetailsBundle.get(castDetailsBundle.size() - 1).getString("title"))) { castDetailsFragment.setAddToBackStack(true); castDetailsFragment.onSaveInstanceState(new Bundle()); } } else { castDetailsFragment.setAddToBackStack(true); castDetailsFragment.onSaveInstanceState(new Bundle()); } } if (tvDetailsFragment != null && tvDetailsFragment.getId() != 0) { // check if the tv is already in our backStack if (tvDetailsBundle.size() > 0) { if (!getSupportActionBar().getTitle() .equals(tvDetailsBundle.get(tvDetailsBundle.size() - 1).getString("title"))) { tvDetailsFragment.setAddToBackStack(true); tvDetailsFragment.onSaveInstanceState(new Bundle()); } } else { tvDetailsFragment.setAddToBackStack(true); tvDetailsFragment.onSaveInstanceState(new Bundle()); } } FragmentTransaction transaction = fm.beginTransaction(); searchList.setTitle(getResources().getString(R.string.search_title)); transaction.replace(R.id.frame_container, searchList); // add the current transaction to the back stack: transaction.addToBackStack("searchList:" + oldPos); transaction.commit(); } } return true; } /** * Called when click back button or by collapseSearchView() method. * * @param item our searchView; * @return true if the item should collapse, false if collapsing should be suppressed. */ @Override public boolean onMenuItemActionCollapse(MenuItem item) { return true; } } /** * Fired from SearchList controller when you tap on movie. * Hides the searchView text area. */ public void collapseSearchView() { searchViewItem.collapseActionView(); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * We update the text value of a TextView, from runOnUiThread() because we can't update it from async task. * * @param text the TextView to update. * @param value the new text value. */ public void setText(final TextView text, final String value) { runOnUiThread(new Runnable() { @Override public void run() { text.setText(value); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * We update the text value of a TextView, from runOnUiThread() because we can't update it from async task. * * @param text the TextView to update. * @param value the new text value. */ public void setTextFromHtml(final TextView text, final String value) { runOnUiThread(new Runnable() { @Override public void run() { text.setText(Html.fromHtml(value)); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * We use our imageLoader to display image on the given image view. * runOnUiThread() is called because we can't update it from async task. * * @param img the ImageView we display image on. * @param url the url from which we display the image. * R.string.imageSize defines the size of our images. */ public void setImage(final ImageView img, final String url) { runOnUiThread(new Runnable() { @Override public void run() { imageLoader.displayImage(MovieDB.imageUrl + getResources().getString(R.string.imageSize) + url, img); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * We set url tag on the imageView. So when we tap later on it we known which url to load. * runOnUiThread() is called because we can't update it from async task. * * @param img the ImageView we display image on. * @param url the url to set tag. * R.string.imageSize defines the size of our images. */ public void setImageTag(final ImageView img, final String url) { runOnUiThread(new Runnable() { @Override public void run() { img.setTag(url); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * We use our imageLoader to display image on the given image view. * runOnUiThread() is called because we can't update it from async task. * * @param img the ImageView we display image on. * @param url the url to set tag. * R.string.backDropImgSize defines the size of our backDrop images. * If we load the image for first time we show fade effect, else no effect. */ public void setBackDropImage(final ImageView img, final String url) { runOnUiThread(new Runnable() { @Override public void run() { if (imageLoader.getDiskCache() .get(MovieDB.imageUrl + getResources().getString(R.string.backDropImgSize) + url).exists()) imageLoader.displayImage( MovieDB.imageUrl + getResources().getString(R.string.backDropImgSize) + url, img, backdropOptionsWithoutFade); else imageLoader.displayImage( MovieDB.imageUrl + getResources().getString(R.string.backDropImgSize) + url, img, backdropOptionsWithFade); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * runOnUiThread() is called because we can't update it from async task. * * @param ratingBar the ratingBar we set value. * @param value the value we will set on the ratingBar. */ public void setRatingBarValue(final RatingBar ratingBar, final float value) { runOnUiThread(new Runnable() { @Override public void run() { ratingBar.setRating(value); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * * @param layout the layout which we hide. */ public void hideLayout(final ViewGroup layout) { runOnUiThread(new Runnable() { @Override public void run() { layout.setVisibility(View.GONE); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * * @param view the view which we hide. */ public void hideView(final View view) { runOnUiThread(new Runnable() { @Override public void run() { view.setVisibility(View.GONE); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * * @param view the view which we show. */ public void showView(final View view) { runOnUiThread(new Runnable() { @Override public void run() { view.setVisibility(View.VISIBLE); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * * @param textView the TextView which we hide. */ public void hideTextView(final TextView textView) { runOnUiThread(new Runnable() { @Override public void run() { textView.setVisibility(View.GONE); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * * @param ratingBar the RatingBar which we hide. */ public void hideRatingBar(final RatingBar ratingBar) { runOnUiThread(new Runnable() { @Override public void run() { ratingBar.setVisibility(View.GONE); } }); } /** * This method is used in MovieDetails, CastDetails and TVDetails. * Makes a view invisible. * * @param view the View which we make invisible. */ public void invisibleView(final View view) { runOnUiThread(new Runnable() { @Override public void run() { view.setVisibility(View.INVISIBLE); } }); } /** * We use this to get the current Viewpager item in movies. */ public int getCurrentMovViewPagerPos() { return currentMovViewPagerPos; } /** * We use this to set the current Viewpager item in movies. */ public void setCurrentMovViewPagerPos(int currentMovViewPagerPos) { this.currentMovViewPagerPos = currentMovViewPagerPos; } /** * We use this to get the current Viewpager item in TVs. */ public int getCurrentTVViewPagerPos() { return currentTVViewPagerPos; } /** * We use this to set the current Viewpager item in TVs. */ public void setCurrentTVViewPagerPos(int currentTVViewPagerPos) { this.currentTVViewPagerPos = currentTVViewPagerPos; } /** * @return our activity_main.xml */ public DrawerLayout getMDrawerLayout() { return mDrawerLayout; } /** * @return true if our ViewPager in Movies should reAttach the Fragments. */ public boolean getReAttachMovieFragments() { return reAttachMovieFragments; } /** * Set if we should reAttach the Fragments in the ViewPager in Movies. */ public void setReAttachMovieFragments(boolean reAttachMovieFragments) { this.reAttachMovieFragments = reAttachMovieFragments; } /** * @return true if our ViewPager in TVs should reAttach the Fragments. */ public boolean getReAttachTVFragments() { return reAttachTVFragments; } /** * Set if we should reAttach the Fragments in the ViewPager in TVs. */ public void setReAttachTVFragments(boolean reAttachTVFragments) { this.reAttachTVFragments = reAttachTVFragments; } /** * Fired when activity is recreated. * * @param outState Bundle in which to place your saved state. */ @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("oldPos", oldPos); outState.putInt("currentMovViewPagerPos", currentMovViewPagerPos); outState.putInt("currentTVViewPagerPos", currentTVViewPagerPos); outState.putBoolean("restoreMovieDetailsAdapterState", restoreMovieDetailsAdapterState); outState.putBoolean("restoreMovieDetailsState", restoreMovieDetailsState); outState.putParcelableArrayList("movieDetailsBundle", movieDetailsBundle); outState.putParcelableArrayList("castDetailsBundle", castDetailsBundle); outState.putParcelableArrayList("tvDetailsBundle", tvDetailsBundle); outState.putInt("currOrientation", currOrientation); outState.putInt("lastVisitedSimMovie", lastVisitedSimMovie); outState.putInt("lastVisitedSimTV", lastVisitedSimTV); outState.putInt("lastVisitedMovieInCredits", lastVisitedMovieInCredits); outState.putBoolean("saveInMovieDetailsSimFragment", saveInMovieDetailsSimFragment); } /** * Method which returns the maximum heap size of the device. */ public static long getMaxMem() { return maxMem; } /** * Method which returns the TrailerList view. */ public TrailerList getTrailerListView() { return trailerListView; } /** * Method which returns the GalleryList view. */ public GalleryList getGalleryListView() { return galleryListView; } /** * Method which sets our movieDetails Fragment. */ public void setMovieDetailsFragment(MovieDetails movieDetailsFragment) { this.movieDetailsFragment = movieDetailsFragment; } /** * Method which returns our movieDetails Fragment. */ public MovieDetails getMovieDetailsFragment() { return movieDetailsFragment; } /** * Method which sets our castDetails Fragment. */ public void setCastDetailsFragment(CastDetails castDetailsFragment) { this.castDetailsFragment = castDetailsFragment; } /** * Method which returns our castDetails Fragment. */ public CastDetails getCastDetailsFragment() { return castDetailsFragment; } /** * Method which returns our TVDetails Fragment. */ public TVDetails getTvDetailsFragment() { return tvDetailsFragment; } /** * Method which sets our TVDetails Fragment. */ public void setTvDetailsFragment(TVDetails tvDetailsFragment) { this.tvDetailsFragment = tvDetailsFragment; } /** * Retrieves the config for the imageLoader with fade effect. */ public DisplayImageOptions getOptionsWithFade() { return optionsWithFade; } /** * Retrieves the config for the imageLoader without fade effect. */ public DisplayImageOptions getOptionsWithoutFade() { return optionsWithoutFade; } /** * Drawer backButton listener. */ public class OnDrawerBackButton implements View.OnClickListener { public OnDrawerBackButton() { // keep references for your onClick logic } @Override public void onClick(View v) { onBackPressed(); } } /** * Gets the saved instance in our Movie Details Info tab. */ public Bundle getMovieDetailsInfoBundle() { return movieDetailsInfoBundle; } /** * Sets the saved instance in our Movie Details Info tab. Most likely sets it to null. */ public void setMovieDetailsInfoBundle(Bundle movieDetailsInfoBundle) { this.movieDetailsInfoBundle = movieDetailsInfoBundle; } /** * Gets the saved instance in our Movie Details Cast tab. */ public Bundle getMovieDetailsCastBundle() { return movieDetailsCastBundle; } /** * Sets the saved instance in our Movie Details Cast tab. Most likely sets it to null. */ public void setMovieDetailsCastBundle(Bundle movieDetailsCastBundle) { this.movieDetailsCastBundle = movieDetailsCastBundle; } /** * Gets the saved instance in our Movie Overview Info tab. */ public Bundle getMovieDetailsOverviewBundle() { return movieDetailsOverviewBundle; } /** * Sets the saved instance in our Movie Details Overview tab. Most likely sets it to null. */ public void setMovieDetailsOverviewBundle(Bundle movieDetailsOverviewBundle) { this.movieDetailsOverviewBundle = movieDetailsOverviewBundle; } /** * Gets the saved instance in our Cast Details Info tab. */ public Bundle getCastDetailsInfoBundle() { return castDetailsInfoBundle; } /** * Sets the saved instance in our Cast Details Info tab. Most likely sets it to null. */ public void setCastDetailsInfoBundle(Bundle castDetailsInfoBundle) { this.castDetailsInfoBundle = castDetailsInfoBundle; } /** * Gets the saved instance in our Cast Details Credits tab. */ public Bundle getCastDetailsCreditsBundle() { return castDetailsCreditsBundle; } /** * Sets the saved instance in our Cast Details Credits tab. Most likely sets it to null. */ public void setCastDetailsCreditsBundle(Bundle castDetailsCreditsBundle) { this.castDetailsCreditsBundle = castDetailsCreditsBundle; } /** * Gets the saved instance in our Cast Details Biography tab. */ public Bundle getCastDetailsBiographyBundle() { return castDetailsBiographyBundle; } /** * Sets the saved instance in our Cast Details Biography tab. Most likely sets it to null. */ public void setCastDetailsBiographyBundle(Bundle castDetailsBiographyBundle) { this.castDetailsBiographyBundle = castDetailsBiographyBundle; } /** * Gets the saved instance in our TV Details Info tab. */ public Bundle getTVDetailsInfoBundle() { return TVDetailsInfoBundle; } /** * Sets the saved instance in our TV Details Info tab. Most likely sets it to null. */ public void setTVDetailsInfoBundle(Bundle TVDetailsInfoBundle) { this.TVDetailsInfoBundle = TVDetailsInfoBundle; } /** * Gets the saved instance in our TV Details Cast tab. */ public Bundle getTVDetailsCastBundle() { return TVDetailsCastBundle; } /** * Sets the saved instance in our TV Details Cast tab. Most likely sets it to null. */ public void setTVDetailsCastBundle(Bundle TVDetailsCastBundle) { this.TVDetailsCastBundle = TVDetailsCastBundle; } /** * Gets the saved instance in our TV Details Overview tab. */ public Bundle getTVDetailsOverviewBundle() { return TVDetailsOverviewBundle; } /** * Sets the saved instance in our TV Details Overview tab. Most likely sets it to null. */ public void setTVDetailsOverviewBundle(Bundle TVDetailsOverviewBundle) { this.TVDetailsOverviewBundle = TVDetailsOverviewBundle; } /** * Method which adds Movie Details savedState to our ArrayList. * We use it for our back navigation. */ public void addMovieDetailsBundle(Bundle movieDetailsBundle) { this.movieDetailsBundle.add(movieDetailsBundle); } /** * Method which removes Movie Details savedState to our ArrayList. * We use it for our back navigation. */ public void removeMovieDetailsBundle(int pos) { movieDetailsBundle.remove(pos); } /** * Method which resets Movie Details ArrayList. * We use it for our back navigation. */ public void resetMovieDetailsBundle() { movieDetailsBundle = new ArrayList<>(); } /** * Method which gets Movie Details savedState from our ArrayList. * We use it for our back navigation. */ public ArrayList<Bundle> getMovieDetailsBundle() { return movieDetailsBundle; } /** * Method which adds Cast Details savedState to our ArrayList. * We use it for our back navigation. */ public void addCastDetailsBundle(Bundle castDetailsBundle) { this.castDetailsBundle.add(castDetailsBundle); } /** * Method which removes Cast Details savedState to our ArrayList. * We use it for our back navigation. */ public void removeCastDetailsBundle(int pos) { castDetailsBundle.remove(pos); } /** * Method which resets Cast Details ArrayList. * We use it for our back navigation. */ public void resetCastDetailsBundle() { castDetailsBundle = new ArrayList<>(); } /** * Method which gets Cast Details savedState from our ArrayList. * We use it for our back navigation. */ public ArrayList<Bundle> getCastDetailsBundle() { return castDetailsBundle; } /** * Method which adds TV Details savedState to our ArrayList. * We use it for our back navigation. */ public void addTvDetailsBundle(Bundle tvDetailsBundle) { this.tvDetailsBundle.add(tvDetailsBundle); } /** * Method which removes TV Details savedState to our ArrayList. * We use it for our back navigation. */ public void removeTvDetailsBundle(int pos) { tvDetailsBundle.remove(pos); } /** * Method which resets TV Details ArrayList. * We use it for our back navigation. */ public void resetTvDetailsBundle() { tvDetailsBundle = new ArrayList<>(); } /** * Method which gets TV Details savedState from our ArrayList. * We use it for our back navigation. */ public ArrayList<Bundle> getTvDetailsBundle() { return tvDetailsBundle; } /** * Set this to true if we should restore our Movie Details savedState when we press back button. */ public void setRestoreMovieDetailsState(boolean restoreMovieDetailsState) { this.restoreMovieDetailsState = restoreMovieDetailsState; } /** * true if we should restore our Movie Details savedState when we press back button. */ public boolean getRestoreMovieDetailsState() { return restoreMovieDetailsState; } /** * Set this to true if we should restore our Movie Details Adapter savedState when we press back. */ public void setRestoreMovieDetailsAdapterState(boolean restoreMovieDetailsAdapterState) { this.restoreMovieDetailsAdapterState = restoreMovieDetailsAdapterState; } /** * true if we should restore our Movie Details Adapter savedState when we press back button. */ public boolean getRestoreMovieDetailsAdapterState() { return restoreMovieDetailsAdapterState; } /** * Set to true if the orientation has changed. */ public void setOrientationChanged(boolean orientationChanged) { this.orientationChanged = orientationChanged; } /** * Returns true if we have used our searchView and after that we have tapped on item from the searchView. * The fragment which is pushed checks for this. If this is true it starts counting incSearchMovieDetails() how many * components we have pushed so far from the searchView to the last component. * So if we click on the search view icon again. We clear the items we have pushed so far until the searchView, * but if we click backButton we have them stored and decSearchMovieDetails or Cast or TV is called. */ public boolean getSearchViewCount() { return searchViewCount; } /** * Set the value searchViewCount. True if we count. Check getSearchViewCount() for more information. */ public void setSearchViewCount(boolean searchViewCount) { this.searchViewCount = searchViewCount; } /** * Called from CastDetails, MovieDetails or TVDetails. * Check if we have used our search view and we are counting the components added to the backStack from the searchView until current. * Check getSearchViewCount() for more information. */ public void incSearchMovieDetails() { searchMovieDetails++; } /** * Decrements the value when we restore the fragment state. This is only called if we have used our search view. * Check getSearchViewCount() for more information. */ public void decSearchMovieDetails() { searchMovieDetails--; } /** * Same as incSearchMovieDetails() */ public void incSearchCastDetails() { searchCastDetails++; } /** * Same as decSearchMovieDetails() */ public void decSearchCastDetails() { searchCastDetails--; } /** * Same as incSearchMovieDetails() */ public void incSearchTvDetails() { searchTvDetails++; } /** * Same as decSearchMovieDetails() */ public void decSearchTvDetails() { searchTvDetails--; } /** * Called when we tap on the searchView icon. * Clears the item savedState we have pushed from the searchView until the end. * For example: MovieList -> MovieDetails -> CastDetails-> SearchView -> MovieDetails -> CastDetails -> MovieDetails. * This method will clear only MovieDetails savedState after the SearchView. * So searchMovieDetails will be 2 and will clear the last two MovieDetails savedStates. */ public void clearMovieDetailsBackStack() { if (movieDetailsBundle.size() > 0) { for (int i = 0; i < searchMovieDetails; i++) { removeMovieDetailsBundle(movieDetailsBundle.size() - 1); } } searchMovieDetails = 0; } /** * Same as clearMovieDetailsBackStack() */ public void clearCastDetailsBackStack() { if (castDetailsBundle.size() > 0) { for (int i = 0; i < searchCastDetails; i++) { removeCastDetailsBundle(castDetailsBundle.size() - 1); } } searchCastDetails = 0; } /** * Same as clearTvDetailsBackStack() */ public void clearTvDetailsBackStack() { if (tvDetailsBundle.size() > 0) { for (int i = 0; i < searchTvDetails; i++) { removeTvDetailsBundle(tvDetailsBundle.size() - 1); } } searchTvDetails = 0; } /** * Returns the MovieSlideTab Fragment. This is the ViewPager parent for the MovieList. */ public MovieSlideTab getMovieSlideTab() { return movieSlideTab; } public void setMovieSlideTab(MovieSlideTab movieSlideTab) { this.movieSlideTab = movieSlideTab; } /** * Returns the TVSlideTab Fragment. This is the ViewPager parent for the MovieList. */ public TVSlideTab getTvSlideTab() { return tvSlideTab; } public void setTvSlideTab(TVSlideTab tvSlideTab) { this.tvSlideTab = tvSlideTab; } public GenresList getGenresList() { return genresList; } /** * Returns our Toolbar. */ public Toolbar getToolbar() { return toolbar; } /** * Class which listens for suggestion events. */ private class SearchSuggestionListener implements SearchView.OnSuggestionListener { /** * Called when a suggestion was clicked. * * @param position the position * @return true if the listener handles the event and wants to override the default behavior of * launching any intent or submitting a search query specified on that item. Return false otherwise. * We don't want to launch any intent, so we handle this ourselve. */ @Override public boolean onSuggestionClick(int position) { Cursor cursor = (Cursor) searchView.getSuggestionsAdapter().getItem(position); if (searchView.getQuery().length() > 1) addSuggestion(cursor); searchList.onSuggestionClick(cursor.getInt(4), cursor.getString(5), cursor.getString(1)); return true; } /** * Called when a suggestion was selected by navigating to it. * * @param position the absolute position in the list of suggestions. * @return true if the listener handles the event and wants to override the default behavior of * launching any intent or submitting a search query specified on that item. Return false otherwise. */ public boolean onSuggestionSelect(int position) { Cursor cursor = (Cursor) searchView.getSuggestionsAdapter().getItem(position); if (searchView.getQuery().length() > 1) addSuggestion(cursor); searchList.onSuggestionClick(cursor.getInt(4), cursor.getString(5), cursor.getString(1)); return true; } } private void addSuggestion(Cursor cursor) { if (searchDB.getSuggestionSize() > 9) { searchDB.cleanSuggestionRecords(); } searchDB.insertSuggestion(cursor.getInt(4), cursor.getString(1), Uri.parse(cursor.getString(3)), cursor.getString(2), cursor.getString(5)); } /** * Called when we are on SearchView. We should clear our count. */ public void clearSearchCount() { searchMovieDetails = 0; searchCastDetails = 0; searchTvDetails = 0; } /** * Returns the old position of our navigation drawer. */ public int getOldPos() { return oldPos; } public int getLastVisitedSimMovie() { return lastVisitedSimMovie; } public void setLastVisitedSimMovie(int lastVisitedSimMovie) { this.lastVisitedSimMovie = lastVisitedSimMovie; } public int getLastVisitedSimTV() { return lastVisitedSimTV; } public void setLastVisitedSimTV(int lastVisitedSimTV) { this.lastVisitedSimTV = lastVisitedSimTV; } public boolean getSaveInMovieDetailsSimFragment() { return saveInMovieDetailsSimFragment; } public void setSaveInMovieDetailsSimFragment(boolean saveInMovieDetailsSimFragment) { this.saveInMovieDetailsSimFragment = saveInMovieDetailsSimFragment; } public MovieDetails getMovieDetailsSimFragment() { return movieDetailsSimFragment; } public void setMovieDetailsSimFragment(MovieDetails movieDetailsSimFragment) { this.movieDetailsSimFragment = movieDetailsSimFragment; } public boolean getSaveInTVDetailsSimFragment() { return saveInTVDetailsSimFragment; } public void setSaveInTVDetailsSimFragment(boolean saveInTVDetailsSimFragment) { this.saveInTVDetailsSimFragment = saveInTVDetailsSimFragment; } public TVDetails getTvDetailsSimFragment() { return tvDetailsSimFragment; } public void setTvDetailsSimFragment(TVDetails tvDetailsSimFragment) { this.tvDetailsSimFragment = tvDetailsSimFragment; } /** * This class handles the connection to our backend server. * If the connection is successful we set our list data. */ class JSONAsyncTask extends AsyncTask<String, Void, Boolean> { private ArrayList<Integer> idsList; private ArrayList<String> posterPathList; private String queryZ; public void setQuery(String query) { this.queryZ = query; } @Override protected void onPreExecute() { super.onPreExecute(); } @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 searchData = new JSONObject(sb.toString()); JSONArray searchResultsArray = searchData.getJSONArray("results"); int length = searchResultsArray.length(); if (length > 10) length = 10; searchDB.cleanAutoCompleteRecords(); idsList = new ArrayList<>(); posterPathList = new ArrayList<>(); for (int i = 0; i < length; i++) { JSONObject object = searchResultsArray.getJSONObject(i); int id = 0; String title = "", posterPath = "", releaseDate = "", mediaType = ""; if (object.has("id") && object.getInt("id") != 0) id = object.getInt("id"); if (object.has("title")) title = object.getString("title"); if (object.has("name")) title = object.getString("name"); title = title.replaceAll("'", ""); if (object.has("poster_path") && !object.getString("poster_path").equals("null") && !object.getString("poster_path").isEmpty()) posterPath = MovieDB.imageUrl + "w154" + object.getString("poster_path"); if (object.has("profile_path") && !object.getString("profile_path").equals("null") && !object.getString("profile_path").isEmpty()) posterPath = MovieDB.imageUrl + "w154" + object.getString("profile_path"); if (object.has("release_date") && !object.getString("release_date").equals("null") && !object.getString("release_date").isEmpty()) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US); try { Date date = sdf.parse(object.getString("release_date")); String formattedDate = dateFormat.format(date); releaseDate = "(" + formattedDate + ")"; } catch (java.text.ParseException e) { } } if (object.has("first_air_date") && !object.getString("first_air_date").equals("null") && !object.getString("first_air_date").isEmpty()) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US); try { Date date = sdf.parse(object.getString("first_air_date")); String formattedDate = dateFormat.format(date); releaseDate = "(" + formattedDate + ")"; } catch (java.text.ParseException e) { } } if (object.has("media_type") && !object.getString("media_type").isEmpty()) mediaType = object.getString("media_type"); Uri path = Uri.parse( "android.resource://de.sourcestream.movieDB/" + R.drawable.placeholder_default); if (!posterPath.isEmpty()) { if (imageLoader.getDiskCache().get(posterPath).exists()) path = Uri.fromFile(new File(imageLoader.getDiskCache().get(posterPath).getPath())); else { idsList.add(id); posterPathList.add(posterPath); } } searchDB.insertAutoComplete(id, title, path, releaseDate, mediaType); } return true; } } catch (ParseException | IOException | JSONException e) { if (conn != null) conn.disconnect(); } finally { if (conn != null) conn.disconnect(); } return false; } @Override protected void onPostExecute(Boolean result) { if (query.length() > 1) { searchAdapter.changeCursor(searchDB.autoComplete()); if (posterPathList != null && posterPathList.size() > 0) { for (int i = 0; i < posterPathList.size(); i++) { searchImgLoadingListener = new SearchImgLoadingListener(idsList.get(i), queryZ); imageLoader.loadImage(posterPathList.get(i), searchImgLoadingListener); } } } } } private class SearchImgLoadingListener extends SimpleImageLoadingListener { private int currId; private String queryZ; public SearchImgLoadingListener(int currId, String query) { this.currId = currId; this.queryZ = query; } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { if (query.equals(queryZ)) { Uri uriFile = Uri.fromFile(new File(imageLoader.getDiskCache().get(imageUri).getPath())); searchDB.updateImg(currId, uriFile); searchAdapter.changeCursor(searchDB.autoComplete()); } } } public void setLastVisitedMovieInCredits(int lastVisitedMovieInCredits) { this.lastVisitedMovieInCredits = lastVisitedMovieInCredits; } public int getLastVisitedMovieInCredits() { return lastVisitedMovieInCredits; } public int getIconMarginConstant() { return iconMarginConstant; } public int getIconMarginLandscape() { return iconMarginLandscape; } public int getIconConstantSpecialCase() { return iconConstantSpecialCase; } public DateFormat getDateFormat() { return dateFormat; } public int getOneIcon() { return oneIcon; } public int getOneIconToolbar() { return oneIconToolbar; } public int getTwoIcons() { return twoIcons; } public int getTwoIconsToolbar() { return twoIconsToolbar; } public int getThreeIcons() { return threeIcons; } public int getThreeIconsToolbar() { return threeIconsToolbar; } }