/**
* technoXist
*
* Copyright (c) 2014-2015 Suyash Bhatt
*
* 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/>.
*/
package com.technoxist.fragment;
import android.app.LoaderManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.support.v7.widget.SwitchCompat;
import android.widget.Toast;
import com.technoxist.Constants;
import com.technoxist.R;
import com.technoxist.adapter.EntriesCursorAdapter;
import com.technoxist.provider.FeedData;
import com.technoxist.provider.FeedData.EntryColumns;
import com.technoxist.provider.FeedDataContentProvider;
import com.technoxist.service.FetcherService;
import com.technoxist.utils.PrefUtils;
import java.util.Date;
public class EntriesListFragment extends SwipeRefreshListFragment {
private static final String STATE_URI = "STATE_URI";
private static final String STATE_SHOW_FEED_INFO = "STATE_SHOW_FEED_INFO";
private static final String STATE_LIST_DISPLAY_DATE = "STATE_LIST_DISPLAY_DATE";
private static final int ENTRIES_LOADER_ID = 1;
private static final int NEW_ENTRIES_NUMBER_LOADER_ID = 2;
private Uri mUri;
private boolean mShowFeedInfo = false;
private EntriesCursorAdapter mEntriesCursorAdapter;
private SearchView mSearchView;
private long mListDisplayDate = new Date().getTime();
private int mNewEntriesNumber, mOldUnreadEntriesNumber = -1;
private boolean mAutoRefreshDisplayDate = false;
private final OnSharedPreferenceChangeListener mPrefListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PrefUtils.SHOW_READ.equals(key)) {
getLoaderManager().restartLoader(ENTRIES_LOADER_ID, null, mEntriesLoader);
} else if (PrefUtils.IS_REFRESHING.equals(key)) {
refreshSwipeProgress();
}
}
};
private LoaderManager.LoaderCallbacks<Cursor> mEntriesLoader = new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String entriesOrder = PrefUtils.getBoolean(PrefUtils.DISPLAY_OLDEST_FIRST, false) ? Constants.DB_ASC : Constants.DB_DESC;
String where = "(" + EntryColumns.FETCH_DATE + Constants.DB_IS_NULL + Constants.DB_OR + EntryColumns.FETCH_DATE + "<=" + mListDisplayDate + ')';
if (!FeedData.shouldShowReadEntries(mUri)) {
where += Constants.DB_AND + EntryColumns.WHERE_UNREAD;
}
CursorLoader cursorLoader = new CursorLoader(getActivity(), mUri, null, where, null, EntryColumns.DATE + entriesOrder);
cursorLoader.setUpdateThrottle(150);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mEntriesCursorAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mEntriesCursorAdapter.swapCursor(Constants.EMPTY_CURSOR);
}
};
private LoaderManager.LoaderCallbacks<Cursor> mEntriesNumberLoader = new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader cursorLoader = new CursorLoader(getActivity(), mUri, new String[]{"SUM(" + EntryColumns.FETCH_DATE + '>' + mListDisplayDate + ")", "SUM(" + EntryColumns.FETCH_DATE + "<=" + mListDisplayDate + Constants.DB_AND + EntryColumns.WHERE_UNREAD + ")"}, null, null, null);
cursorLoader.setUpdateThrottle(150);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
data.moveToFirst();
mNewEntriesNumber = data.getInt(0);
mOldUnreadEntriesNumber = data.getInt(1);
if (mAutoRefreshDisplayDate && mNewEntriesNumber != 0 && mOldUnreadEntriesNumber == 0) {
mListDisplayDate = new Date().getTime();
getLoaderManager().restartLoader(ENTRIES_LOADER_ID, null, mEntriesLoader);
getLoaderManager().restartLoader(NEW_ENTRIES_NUMBER_LOADER_ID, null, mEntriesNumberLoader);
} else {
refreshUI();
}
mAutoRefreshDisplayDate = false;
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
};
private class SwipeGestureListener extends SimpleOnGestureListener implements OnTouchListener {
private final GestureDetector mGestureDetector;
public SwipeGestureListener(Context context) {
mGestureDetector = new GestureDetector(context, this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
setHasOptionsMenu(true);
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mUri = savedInstanceState.getParcelable(STATE_URI);
mShowFeedInfo = savedInstanceState.getBoolean(STATE_SHOW_FEED_INFO);
mListDisplayDate = savedInstanceState.getLong(STATE_LIST_DISPLAY_DATE);
mEntriesCursorAdapter = new EntriesCursorAdapter(getActivity(), mUri, Constants.EMPTY_CURSOR, mShowFeedInfo);
}
}
@Override
public void onStart() {
super.onStart();
refreshSwipeProgress();
PrefUtils.registerOnPrefChangeListener(mPrefListener);
if (mUri != null) {
// If the list is empty when we are going back here, try with the last display date
if (mNewEntriesNumber != 0 && mOldUnreadEntriesNumber == 0) {
mListDisplayDate = new Date().getTime();
} else {
mAutoRefreshDisplayDate = true; // We will try to update the list after if necessary
}
getLoaderManager().restartLoader(ENTRIES_LOADER_ID, null, mEntriesLoader);
getLoaderManager().restartLoader(NEW_ENTRIES_NUMBER_LOADER_ID, null, mEntriesNumberLoader);
}
}
@Override
public View inflateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_entry_list, container, true);
if (mEntriesCursorAdapter != null) {
setListAdapter(mEntriesCursorAdapter);
}
ListView mListView = (ListView) rootView.findViewById(android.R.id.list);
mListView.setFastScrollEnabled(false);
mListView.setOnTouchListener(new SwipeGestureListener(getActivity()));
mListView.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_up_right));
mSearchView = (SearchView) rootView.findViewById(R.id.searchView);
if (savedInstanceState != null) {
refreshUI(); // To hide/show the search bar
}
mSearchView.post(new Runnable() { // Do this AFTER the text has been restored from saveInstanceState
@Override
public void run() {
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
return false;
}
@Override
public boolean onQueryTextChange(String s) {
setData(EntryColumns.SEARCH_URI(s), true);
return false;
}
});
}
});
return rootView;
}
@Override
public void onStop() {
PrefUtils.unregisterOnPrefChangeListener(mPrefListener);
super.onStop();
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelable(STATE_URI, mUri);
outState.putBoolean(STATE_SHOW_FEED_INFO, mShowFeedInfo);
outState.putLong(STATE_LIST_DISPLAY_DATE, mListDisplayDate);
super.onSaveInstanceState(outState);
}
@Override
public void onRefresh() {
startRefresh();
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
startActivity(new Intent(Intent.ACTION_VIEW, ContentUris.withAppendedId(mUri, id)));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear(); // This is needed to remove a bug on Android 4.0.3
inflater.inflate(R.menu.entry_list, menu);
MenuItem switchItem = menu.findItem(R.id.switchRead);
View switchView = MenuItemCompat.getActionView(switchItem);
SwitchCompat mswitch = (SwitchCompat) switchView.findViewById(R.id.switch_unread);
mswitch.setThumbResource(R.drawable.switch_thumb);
mswitch.setTrackResource(R.drawable.switch_track);
if (!PrefUtils.getBoolean(PrefUtils.SHOW_READ, true)) {
mswitch.setChecked(true);
} else {
mswitch.setChecked(false);
}
mswitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// The switch is enabled
PrefUtils.putBoolean(PrefUtils.SHOW_READ, false);
Toast.makeText(getActivity(), R.string.show_unread, Toast.LENGTH_SHORT).show();
} else {
// The toggle is disabled
PrefUtils.putBoolean(PrefUtils.SHOW_READ, true);
Toast.makeText(getActivity(), R.string.show_all, Toast.LENGTH_SHORT).show();
}
}
});
if (EntryColumns.FAVORITES_CONTENT_URI.equals(mUri)) {
menu.findItem(R.id.menu_refresh).setVisible(false);
} else if (mUri != null && FeedDataContentProvider.URI_MATCHER.match(mUri) == FeedDataContentProvider.URI_SEARCH) {
menu.findItem(R.id.menu_share_starred).setVisible(false);
} else {
menu.findItem(R.id.menu_share_starred).setVisible(false);
}
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_share_starred: {
String starredList = "";
Cursor cursor = mEntriesCursorAdapter.getCursor();
if (cursor != null && !cursor.isClosed()) {
int titlePos = cursor.getColumnIndex(EntryColumns.TITLE);
int linkPos = cursor.getColumnIndex(EntryColumns.LINK);
if (cursor.moveToFirst()) {
do {
starredList += cursor.getString(titlePos) + "\n" + cursor.getString(linkPos) + "\n\n";
} while (cursor.moveToNext());
}
startActivity(Intent.createChooser(
new Intent(Intent.ACTION_SEND).putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_favorites_title))
.putExtra(Intent.EXTRA_TEXT, starredList).setType(Constants.MIMETYPE_TEXT_PLAIN), getString(R.string.menu_share)
));
}
return true;
}
case R.id.menu_refresh: {
startRefresh();
return true;
}
case R.id.menu_all_read: {
mEntriesCursorAdapter.markAllAsRead(mListDisplayDate);
// If we are on "all items" uri, we can remove the notification here
if (EntryColumns.CONTENT_URI.equals(mUri) && Constants.NOTIF_MGR != null) {
Constants.NOTIF_MGR.cancel(0);
}
return true;
}
}
return super.onOptionsItemSelected(item);
}
private void startRefresh() {
if (!PrefUtils.getBoolean(PrefUtils.IS_REFRESHING, false)) {
if (FeedDataContentProvider.URI_MATCHER.match(mUri) == FeedDataContentProvider.URI_ENTRIES_FOR_FEED) {
getActivity().startService(new Intent(getActivity(), FetcherService.class).setAction(FetcherService.ACTION_REFRESH_FEEDS).putExtra(Constants.FEED_ID,
mUri.getPathSegments().get(1)));
} else {
getActivity().startService(new Intent(getActivity(), FetcherService.class).setAction(FetcherService.ACTION_REFRESH_FEEDS));
}
}
}
public Uri getUri() {
return mUri;
}
public String getCurrentSearch() {
return mSearchView == null ? null : mSearchView.getQuery().toString();
}
public void setData(Uri uri, boolean showFeedInfo) {
mUri = uri;
mShowFeedInfo = showFeedInfo;
mEntriesCursorAdapter = new EntriesCursorAdapter(getActivity(), mUri, Constants.EMPTY_CURSOR, mShowFeedInfo);
setListAdapter(mEntriesCursorAdapter);
mListDisplayDate = new Date().getTime();
if (mUri != null) {
getLoaderManager().restartLoader(ENTRIES_LOADER_ID, null, mEntriesLoader);
getLoaderManager().restartLoader(NEW_ENTRIES_NUMBER_LOADER_ID, null, mEntriesNumberLoader);
}
refreshUI();
}
private void refreshUI() {
if (mUri != null && FeedDataContentProvider.URI_MATCHER.match(mUri) == FeedDataContentProvider.URI_SEARCH) {
mSearchView.setVisibility(View.VISIBLE);
} else {
mSearchView.setVisibility(View.GONE);
}
if (mUri != null && FeedDataContentProvider.URI_MATCHER.match(mUri) != FeedDataContentProvider.URI_FAVORITES) {
enableSwipe();
} else {
disableSwipe();
}
if (mNewEntriesNumber > 0) {
mNewEntriesNumber = 0;
mListDisplayDate = new Date().getTime();
refreshUI();
if (mUri != null) {
getLoaderManager().restartLoader(ENTRIES_LOADER_ID, null, mEntriesLoader);
getLoaderManager().restartLoader(NEW_ENTRIES_NUMBER_LOADER_ID, null, mEntriesNumberLoader);
}
}
}
private void refreshSwipeProgress() {
if (PrefUtils.getBoolean(PrefUtils.IS_REFRESHING, false)) {
showSwipeProgress();
} else {
hideSwipeProgress();
}
}
}