org.tvheadend.tvhclient.SearchResultActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.tvheadend.tvhclient.SearchResultActivity.java

Source

/*
 *  Copyright (C) 2013 Robert Siebert
 *  Copyright (C) 2011 John Trnblom
 *
 * This file is part of TVHClient.
 *
 * TVHClient is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TVHClient is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with TVHClient.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.tvheadend.tvhclient;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.tvheadend.tvhclient.adapter.SearchResultAdapter;
import org.tvheadend.tvhclient.fragments.ProgramDetailsFragment;
import org.tvheadend.tvhclient.htsp.HTSService;
import org.tvheadend.tvhclient.intent.SearchEPGIntent;
import org.tvheadend.tvhclient.intent.SearchIMDbIntent;
import org.tvheadend.tvhclient.interfaces.HTSListener;
import org.tvheadend.tvhclient.model.Channel;
import org.tvheadend.tvhclient.model.Program;
import org.tvheadend.tvhclient.model.Recording;

import android.app.SearchManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.SearchRecentSuggestions;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;

public class SearchResultActivity extends ActionBarActivity implements HTSListener {

    @SuppressWarnings("unused")
    private final static String TAG = SearchResultActivity.class.getSimpleName();

    private ActionBar actionBar = null;
    private SearchResultAdapter adapter;
    private ListView listView;
    private Pattern pattern;
    private Channel channel;
    // The currently selected program
    private Program program;

    private Runnable updateTask;
    private Handler updateHandler = new Handler();

    @Override
    public void onCreate(Bundle icicle) {
        setTheme(Utils.getThemeId(this));
        super.onCreate(icicle);
        setContentView(R.layout.list_layout);

        // Setup the action bar and show the title
        actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setHomeButtonEnabled(true);
        actionBar.setTitle(R.string.menu_search);
        actionBar.setSubtitle(getIntent().getStringExtra(SearchManager.QUERY));

        listView = (ListView) findViewById(R.id.item_list);
        registerForContextMenu(listView);

        List<Program> list = new ArrayList<Program>();
        adapter = new SearchResultAdapter(this, list);
        adapter.sort();
        listView.setAdapter(adapter);

        // Show the details of the program when the user has selected one
        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                program = adapter.getItem(position);
                if (program != null) {
                    Bundle args = new Bundle();
                    args.putLong(Constants.BUNDLE_PROGRAM_ID, program.id);
                    args.putLong(Constants.BUNDLE_CHANNEL_ID, program.channel.id);
                    args.putBoolean(Constants.BUNDLE_DUAL_PANE, false);
                    args.putBoolean(Constants.BUNDLE_SHOW_CONTROLS, true);
                    // Create the fragment and show it as a dialog.
                    DialogFragment newFragment = ProgramDetailsFragment.newInstance(args);
                    newFragment.show(getSupportFragmentManager(), "dialog");
                }
            }
        });

        onNewIntent(getIntent());
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        // Quit if the search mode is not active
        if (!Intent.ACTION_SEARCH.equals(intent.getAction()) || !intent.hasExtra(SearchManager.QUERY)) {
            return;
        }

        // Get the possible channel
        TVHClientApplication app = (TVHClientApplication) getApplication();
        Bundle bundle = intent.getBundleExtra(SearchManager.APP_DATA);
        if (bundle != null) {
            channel = app.getChannel(bundle.getLong(Constants.BUNDLE_CHANNEL_ID));
        } else {
            channel = null;
        }

        // Create the intent with the search options 
        String query = intent.getStringExtra(SearchManager.QUERY);
        pattern = Pattern.compile(query, Pattern.CASE_INSENSITIVE);
        intent = new Intent(SearchResultActivity.this, HTSService.class);
        intent.setAction(Constants.ACTION_EPG_QUERY);
        intent.putExtra("query", query);
        if (channel != null) {
            intent.putExtra(Constants.BUNDLE_CHANNEL_ID, channel.id);
        }

        // Save the query so it can be shown again
        SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, SuggestionProvider.AUTHORITY,
                SuggestionProvider.MODE);
        suggestions.saveRecentQuery(query, null);

        // Now call the service with the query to get results
        startService(intent);

        // Clear the previous results before adding new ones
        adapter.clear();

        // If no channel is given go through the list of programs in all
        // channels and search if the desired program exists. If a channel was
        // given search through the list of programs in the given channel. 
        if (channel == null) {
            for (Channel ch : app.getChannels()) {
                if (ch != null) {
                    synchronized (ch.epg) {
                        for (Program p : ch.epg) {
                            if (p != null && p.title != null && p.title.length() > 0) {
                                // Check if the program name matches the search pattern
                                if (pattern.matcher(p.title).find()) {
                                    adapter.add(p);
                                    adapter.sort();
                                    adapter.notifyDataSetChanged();
                                }
                            }
                        }
                    }
                }
            }
        } else {
            if (channel.epg != null) {
                synchronized (channel.epg) {
                    for (Program p : channel.epg) {
                        if (p != null && p.title != null && p.title.length() > 0) {
                            // Check if the program name matches the search pattern
                            if (pattern.matcher(p.title).find()) {
                                adapter.add(p);
                                adapter.sort();
                                adapter.notifyDataSetChanged();
                            }
                        }
                    }
                }
            }
        }

        actionBar.setTitle(android.R.string.search_go);
        actionBar.setSubtitle(getString(R.string.loading));

        // Create the runnable that will initiate the update of the adapter and
        // indicates that we are done when nothing has happened after 2s. 
        updateTask = new Runnable() {
            public void run() {
                adapter.notifyDataSetChanged();
                actionBar.setSubtitle(adapter.getCount() + " " + getString(R.string.results));
            }
        };
    }

    @Override
    protected void onResume() {
        super.onResume();
        TVHClientApplication app = (TVHClientApplication) getApplication();
        app.addListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        TVHClientApplication app = (TVHClientApplication) getApplication();
        app.removeListener(this);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // Hide the genre color menu if no genre colors shall be shown
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        final boolean showGenreColors = prefs.getBoolean("showGenreColorsSearchPref", false);
        (menu.findItem(R.id.menu_genre_color_info)).setVisible(showGenreColors);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.search_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            onBackPressed();
            return true;

        case R.id.menu_search:
            onSearchRequested();
            return true;

        case R.id.menu_genre_color_info:
            Utils.showGenreColorDialog(this);
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_search_imdb:
            startActivity(new SearchIMDbIntent(this, program.title));
            return true;

        case R.id.menu_search_epg:
            startActivity(new SearchEPGIntent(this, program.title));
            return true;

        case R.id.menu_record_remove:
            Utils.confirmRemoveRecording(this, program.recording);
            return true;

        case R.id.menu_record_cancel:
            Utils.confirmCancelRecording(this, program.recording);
            return true;

        case R.id.menu_record_once:
            Utils.recordProgram(this, program, false);
            return true;

        case R.id.menu_record_series:
            Utils.recordProgram(this, program, true);
            return true;

        default:
            return super.onContextItemSelected(item);
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        getMenuInflater().inflate(R.menu.program_context_menu, menu);
        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
        program = adapter.getItem(info.position);

        // Set the title of the context menu
        if (program != null) {
            menu.setHeaderTitle(program.title);
        }
        //  Show or hide the menu items depending on the program state
        Utils.setProgramMenu(menu, program);
    }

    /**
     * Starts a timer that will update the search list view when expired. If
     * this method is called while the timer is running, the timer will be
     * restarted. This will prevent calls adapter.notifyDataSetChanged() until
     * all data has been loaded and nothing has happened for 2s.
     */
    private void startDelayedAdapterUpdate() {
        updateHandler.removeCallbacks(updateTask);
        updateHandler.postDelayed(updateTask, 2000);

        // Show that we are still loading
        actionBar.setSubtitle(getString(R.string.loading) + "... (" + adapter.getCount() + " "
                + getString(R.string.results) + ")");
    }

    /**
     * This method is part of the HTSListener interface. Whenever the HTSService
     * sends a new message the correct action will then be executed here.
     */
    @Override
    public void onMessage(String action, final Object obj) {
        if (action.equals(Constants.ACTION_PROGRAM_ADD)) {
            runOnUiThread(new Runnable() {
                public void run() {
                    Program p = (Program) obj;
                    if (p != null && p.title != null && p.title.length() > 0) {
                        if (pattern != null && pattern.matcher(p.title).find()) {
                            adapter.add(p);
                            adapter.sort();
                            startDelayedAdapterUpdate();
                        }
                    }
                }
            });
        } else if (action.equals(Constants.ACTION_PROGRAM_DELETE)) {
            runOnUiThread(new Runnable() {
                public void run() {
                    Program p = (Program) obj;
                    adapter.remove(p);
                    startDelayedAdapterUpdate();
                }
            });
        } else if (action.equals(Constants.ACTION_PROGRAM_UPDATE)) {
            runOnUiThread(new Runnable() {
                public void run() {
                    adapter.update((Program) obj);
                    startDelayedAdapterUpdate();
                }
            });
        } else if (action.equals(Constants.ACTION_DVR_ADD) || action.equals(Constants.ACTION_DVR_DELETE)
                || action.equals(Constants.ACTION_DVR_UPDATE)) {
            runOnUiThread(new Runnable() {
                public void run() {
                    Recording rec = (Recording) obj;
                    for (Program p : adapter.getList()) {
                        if (rec == p.recording) {
                            adapter.update(p);
                            startDelayedAdapterUpdate();
                            return;
                        }
                    }
                }
            });
        }
    }
}