co.nerdart.ourss.activity.EditFeedActivity.java Source code

Java tutorial

Introduction

Here is the source code for co.nerdart.ourss.activity.EditFeedActivity.java

Source

/**
 * OURSS was forked from FeedEx, which used Sparse RSS.
 *
 *
 *
 *     --> FeedEx --> Copyright (c) 2012-2013 Frederic Julian
 *
 *     This program 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.
 *
 *     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Some parts of this software are based on "Sparse rss" under the MIT license (see
 * below). Please refers to the original project to identify which parts are under the
 * MIT license.
 *
 * Copyright (c) 2010-2012 Stefan Handschuh
 *
 *     Permission is hereby granted, free of charge, to any person obtaining a copy
 *     of this software and associated documentation files (the "Software"), to deal
 *     in the Software without restriction, including without limitation the rights
 *     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *     copies of the Software, and to permit persons to whom the Software is
 *     furnished to do so, subject to the following conditions:
 *
 *     The above copyright notice and this permission notice shall be included in
 *     all copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *     THE SOFTWARE.
 */

package co.nerdart.ourss.activity;

import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.ProgressDialog;
import android.content.AsyncTaskLoader;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.text.Html;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SimpleAdapter;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;

import co.nerdart.ourss.Constants;
import co.nerdart.ourss.NetworkUtils;
import co.nerdart.ourss.R;
import co.nerdart.ourss.UiUtils;
import co.nerdart.ourss.adapter.FiltersCursorAdapter;
import co.nerdart.ourss.provider.FeedData.FeedColumns;
import co.nerdart.ourss.provider.FeedData.FilterColumns;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;

