/* * Copyright 2016 Gleb Godonoga. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.andrada.sitracker.ui.fragment; import android.app.Activity; import android.app.backup.BackupManager; import android.content.BroadcastReceiver; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Bundle; import android.support.design.widget.Snackbar; import android.support.v4.view.ViewCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; import android.view.Menu; import android.view.MenuItem; import android.view.View; import com.afollestad.materialcab.MaterialCab; import com.andrada.sitracker.Constants; import com.andrada.sitracker.R; import com.andrada.sitracker.analytics.FBAEvent; import com.andrada.sitracker.analytics.MarkAsReadEvent; import com.andrada.sitracker.analytics.RemoveAuthorEvent; import com.andrada.sitracker.analytics.SelectAuthorEvent; import com.andrada.sitracker.contracts.AuthorItemListener; import com.andrada.sitracker.contracts.AuthorUpdateStatusListener; import com.andrada.sitracker.contracts.OnBackAware; import com.andrada.sitracker.db.beans.Author; import com.andrada.sitracker.events.AuthorCheckedEvent; import com.andrada.sitracker.events.AuthorSelectedEvent; import com.andrada.sitracker.events.AuthorSortMethodChanged; import com.andrada.sitracker.events.BackUpRestoredEvent; import com.andrada.sitracker.events.PublicationMarkedAsReadEvent; import com.andrada.sitracker.tasks.UpdateAuthorsTask_; import com.andrada.sitracker.tasks.filters.UpdateStatusMessageFilter; import com.andrada.sitracker.tasks.receivers.UpdateStatusReceiver; import com.andrada.sitracker.ui.SearchActivity_; import com.andrada.sitracker.ui.fragment.adapters.AuthorsAdapter; import com.andrada.sitracker.ui.widget.DividerItemDecoration; import com.andrada.sitracker.analytics.AnalyticsManager; import com.andrada.sitracker.util.UpdateServiceHelper; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.OptionsItem; import org.androidannotations.annotations.OptionsMenu; import org.androidannotations.annotations.SystemService; import org.androidannotations.annotations.UiThread; import org.androidannotations.annotations.ViewById; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; import java.util.LinkedList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import de.greenrobot.event.EventBus; import static com.andrada.sitracker.util.LogUtils.LOGI; @EFragment(R.layout.fragment_myauthors) @OptionsMenu(R.menu.authors_menu) public class AuthorsFragment extends BaseListFragment implements AuthorUpdateStatusListener, AuthorItemListener, MaterialCab.Callback, OnBackAware { private static final String TAG = "authorListFragment"; @ViewById(R.id.authors_list) RecyclerView list; @ViewById View empty; @Bean AuthorsAdapter adapter; @SystemService ConnectivityManager connectivityManager; boolean mIsUpdating = false; @Nullable private Snackbar mNoNetworkSnack; MaterialCab mCab; private BroadcastReceiver updateStatusReceiver; RecyclerView.AdapterDataObserver dataObserver = new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { if (adapter != null && empty != null) { if (adapter.getItemCount() > 0) { list.setVisibility(View.VISIBLE); empty.setVisibility(View.GONE); } else { list.setVisibility(View.GONE); empty.setVisibility(View.VISIBLE); } } } }; //region Fragment lifecycle overrides @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); LOGI("SITracker", "AuthorsFragment - OnCreate"); if (savedInstanceState != null) { mCab = MaterialCab.restoreState(savedInstanceState, (AppCompatActivity) this.getActivity(), this); } } @Override public void onStart() { super.onStart(); getActivity().invalidateOptionsMenu(); adapter.notifyDataSetChanged(); } @Override public void onResume() { super.onResume(); EventBus.getDefault().register(this); getBaseActivity().getActionBarToolbar().setTitle(getString(R.string.navdrawer_item_my_authors)); //Receiver registration mIsUpdating = UpdateServiceHelper.isServiceCurrentlyRunning(getActivity().getApplicationContext()); if (updateStatusReceiver == null) { updateStatusReceiver = new UpdateStatusReceiver(this); updateStatusReceiver.setOrderedHint(true); } UpdateStatusMessageFilter filter = new UpdateStatusMessageFilter(); filter.setPriority(1); getActivity().registerReceiver(updateStatusReceiver, filter); adapter.registerAdapterDataObserver(dataObserver); //Reload authors adapter.reloadAuthors(); onRefreshingStateChanged(mIsUpdating); } @Override public void onPause() { super.onPause(); EventBus.getDefault().unregister(this); getActivity().unregisterReceiver(updateStatusReceiver); adapter.unregisterAdapterDataObserver(dataObserver); if (mCab != null && mCab.isActive()) { cleanUpAfterCabAction(); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mCab != null) mCab.saveState(outState); } @Override @UiThread(delay = 100) protected void onRefreshingStateChanged(boolean refreshing) { super.onRefreshingStateChanged(refreshing); } //endregion //region Menu item tap handlers @OptionsItem(R.id.action_search) void menuSearchSelected() { SearchActivity_.intent(this.getActivity()).start(); } protected void requestDataRefresh() { if (mIsUpdating) { return; } performManualRefresh(); } void performManualRefresh() { if (!mIsUpdating && adapter.getItemCount() > 0) { final NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); if (activeNetwork != null && activeNetwork.isConnected()) { Intent updateIntent = new Intent(getActivity(), UpdateAuthorsTask_.class); updateIntent.putExtra(Constants.UPDATE_IGNORES_NETWORK, true); getActivity().startService(updateIntent); AnalyticsManager.getInstance().logEvent(new FBAEvent(Constants.GA_EVENT_AUTHORS_MANUAL_REFRESH)); //Start refreshing toggleUpdatingState(); } else { //Surface crouton that network is unavailable showNoNetworkSnackbar(); } } } //endregion /** * Snackbar click handler */ private View.OnClickListener snackBarClickListener = new View.OnClickListener() { @Override public void onClick(View v) { if (mNoNetworkSnack != null) { mNoNetworkSnack.dismiss(); mNoNetworkSnack = null; } Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { Activity activity = getActivity(); if (activity != null) { activity.runOnUiThread(new Runnable() { @Override public void run() { performManualRefresh(); } }); } } }, 1500); } }; @AfterViews void bindAdapter() { adapter.updateContext(getBaseActivity()); adapter.setAuthorItemListener(this); list.setHasFixedSize(true); list.setLayoutManager(new LinearLayoutManager(list.getContext())); list.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST)); list.setAdapter(adapter); list.setBackgroundResource(R.drawable.authors_list_background); //recyclerView.setEmptyView(empty); } private void toggleUpdatingState() { mIsUpdating = !mIsUpdating; onRefreshingStateChanged(mIsUpdating); } //region Public methods public boolean isUpdating() { return UpdateServiceHelper.isServiceCurrentlyRunning(getActivity().getApplicationContext()); } //endregion //region AuthorUpdateStatusListener callbacks @Override public void onAuthorsUpdated() { toggleUpdatingState(); adapter.reloadAuthors(); } @Override public void onAuthorsUpdateFailed() { toggleUpdatingState(); //surface crouton that update failed SpannableStringBuilder snackbarText = new SpannableStringBuilder(); snackbarText.append(getResources().getText(R.string.update_failed_crouton_message)); snackbarText.setSpan(new ForegroundColorSpan(0xFFFF0000), 0, snackbarText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); Snackbar.make(getActivity().findViewById(R.id.drawer_layout), snackbarText, Snackbar.LENGTH_SHORT).show(); } @Override public void onAuthorsUpToDate() { toggleUpdatingState(); Snackbar.make(getActivity().findViewById(R.id.drawer_layout), R.string.authors_up_to_date, Snackbar.LENGTH_SHORT).show(); } //endregion public void onEvent(@NotNull PublicationMarkedAsReadEvent event) { //ensure we update the new status of the author if he has no new publications if (event.refreshAuthor) { adapter.reloadAuthors(); } } public void onEvent(BackUpRestoredEvent event) { if (adapter != null) { adapter.reloadAuthors(); this.showSuccessfulRestore(); } } public void onEvent(AuthorCheckedEvent event) { final int currentPosition = adapter.getItemPositionByAuthorId(event.authorId); startActionMode(currentPosition); } @UiThread protected void showSuccessfulRestore() { String message = getResources().getString(R.string.backup_restored_crouton_message); Snackbar.make(getActivity().findViewById(R.id.drawer_layout), message, Snackbar.LENGTH_SHORT).show(); } //region AuthorAddedEvent handler @SuppressWarnings("UnusedParameters") public void onEvent(AuthorSortMethodChanged event) { adapter.reloadAuthors(); } //endregion private void showNoNetworkSnackbar() { String msg = getResources().getString(R.string.no_network_error); Snackbar.make(getActivity().findViewById(R.id.drawer_layout), msg, Snackbar.LENGTH_LONG) .setAction(getResources().getString(R.string.no_network_retry), this.snackBarClickListener) .show(); } public AuthorsAdapter getAdapter() { return adapter; } @Override public boolean canSwipeRefreshChildScrollUp() { return ViewCompat.canScrollVertically(list, -1); } @Override public void onAuthorItemClick(AuthorsAdapter.ViewHolder viewHolder) { if (mCab != null && mCab.isActive()) { startActionMode(viewHolder.getAdapterPosition()); return; } Author auth = adapter.getItem(viewHolder.getAdapterPosition()); if (auth != null) { AnalyticsManager.getInstance().logEvent(new SelectAuthorEvent(auth.getName(), auth.getUrl())); EventBus.getDefault().post(new AuthorSelectedEvent(auth.getId(), auth.getName())); } } @Override public void onAuthorItemLongClick(AuthorsAdapter.ViewHolder viewHolder) { startActionMode(viewHolder.getAdapterPosition()); } private void startActionMode(int position) { adapter.toggleSelected(position); if (adapter.getSelectedCount() == 0) { mCab.finish(); return; } getBaseActivity().trySetToolbarScrollable(false); if (mCab == null) mCab = new MaterialCab(getBaseActivity(), R.id.cab_stub) .setMenu(R.menu.context_authors) .start(this); else if (!mCab.isActive()) mCab.reset().start(this); mCab.setTitle(getResources().getQuantityString( R.plurals.authors_selected, adapter.getSelectedCount(), adapter.getSelectedCount())); } @Override public boolean onCabCreated(MaterialCab materialCab, Menu menu) { // Makes the icons in the overflow menu visible if (menu.getClass().getSimpleName().equals("MenuBuilder")) { try { Field field = menu.getClass(). getDeclaredField("mOptionalIconsVisible"); field.setAccessible(true); field.setBoolean(menu, true); } catch (Exception ignored) { ignored.printStackTrace(); } } return true; // allow creation } @Override public boolean onCabItemClicked(MenuItem item) { List<Long> selectedAuthors = new LinkedList<>(); for (Integer position : adapter.getSelectedPositions()) { selectedAuthors.add(adapter.getItemId(position)); } if (item.getItemId() == R.id.action_remove) { AnalyticsManager.getInstance().logEvent(new RemoveAuthorEvent(selectedAuthors.size())); //This stuff is on background thread adapter.removeAuthors(selectedAuthors); cleanUpAfterCabAction(); return true; } else if (item.getItemId() == R.id.action_mark_read) { adapter.markAuthorsRead(selectedAuthors); AnalyticsManager.getInstance().logEvent(new MarkAsReadEvent(selectedAuthors.size())); BackupManager bm = new BackupManager(getActivity()); bm.dataChanged(); cleanUpAfterCabAction(); return true; } else if (item.getItemId() == R.id.action_open_authors_browser) { for (int i = 0; i < adapter.getItemCount(); i++) { if (selectedAuthors.contains(adapter.getItemId(i))) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(adapter.getItem(i).getUrl())); getActivity().startActivity(intent); } } cleanUpAfterCabAction(); return true; } return false; } private void cleanUpAfterCabAction() { adapter.clearSelected(); if (mCab != null) { mCab.finish(); mCab = null; } } @Override public boolean onCabFinished(MaterialCab materialCab) { getBaseActivity().trySetToolbarScrollable(true); adapter.clearSelected(); mCab = null; return true; // allow destruction } public boolean onBackPressed() { if (mCab != null && mCab.isActive()) { cleanUpAfterCabAction(); return true; } return false; } }