/* * Firetweet - Twitter client for Android * * Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com> * * 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 org.getlantern.firetweet.fragment.support; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v4.content.Loader; import com.squareup.otto.Subscribe; import org.getlantern.querybuilder.Columns.Column; import org.getlantern.querybuilder.Expression; import org.getlantern.querybuilder.RawItemArray; import org.getlantern.firetweet.adapter.CursorStatusesAdapter; import org.getlantern.firetweet.loader.support.ExtendedCursorLoader; import org.getlantern.firetweet.provider.FiretweetDataStore.Accounts; import org.getlantern.firetweet.provider.FiretweetDataStore.Statuses; import org.getlantern.firetweet.util.AsyncTaskUtils; import org.getlantern.firetweet.util.Utils; import org.getlantern.firetweet.util.message.FavoriteCreatedEvent; import org.getlantern.firetweet.util.message.FavoriteDestroyedEvent; import org.getlantern.firetweet.util.message.GetStatusesTaskEvent; import org.getlantern.firetweet.util.message.StatusDestroyedEvent; import org.getlantern.firetweet.util.message.StatusListChangedEvent; import org.getlantern.firetweet.util.message.StatusRetweetedEvent; import static org.getlantern.firetweet.util.Utils.buildStatusFilterWhereClause; import static org.getlantern.firetweet.util.Utils.getNewestStatusIdsFromDatabase; import static org.getlantern.firetweet.util.Utils.getOldestStatusIdsFromDatabase; import static org.getlantern.firetweet.util.Utils.getTableNameByUri; /** * Created by mariotaku on 14/12/3. */ public abstract class CursorStatusesFragment extends AbsStatusesFragment<Cursor> { @Override protected void onLoadingFinished() { } private ContentObserver mContentObserver; public abstract Uri getContentUri(); @Override public Loader<Cursor> onCreateStatusesLoader(final Context context, final Bundle args, final boolean fromUser) { final Uri uri = getContentUri(); final String table = getTableNameByUri(uri); final String sortOrder = getSortOrder(); final long[] accountIds = getAccountIds(); final Expression accountWhere = Expression.in(new Column(Statuses.ACCOUNT_ID), new RawItemArray(accountIds)); final Expression filterWhere = getFiltersWhere(table), where; if (filterWhere != null) { where = Expression.and(accountWhere, filterWhere); } else { where = accountWhere; } final String selection = processWhere(where).getSQL(); getAdapter().setShowAccountsColor(accountIds.length > 1); return new ExtendedCursorLoader(context, uri, Statuses.COLUMNS, selection, null, sortOrder, fromUser); } @Override protected Object createMessageBusCallback() { return new CursorStatusesBusCallback(); } protected class CursorStatusesBusCallback { @Subscribe public void notifyGetStatusesTaskChanged(GetStatusesTaskEvent event) { if (!event.uri.equals(getContentUri())) return; setRefreshing(event.running); if (!event.running) { setLoadMoreIndicatorVisible(false); setRefreshEnabled(true); } } @Subscribe public void notifyFavoriteCreated(FavoriteCreatedEvent event) { } @Subscribe public void notifyFavoriteDestroyed(FavoriteDestroyedEvent event) { } @Subscribe public void notifyStatusDestroyed(StatusDestroyedEvent event) { } @Subscribe public void notifyStatusListChanged(StatusListChangedEvent event) { } @Subscribe public void notifyStatusRetweeted(StatusRetweetedEvent event) { } } @Override protected long[] getAccountIds() { final Bundle args = getArguments(); if (args != null && args.getLong(EXTRA_ACCOUNT_ID) > 0) { return new long[]{args.getLong(EXTRA_ACCOUNT_ID)}; } return Utils.getActivatedAccountIds(getActivity()); } @Override public void onStart() { super.onStart(); final ContentResolver cr = getContentResolver(); mContentObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { reloadStatuses(); } }; cr.registerContentObserver(Accounts.CONTENT_URI, true, mContentObserver); updateRefreshState(); } protected void reloadStatuses() { final Bundle args = new Bundle(), fragmentArgs = getArguments(); if (fragmentArgs != null) { args.putAll(fragmentArgs); args.putBoolean(EXTRA_FROM_USER, true); } getLoaderManager().restartLoader(0, args, this); } @Override public void onStop() { final ContentResolver cr = getContentResolver(); cr.unregisterContentObserver(mContentObserver); super.onStop(); } @Override protected boolean hasMoreData(final Cursor cursor) { return cursor != null && cursor.getCount() != 0; } @Override protected CursorStatusesAdapter onCreateAdapter(final Context context, final boolean compact) { return new CursorStatusesAdapter(context, compact); } @Override public void onLoaderReset(Loader<Cursor> loader) { getAdapter().setData(null); } @Override public void onLoadMoreContents() { super.onLoadMoreContents(); AsyncTaskUtils.executeTask(new AsyncTask<Object, Object, long[][]>() { @Override protected long[][] doInBackground(final Object... params) { final long[][] result = new long[3][]; result[0] = getAccountIds(); result[1] = getOldestStatusIds(result[0]); return result; } @Override protected void onPostExecute(final long[][] result) { getStatuses(result[0], result[1], result[2]); } }); } @Override public boolean triggerRefresh() { AsyncTaskUtils.executeTask(new AsyncTask<Object, Object, long[][]>() { @Override protected long[][] doInBackground(final Object... params) { final long[][] result = new long[3][]; result[0] = getAccountIds(); result[2] = getNewestStatusIds(result[0]); return result; } @Override protected void onPostExecute(final long[][] result) { getStatuses(result[0], result[1], result[2]); } }); return true; } protected Expression getFiltersWhere(String table) { if (!isFilterEnabled()) return null; return buildStatusFilterWhereClause(table, null); } protected long[] getNewestStatusIds(long[] accountIds) { return getNewestStatusIdsFromDatabase(getActivity(), getContentUri(), accountIds); } protected abstract int getNotificationType(); protected long[] getOldestStatusIds(long[] accountIds) { return getOldestStatusIdsFromDatabase(getActivity(), getContentUri(), accountIds); } protected abstract boolean isFilterEnabled(); protected Expression processWhere(final Expression where) { return where; } protected abstract void updateRefreshState(); private String getSortOrder() { final SharedPreferences preferences = getSharedPreferences(); final boolean sortById = preferences.getBoolean(KEY_SORT_TIMELINE_BY_ID, false); return sortById ? Statuses.SORT_ORDER_STATUS_ID_DESC : Statuses.SORT_ORDER_TIMESTAMP_DESC; } }