public class EditFeedActivity extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    static final String FEED_SEARCH_TITLE = "title";
    static final String FEED_SEARCH_URL = "url";
    static final String FEED_SEARCH_DESC = "contentSnippet";

    private static final String[] FEED_PROJECTION = new String[] { FeedColumns.NAME, FeedColumns.URL,
            FeedColumns.RETRIEVE_FULLTEXT };

    private EditText mNameEditText, mUrlEditText;
    private CheckBox mRetrieveFulltextCb;
    private ListView mFiltersListView;
    private String mPreviousName;

    private FiltersCursorAdapter mFiltersCursorAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        UiUtils.setPreferenceTheme(this);
        super.onCreate(savedInstanceState);

        ActionBar actionBar = getActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);

        setContentView(R.layout.feed_edit);
        setResult(RESULT_CANCELED);

        Intent intent = getIntent();

        mNameEditText = (EditText) findViewById(R.id.feed_title);
        mUrlEditText = (EditText) findViewById(R.id.feed_url);
        mRetrieveFulltextCb = (CheckBox) findViewById(R.id.retrieve_fulltext);
        mFiltersListView = (ListView) findViewById(android.R.id.list);
        View filtersLayout = findViewById(R.id.filters_layout);
        View buttonLayout = findViewById(R.id.button_layout);

        if (intent.getAction().equals(Intent.ACTION_INSERT) || intent.getAction().equals(Intent.ACTION_SEND)) {
            setTitle(R.string.new_feed_title);

            filtersLayout.setVisibility(View.GONE);

            if (intent.hasExtra(Intent.EXTRA_TEXT)) {
                mUrlEditText.setText(intent.getStringExtra(Intent.EXTRA_TEXT));
            }

            restoreInstanceState(savedInstanceState);
        } else if (intent.getAction().equals(Intent.ACTION_EDIT)) {
            setTitle(R.string.edit_feed_title);

            buttonLayout.setVisibility(View.GONE);

            mFiltersCursorAdapter = new FiltersCursorAdapter(this, null);
            mFiltersListView.setAdapter(mFiltersCursorAdapter);
            mFiltersListView.setOnItemLongClickListener(new OnItemLongClickListener() {
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                    startActionMode(mFilterActionModeCallback);
                    mFiltersCursorAdapter.setSelectedFilter(position);
                    mFiltersListView.invalidateViews();
                    return true;
                }
            });

            getLoaderManager().initLoader(0, null, this);

            if (!restoreInstanceState(savedInstanceState)) {
                Cursor cursor = getContentResolver().query(intent.getData(), FEED_PROJECTION, null, null, null);

                if (cursor.moveToNext()) {
                    mPreviousName = cursor.getString(0);
                    mNameEditText.setText(mPreviousName);
                    mUrlEditText.setText(cursor.getString(1));
                    mRetrieveFulltextCb.setChecked(cursor.getInt(2) == 1);
                    cursor.close();
                } else {
                    cursor.close();
                    Crouton.makeText(EditFeedActivity.this, R.string.error, Style.INFO);
                    finish();
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        if (getIntent().getAction().equals(Intent.ACTION_EDIT)) {
            String url = mUrlEditText.getText().toString();
            ContentResolver cr = getContentResolver();

            Cursor cursor = getContentResolver().query(FeedColumns.CONTENT_URI, FeedColumns.PROJECTION_ID,
                    FeedColumns.URL + Constants.DB_ARG, new String[] { url }, null);

            if (cursor.moveToFirst() && !getIntent().getData().getLastPathSegment().equals(cursor.getString(0))) {
                cursor.close();
                //Toast.makeText(EditFeedActivity.this, R.string.error_feed_url_exists,
                //      Toast.LENGTH_LONG).show();
                Crouton.makeText(EditFeedActivity.this, R.string.error_feed_url_exists, Style.INFO);
            } else {
                cursor.close();
                ContentValues values = new ContentValues();

                if (!url.startsWith(Constants.HTTP) && !url.startsWith(Constants.HTTPS)) {
                    url = Constants.HTTP + url;
                }
                values.put(FeedColumns.URL, url);

                String name = mNameEditText.getText().toString();

                values.put(FeedColumns.NAME, name.trim().length() > 0 ? name : null);
                values.put(FeedColumns.RETRIEVE_FULLTEXT, mRetrieveFulltextCb.isChecked() ? 1 : null);
                values.put(FeedColumns.FETCH_MODE, 0);
                values.putNull(FeedColumns.ERROR);

                cr.update(getIntent().getData(), values, null, null);
                if (!name.equals(mPreviousName)) {
                    cr.notifyChange(FeedColumns.GROUPS_CONTENT_URI, null);
                }
            }
        }

        super.onDestroy();
    }

    private boolean restoreInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            mNameEditText.setText(savedInstanceState.getCharSequence(FeedColumns.NAME));
            mUrlEditText.setText(savedInstanceState.getCharSequence(FeedColumns.URL));
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putCharSequence(FeedColumns.NAME, mNameEditText.getText());
        outState.putCharSequence(FeedColumns.URL, mUrlEditText.getText());
    }

    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            finish();
            return true;
        }

        return true;
    }

    public void onClickAddFilter(View view) {
        final View dialogView = getLayoutInflater().inflate(R.layout.filter_edit, null);

        new AlertDialog.Builder(this) //
                .setTitle(R.string.filter_add_title) //
                .setView(dialogView) //
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        String filterText = ((EditText) dialogView.findViewById(R.id.filterText)).getText()
                                .toString();
                        if (filterText.length() != 0) {
                            String feedId = getIntent().getData().getLastPathSegment();

                            ContentValues values = new ContentValues();
                            values.put(FilterColumns.FILTER_TEXT, filterText);
                            values.put(FilterColumns.IS_REGEX,
                                    ((CheckBox) dialogView.findViewById(R.id.regexCheckBox)).isChecked());
                            values.put(FilterColumns.IS_APPLIED_TO_TITLE,
                                    ((RadioButton) dialogView.findViewById(R.id.applyTitleRadio)).isChecked());

                            ContentResolver cr = getContentResolver();
                            cr.insert(FilterColumns.FILTERS_FOR_FEED_CONTENT_URI(feedId), values);
                        }
                    }
                }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                    }
                }).show();
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        CursorLoader cursorLoader = new CursorLoader(this,
                FilterColumns.FILTERS_FOR_FEED_CONTENT_URI(getIntent().getData().getLastPathSegment()), null, null,
                null, null);
        cursorLoader.setUpdateThrottle(Constants.UPDATE_THROTTLE_DELAY);
        return cursorLoader;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mFiltersCursorAdapter.changeCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mFiltersCursorAdapter.changeCursor(null);
    }

    public void onClickOk(View view) {
        // only in insert mode

        String url = mUrlEditText.getText().toString();
        ContentResolver cr = getContentResolver();

        if (!url.startsWith(Constants.HTTP) && !url.startsWith(Constants.HTTPS)) {
            url = Constants.HTTP + url;
        }

        Cursor cursor = cr.query(FeedColumns.CONTENT_URI, null, FeedColumns.URL + Constants.DB_ARG,
                new String[] { url }, null);

        if (cursor.moveToFirst()) {
            cursor.close();
            Crouton.makeText(EditFeedActivity.this, R.string.error_feed_url_exists, Style.INFO);
        } else {
            cursor.close();
            ContentValues values = new ContentValues();

            values.put(FeedColumns.URL, url);
            values.putNull(FeedColumns.ERROR);

            String name = mNameEditText.getText().toString();

            if (name.trim().length() > 0) {
                values.put(FeedColumns.NAME, name);
            }
            values.put(FeedColumns.RETRIEVE_FULLTEXT, mRetrieveFulltextCb.isChecked() ? 1 : null);
            cr.insert(FeedColumns.CONTENT_URI, values);
            cr.notifyChange(FeedColumns.GROUPS_CONTENT_URI, null);
        }

        setResult(RESULT_OK);
        finish();
    }

    public void onClickCancel(View view) {
        finish();
    }

    private final ActionMode.Callback mFilterActionModeCallback = new ActionMode.Callback() {

        // Called when the action mode is created; startActionMode() was called
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // Inflate a menu resource providing context menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.edit_context_menu, menu);
            return true;
        }

        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false; // Return false if nothing is done
        }

        // Called when the user selects a contextual menu item
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {

            switch (item.getItemId()) {
            case R.id.menu_edit:
                Cursor c = mFiltersCursorAdapter.getCursor();
                if (c.moveToPosition(mFiltersCursorAdapter.getSelectedFilter())) {
                    final View dialogView = getLayoutInflater().inflate(R.layout.filter_edit, null);
                    final EditText filterText = (EditText) dialogView.findViewById(R.id.filterText);
                    final CheckBox regexCheckBox = (CheckBox) dialogView.findViewById(R.id.regexCheckBox);
                    final RadioButton applyTitleRadio = (RadioButton) dialogView.findViewById(R.id.applyTitleRadio);
                    final RadioButton applyContentRadio = (RadioButton) dialogView
                            .findViewById(R.id.applyContentRadio);

                    filterText.setText(c.getString(c.getColumnIndex(FilterColumns.FILTER_TEXT)));
                    regexCheckBox.setChecked(c.getInt(c.getColumnIndex(FilterColumns.IS_REGEX)) == 1);
                    if (c.getInt(c.getColumnIndex(FilterColumns.IS_APPLIED_TO_TITLE)) == 1) {
                        applyTitleRadio.setChecked(true);
                    } else {
                        applyContentRadio.setChecked(true);
                    }

                    final long filterId = mFiltersCursorAdapter
                            .getItemId(mFiltersCursorAdapter.getSelectedFilter());
                    new AlertDialog.Builder(EditFeedActivity.this) //
                            .setTitle(R.string.filter_edit_title) //
                            .setView(dialogView) //
                            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    new Thread() {
                                        @Override
                                        public void run() {
                                            String filter = filterText.getText().toString();
                                            if (!filter.isEmpty()) {
                                                ContentResolver cr = getContentResolver();
                                                ContentValues values = new ContentValues();
                                                values.put(FilterColumns.FILTER_TEXT, filter);
                                                values.put(FilterColumns.IS_REGEX, regexCheckBox.isChecked());
                                                values.put(FilterColumns.IS_APPLIED_TO_TITLE,
                                                        applyTitleRadio.isChecked());
                                                if (cr.update(FilterColumns.CONTENT_URI, values,
                                                        FilterColumns._ID + '=' + filterId, null) > 0) {
                                                    cr.notifyChange(
                                                            FilterColumns.FILTERS_FOR_FEED_CONTENT_URI(
                                                                    getIntent().getData().getLastPathSegment()),
                                                            null);
                                                }
                                            }
                                        }
                                    }.start();
                                }
                            }).setNegativeButton(android.R.string.cancel, null).show();
                }

                mode.finish(); // Action picked, so close the CAB
                return true;
            case R.id.menu_delete:
                final long filterId = mFiltersCursorAdapter.getItemId(mFiltersCursorAdapter.getSelectedFilter());
                new AlertDialog.Builder(EditFeedActivity.this) //
                        .setIcon(android.R.drawable.ic_dialog_alert) //
                        .setTitle(R.string.filter_delete_title) //
                        .setMessage(R.string.question_delete_filter) //
                        .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                new Thread() {
                                    @Override
                                    public void run() {
                                        ContentResolver cr = getContentResolver();
                                        if (cr.delete(FilterColumns.CONTENT_URI, FilterColumns._ID + '=' + filterId,
                                                null) > 0) {
                                            cr.notifyChange(FilterColumns.FILTERS_FOR_FEED_CONTENT_URI(
                                                    getIntent().getData().getLastPathSegment()), null);
                                        }
                                    }
                                }.start();
                            }
                        }).setNegativeButton(android.R.string.no, null).show();

                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
            }
        }

        // Called when the user exits the action mode
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            mFiltersCursorAdapter.setSelectedFilter(-1);
            mFiltersListView.invalidateViews();
        }
    };

    public void onClickSearch(View view) {
        final View dialogView = getLayoutInflater().inflate(R.layout.search_feed, null);
        final EditText searchText = (EditText) dialogView.findViewById(R.id.searchText);
        final RadioGroup radioGroup = (RadioGroup) dialogView.findViewById(R.id.radioGroup);

        new AlertDialog.Builder(EditFeedActivity.this) //
                .setIcon(R.drawable.action_search) //
                .setTitle(R.string.feed_search) //
                .setView(dialogView) //
                .setPositiveButton(android.R.string.search_go, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        if (searchText.getText().length() > 0) {
                            String tmp = searchText.getText().toString();
                            try {
                                tmp = URLEncoder.encode(searchText.getText().toString(), Constants.UTF8);
                            } catch (UnsupportedEncodingException ignored) {
                            }
                            final String text = tmp;

                            switch (radioGroup.getCheckedRadioButtonId()) {
                            case R.id.byWebSearch:
                                final ProgressDialog pd = new ProgressDialog(EditFeedActivity.this);
                                pd.setMessage(getString(R.string.loading));
                                pd.setCancelable(true);
                                pd.setIndeterminate(true);
                                pd.show();

                                getLoaderManager().restartLoader(1, null,
                                        new LoaderCallbacks<ArrayList<HashMap<String, String>>>() {

                                            @Override
                                            public Loader<ArrayList<HashMap<String, String>>> onCreateLoader(int id,
                                                    Bundle args) {
                                                return new GetFeedSearchResultsLoader(EditFeedActivity.this, text);
                                            }

                                            @Override
                                            public void onLoadFinished(
                                                    Loader<ArrayList<HashMap<String, String>>> loader,
                                                    final ArrayList<HashMap<String, String>> data) {
                                                pd.cancel();

                                                if (data == null) {
                                                    Crouton.makeText(EditFeedActivity.this, R.string.error,
                                                            Style.INFO);
                                                } else if (data.isEmpty()) {
                                                    Crouton.makeText(EditFeedActivity.this, R.string.no_result,
                                                            Style.INFO);
                                                } else {
                                                    AlertDialog.Builder builder = new AlertDialog.Builder(
                                                            EditFeedActivity.this);
                                                    builder.setTitle(R.string.feed_search);

                                                    // create the grid item mapping
                                                    String[] from = new String[] { FEED_SEARCH_TITLE,
                                                            FEED_SEARCH_DESC };
                                                    int[] to = new int[] { android.R.id.text1, android.R.id.text2 };

                                                    // fill in the grid_item layout
                                                    SimpleAdapter adapter = new SimpleAdapter(EditFeedActivity.this,
                                                            data, R.layout.search_result_item, from, to);
                                                    builder.setAdapter(adapter,
                                                            new DialogInterface.OnClickListener() {
                                                                @Override
                                                                public void onClick(DialogInterface dialog,
                                                                        int which) {
                                                                    mNameEditText.setText(
                                                                            data.get(which).get(FEED_SEARCH_TITLE));
                                                                    mUrlEditText.setText(
                                                                            data.get(which).get(FEED_SEARCH_URL));
                                                                }
                                                            });
                                                    builder.show();
                                                }
                                            }

                                            @Override
                                            public void onLoaderReset(
                                                    Loader<ArrayList<HashMap<String, String>>> loader) {
                                            }
                                        }).forceLoad();
                                break;

                            case R.id.byTopic:
                                mUrlEditText.setText("http://www.faroo.com/api?q=" + text
                                        + "&start=1&length=10&l=en&src=news&f=rss");
                                break;

                            case R.id.byYoutube:
                                mUrlEditText.setText("http://www.youtube.com/rss/user/" + text.replaceAll("\\+", "")
                                        + "/videos.rss");
                                break;
                            }
                        }
                    }
                }).setNegativeButton(android.R.string.cancel, null).show();
    }
}

