/*
* Copyright (c) 2015, Nils Braden
*
* This file is part of ttrss-reader-fork. 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.ttrssreader.gui.fragments;
import org.ttrssreader.R;
import org.ttrssreader.controllers.Controller;
import org.ttrssreader.gui.MenuActivity;
import org.ttrssreader.gui.interfaces.IDataChangedListener;
import org.ttrssreader.gui.interfaces.IItemSelectedListener;
import org.ttrssreader.gui.interfaces.IItemSelectedListener.TYPE;
import org.ttrssreader.gui.view.MyGestureDetector;
import org.ttrssreader.model.MainAdapter;
import org.ttrssreader.utils.AsyncTask;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.app.Activity;
import android.app.ListFragment;
import android.app.LoaderManager;
import android.content.Loader;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
public abstract class MainListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
@SuppressWarnings("unused")
private static final String TAG = MainListFragment.class.getSimpleName();
protected static final int TYPE_CAT_ID = 1;
protected static final int TYPE_FEED_ID = 2;
protected static final int TYPE_HEADLINE_ID = 3;
private static final String SELECTED_ID = "selectedId";
private static final int SELECTED_ID_DEFAULT = Integer.MIN_VALUE;
protected int selectedId = SELECTED_ID_DEFAULT;
private int scrollPosition;
protected MainAdapter adapter = null;
protected GestureDetector gestureDetector;
protected View.OnTouchListener gestureListener;
protected String title;
protected int unreadCount;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Async update of title und unread data:
updateTitleAndUnread();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.item_list, container, false);
}
@SuppressWarnings("deprecation")
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
int[] attrs = new int[] {android.R.attr.windowBackground};
TypedArray ta = getActivity().obtainStyledAttributes(attrs);
Drawable drawableFromTheme = ta.getDrawable(0);
ta.recycle();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) view.setBackgroundDrawable(drawableFromTheme);
else view.setBackground(drawableFromTheme);
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle instance) {
super.onActivityCreated(instance);
setListAdapter(adapter);
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
setChecked(selectedId);
super.onChanged();
}
});
registerForContextMenu(getListView());
ActionBar actionBar = ((MenuActivity) getActivity()).getSupportActionBar();
gestureDetector = new GestureDetector(getActivity(),
new MyGestureDetector(actionBar, Controller.getInstance().hideActionbar()), null);
gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event) || v.performClick();
}
};
getListView().setOnTouchListener(gestureListener);
// Read the selected list item after orientation changes and similar
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ID, selectedId);
super.onSaveInstanceState(outState);
}
@Override
public void onStop() {
super.onStop();
getListView().setVisibility(View.GONE);
}
@Override
public void onResume() {
getListView().setVisibility(View.VISIBLE);
getListView().setSelectionFromTop(scrollPosition, 0);
super.onResume();
}
@Override
public void onPause() {
super.onPause();
scrollPosition = getListView().getFirstVisiblePosition();
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
selectedId = adapter.getId(position);
setChecked(selectedId);
Activity activity = getActivity();
if (activity instanceof IItemSelectedListener) {
((IItemSelectedListener) activity).itemSelected(this, selectedId);
}
}
private void setChecked(int id) {
// Return if data hasn't been retrieved or content view has not been created yet
if (adapter == null || getView() == null || getListView() == null) return;
int pos = -1;
for (int item : adapter.getIds()) {
pos++;
if (item == id) {
getListView().setItemChecked(pos, true);
getListView().smoothScrollToPosition(pos);
return;
}
}
// Nothing found, uncheck everything:
getListView().setItemChecked(getListView().getCheckedItemPosition(), false);
}
public void doRefresh() {
if (adapter != null) adapter.notifyDataSetChanged();
}
public String getTitle() {
return title != null ? title : "";
}
public int getUnread() {
if (unreadCount > 0) return unreadCount;
return 0;
}
public abstract TYPE getType();
public void setSelectedId(int selectedId) {
this.selectedId = selectedId;
setChecked(selectedId);
}
/**
* Updates in here are started asynchronously since the DB is accessed. When the children are done with the updates
* we call {@link IDataChangedListener#dataLoadingFinished()} in the UI-thread again.
*/
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.notifyDataSetChanged();
updateTitleAndUnread();
}
private volatile Boolean updateTitleAndUnreadRunning = false;
private void updateTitleAndUnread() {
if (!updateTitleAndUnreadRunning) {
updateTitleAndUnreadRunning = true;
new AsyncTask<Void, Void, Void>() {
protected Void doInBackground(Void... params) {
fetchOtherData();
return null;
}
protected void onPostExecute(Void result) {
if (getActivity() instanceof IDataChangedListener)
((IDataChangedListener) getActivity()).dataLoadingFinished();
updateTitleAndUnreadRunning = false;
}
}.execute();
}
}
protected abstract void fetchOtherData();
@Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
if (Controller.sFragmentAnimationDirection != 0 && Controller.getInstance().animations()) {
Animator a;
if (Controller.sFragmentAnimationDirection > 0)
a = AnimatorInflater.loadAnimator(getActivity(), R.animator.slide_out_left);
else a = AnimatorInflater.loadAnimator(getActivity(), R.animator.slide_out_right);
// Reset:
Controller.sFragmentAnimationDirection = 0;
return a;
}
return super.onCreateAnimator(transit, enter, nextAnim);
}
/**
* Needed to determine if we need to read the title from this fragment or use the generic title from the
* CategoryFragment instead.
*/
public boolean isEmptyPlaceholder() {
return false;
}
}