package org.ovirt.mobile.movirt.ui.listfragment; import android.content.res.ColorStateList; import android.database.Cursor; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v4.content.Loader; import android.support.v4.widget.SwipeRefreshLayout; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CursorAdapter; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.Spinner; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.UiThread; import org.androidannotations.annotations.ViewById; import org.ovirt.mobile.movirt.R; import org.ovirt.mobile.movirt.auth.account.AccountRxStore; import org.ovirt.mobile.movirt.auth.account.EnvironmentStore; import org.ovirt.mobile.movirt.model.base.BaseEntity; import org.ovirt.mobile.movirt.provider.ProviderFacade; import org.ovirt.mobile.movirt.provider.SortOrder; import org.ovirt.mobile.movirt.ui.EndlessScrollListener; import org.ovirt.mobile.movirt.ui.HasLoader; import org.ovirt.mobile.movirt.ui.RefreshableLoaderFragment; import org.ovirt.mobile.movirt.ui.listfragment.spinner.CustomSort; import org.ovirt.mobile.movirt.ui.listfragment.spinner.SortEntry; import org.ovirt.mobile.movirt.ui.listfragment.spinner.SortOrderType; import org.ovirt.mobile.movirt.util.CursorAdapterLoader; @EFragment(R.layout.fragment_base_entity_list) public abstract class BaseListFragment<E extends BaseEntity<?>> extends RefreshableLoaderFragment implements HasLoader, BaseListFragmentContract.View { private static final int BASE_LOADER = 0; private static final int ITEMS_PER_PAGE = 20; @ViewById(android.R.id.list) protected ListView listView; @ViewById protected EditText searchText; @ViewById protected Spinner orderBySpinner; @ViewById protected Spinner orderSpinner; @ViewById protected SwipeRefreshLayout swipeContainer; @Bean protected ProviderFacade provider; @Bean protected EnvironmentStore environmentStore; @ViewById public FloatingActionButton fab; @ViewById public LinearLayout searchbox; @ViewById public LinearLayout orderingLayout; @Bean public AccountRxStore rxStore; @InstanceState public boolean searchToggle; @InstanceState public int orderSpinnerPosition = AdapterView.INVALID_POSITION; @InstanceState public int orderBySpinnerPosition = AdapterView.INVALID_POSITION; protected final Class<E> entityClass; protected BaseListFragmentContract.Presenter presenter; private final TextWatcher textWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @Override public void afterTextChanged(Editable editable) { refreshDisplay(); } }; protected final EndlessScrollListener endlessScrollListener = new EndlessScrollListener() { @Override public void onLoadMore(int page, int totalItemsCount) { loadMoreData(page); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { BaseListFragment.this.onScrollStateChanged(view, scrollState); } }; protected int page = 1; protected CursorAdapterLoader cursorAdapterLoader; protected BaseListFragment(Class<E> clazz) { this.entityClass = clazz; } public void loadMoreData(int page) { this.page = page; restartLoader(); } protected void resetListViewPosition() { page = 1; listView.smoothScrollToPosition(0); endlessScrollListener.resetListener(); } class OrderItemSelectedListener implements AdapterView.OnItemSelectedListener { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { orderSpinnerPosition = i; presenter.onOrderSelected(SortOrder.fromIndex(i)); } @Override public void onNothingSelected(AdapterView<?> adapterView) { } } class OrderByItemSelectedListener implements AdapterView.OnItemSelectedListener { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { orderBySpinnerPosition = i; presenter.onOrderBySelected((SortEntry) adapterView.getSelectedItem()); } @Override public void onNothingSelected(AdapterView<?> adapterView) { } } @Override protected SwipeRefreshLayout getSwipeRefreshLayout() { return swipeContainer; } @AfterViews protected void init() { initSpinners(); initAdapters(); initListeners(); presenter = BaseListFragmentPresenter_.getInstance_(getActivity().getApplicationContext()) .initAllSortEntries(getSortEntries()) .initSelection(asSortEntry(orderBySpinnerPosition), SortOrder.fromIndex(orderSpinnerPosition)) .setView(this) .initialize(); } private void initSpinners() { if (getCustomSort() == null) { ArrayAdapter<SortEntry> spinnerArrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item, getSortEntries()); spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); orderBySpinner.setAdapter(spinnerArrayAdapter); } else { orderingLayout.setVisibility(View.GONE); } } public void swapOrderSpinner(SortEntry orderBy) { final SortOrderType sortOrderType = orderBy.getSortOrder(); ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item, new String[]{ sortOrderType.getDisplayNameBySortOrder(SortOrder.fromIndex(0)), // ASCENDING sortOrderType.getDisplayNameBySortOrder(SortOrder.fromIndex(1)) // DESCENDING }); spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); orderSpinner.setAdapter(spinnerArrayAdapter); } protected void initAdapters() { final CursorAdapter cursorAdapter = createCursorAdapter(); listView.setAdapter(cursorAdapter); listView.setEmptyView(getActivity().findViewById(android.R.id.empty)); listView.setTextFilterEnabled(true); cursorAdapterLoader = new CursorAdapterLoader(cursorAdapter) { @Override public synchronized Loader<Cursor> onCreateLoader(int id, Bundle args) { ProviderFacade.QueryBuilder<E> query = provider.query(entityClass); appendQuery(query); final CustomSort customSort = getCustomSort(); if (customSort == null) { final SortEntry orderBy = (SortEntry) orderBySpinner.getSelectedItem(); final SortOrder order = SortOrderType.getSortOrder((String) orderSpinner.getSelectedItem()); query.orderBy(orderBy.orderBySql(), order); } else { for (CustomSort.CustomSortEntry entry : customSort.getSortEntries()) { query.orderBy(entry.getColumnName(), entry.getSortOrder()); } } return query.limit(page * ITEMS_PER_PAGE).asLoader(); } }; getLoaderManager().initLoader(BASE_LOADER, null, cursorAdapterLoader); } protected void initListeners() { listView.setOnScrollListener(endlessScrollListener); listView.setOnItemLongClickListener(getOnItemLongClickListener()); searchText.removeTextChangedListener(textWatcher); searchText.addTextChangedListener(textWatcher); if (getCustomSort() == null) { orderBySpinner.setOnItemSelectedListener(new OrderByItemSelectedListener()); orderSpinner.setOnItemSelectedListener(new OrderItemSelectedListener()); } fab.setBackgroundTintList(ColorStateList.valueOf(getResources().getColor(R.color.material_blue_400))); fab.setOnClickListener(view -> toggleSearchBoxVisibility()); } protected void appendQuery(ProviderFacade.QueryBuilder<E> query) { // left intentionally empty } protected void onScrollStateChanged(AbsListView view, int scrollState) { // left intentionally empty } protected AdapterView.OnItemLongClickListener getOnItemLongClickListener() { return null; } protected CustomSort getCustomSort() { return null; } protected SortEntry[] getSortEntries() { return new SortEntry[]{}; } @UiThread(propagation = UiThread.Propagation.REUSE) public void setSearchBoxVisibility(boolean visible) { if (visible) { searchbox.setVisibility(View.VISIBLE); } else { searchbox.setVisibility(View.GONE); } } public void toggleSearchBoxVisibility() { searchToggle = !searchToggle; setSearchBoxVisibility(searchToggle); } @UiThread(propagation = UiThread.Propagation.REUSE) @Override public void restartLoader() { getLoaderManager().restartLoader(BASE_LOADER, null, cursorAdapterLoader); } @Override public void destroyLoader() { getLoaderManager().destroyLoader(BASE_LOADER); } @Override public void refreshDisplay() { if (isResumed()) { resetListViewPosition(); restartLoader(); } } @Override public void displaySelection(SortEntry sortEntry, SortOrder sortOrder) { orderBySpinner.setSelection(asSortEntryIndex(sortEntry)); swapOrderSpinner(sortEntry); orderSpinner.setSelection(sortOrder.getIndex()); } @Override public BaseListFragmentContract.Presenter getPresenter() { return presenter; } @Override public void onDestroyView() { if (presenter != null) { presenter.destroy(); } super.onDestroyView(); } @Override public void onResume() { setSearchBoxVisibility(searchToggle); super.onResume(); } protected abstract CursorAdapter createCursorAdapter(); private SortEntry asSortEntry(int index) { final SortEntry[] sortEntries = getSortEntries(); if (index >= 0 && index < sortEntries.length) { return sortEntries[index]; } return null; } private int asSortEntryIndex(SortEntry sortEntry) { final SortEntry[] sortEntries = getSortEntries(); for (int i = 0; i < sortEntries.length; i++) { SortEntry entry = sortEntries[i]; if (entry.equals(sortEntry)) { return i; } } return 0; } }