/**
 * A custom Loader that loads feed search results from the google WS.
 */
class GetFeedSearchResultsLoader extends AsyncTaskLoader<ArrayList<HashMap<String, String>>> {

    private final String mSearchText;

    public GetFeedSearchResultsLoader(Context context, String searchText) {
        super(context);
        mSearchText = searchText;
    }

    /**
     * This is where the bulk of our work is done. This function is called in a background thread and should generate a new set of data to be
     * published by the loader.
     */
    @Override
    public ArrayList<HashMap<String, String>> loadInBackground() {
        try {
            HttpURLConnection conn = NetworkUtils
                    .setupConnection("https://ajax.googleapis.com/ajax/services/feed/find?v=1.0&q=" + mSearchText);
            try {
                String jsonStr = new String(NetworkUtils.getBytes(NetworkUtils.getConnectionInputStream(conn)));

                // Parse results
                final ArrayList<HashMap<String, String>> results = new ArrayList<HashMap<String, String>>();
                JSONObject response = new JSONObject(jsonStr).getJSONObject("responseData");
                JSONArray entries = response.getJSONArray("entries");
                for (int i = 0; i < entries.length(); i++) {
                    try {
                        JSONObject entry = (JSONObject) entries.get(i);
                        String url = entry.get(EditFeedActivity.FEED_SEARCH_URL).toString();
                        if (!url.isEmpty()) {
                            HashMap<String, String> map = new HashMap<String, String>();
                            map.put(EditFeedActivity.FEED_SEARCH_TITLE, Html
                                    .fromHtml(entry.get(EditFeedActivity.FEED_SEARCH_TITLE).toString()).toString());
                            map.put(EditFeedActivity.FEED_SEARCH_URL, url);
                            map.put(EditFeedActivity.FEED_SEARCH_DESC, Html
                                    .fromHtml(entry.get(EditFeedActivity.FEED_SEARCH_DESC).toString()).toString());

                            results.add(map);
                        }
                    } catch (Exception ignored) {
                    }
                }

                return results;
            } finally {
                conn.disconnect();
            }
        } catch (Exception e) {
            return null;
        }
    }
}