package com.ghostflying.portalwaitinglist.fragment;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Intent;
import android.content.Loader;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.ghostflying.portalwaitinglist.AuthActivity;
import com.ghostflying.portalwaitinglist.R;
import com.ghostflying.portalwaitinglist.SettingActivity;
import com.ghostflying.portalwaitinglist.dao.datahelper.PortalEventHelper;
import com.ghostflying.portalwaitinglist.loader.PortalListLoader;
import com.ghostflying.portalwaitinglist.model.Message;
import com.ghostflying.portalwaitinglist.model.PortalDetail;
import com.ghostflying.portalwaitinglist.model.PortalEvent;
import com.ghostflying.portalwaitinglist.recyclerviewHelper.PortalListAdapter;
import com.ghostflying.portalwaitinglist.util.GMailServiceUtil;
import com.ghostflying.portalwaitinglist.util.MailProcessUtil;
import com.ghostflying.portalwaitinglist.util.SearchUtil;
import com.ghostflying.portalwaitinglist.util.SettingUtil;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import retrofit.RetrofitError;
public class PortalListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<PortalListLoader.PortalListViewModel> {
private OnFragmentInteractionListener mListener;
Toolbar toolbar;
String token;
String account;
SwipeRefreshLayout swipeRefreshLayout;
RecyclerView recyclerView;
DrawerLayout drawerLayout;
List<PortalDetail> totalPortalDetails;
TextView countEverything;
TextView countAccepted;
TextView countRejected;
TextView countWaiting;
TextView totalPortals;
TextView totalSubmission;
TextView totalEdit;
View selectedType;
SearchTask searchTask;
MenuItem searchItem;
public static PortalListFragment newInstance() {
PortalListFragment fragment = new PortalListFragment();
return fragment;
}
public PortalListFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_portal_list, container, false);
// Initial some members
account = SettingUtil.getAccount();
totalPortalDetails = new ArrayList<>();
setToolbar(view);
setDrawerLayout(view);
setRecyclerView(view);
setSwipeRefreshLayout(view);
setHasOptionsMenu(true);
switchActionBarColorBySetting();
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_main, menu);
// set search view
searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setQueryHint(getString(R.string.search_hint));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
new SearchTask().execute(newText);
return true;
}
});
// prefer to use this instead of setOnCloseListener
// see http://developer.android.com/guide/topics/ui/actionbar.html#ActionView
MenuItemCompat.setOnActionExpandListener(
menu.findItem(R.id.action_search),
new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
getLoaderManager().getLoader(0).onContentChanged();
return true;
}
}
);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_filter) {
drawerLayout.openDrawer(Gravity.RIGHT);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onResume(){
super.onResume();
// read stored data.
getLoaderManager().initLoader(0, null, this);
}
@Override
public void onStop (){
searchItem.collapseActionView();
super.onStop();
}
private void setDrawerLayout(View v){
drawerLayout = (DrawerLayout)v.findViewById(R.id.drawer_layout);
// set the status bar bg when nav do not open
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
drawerLayout.setStatusBarBackgroundColor(getResources().getColor(R.color.primary_dark));
// handle the home button
ActionBarDrawerToggle actionBarDrawerToggle =
new ActionBarDrawerToggle(getActivity(),
drawerLayout,
toolbar,
R.string.app_name,
R.string.app_name);
actionBarDrawerToggle.syncState();
drawerLayout.setDrawerListener(actionBarDrawerToggle);
// result filter
v.findViewById(R.id.item_everything).setOnClickListener(resultFilterClickListener);
v.findViewById(R.id.item_accepted).setOnClickListener(resultFilterClickListener);
v.findViewById(R.id.item_rejected).setOnClickListener(resultFilterClickListener);
v.findViewById(R.id.item_waiting).setOnClickListener(resultFilterClickListener);
// sort
v.findViewById(R.id.item_smart_order).setOnClickListener(sortClickListener);
v.findViewById(R.id.item_last_asc_order).setOnClickListener(sortClickListener);
v.findViewById(R.id.item_last_desc_order).setOnClickListener(sortClickListener);
v.findViewById(R.id.item_proposed_asc_order).setOnClickListener(sortClickListener);
v.findViewById(R.id.item_proposed_desc_order).setOnClickListener(sortClickListener);
v.findViewById(R.id.item_alphabetical_order).setOnClickListener(sortClickListener);
// type filter
v.findViewById(R.id.navigation_item_all).setOnClickListener(typeFilterClickListener);
v.findViewById(R.id.navigation_item_submission).setOnClickListener(typeFilterClickListener);
v.findViewById(R.id.navigation_item_edit).setOnClickListener(typeFilterClickListener);
// select from setting
switch (SettingUtil.getTypeFilterMethod()){
case ALL:
selectedType = v.findViewById(R.id.navigation_item_all);
break;
case SUBMISSION:
selectedType = v.findViewById(R.id.navigation_item_submission);
break;
case EDIT:
selectedType = v.findViewById(R.id.navigation_item_edit);
break;
default:
selectedType = v.findViewById(R.id.navigation_item_all);
}
selectedType.setSelected(true);
// other in navigation
v.findViewById(R.id.navigation_item_setting).setOnClickListener(navigationDrawerClickListener);
v.findViewById(R.id.navigation_item_feedback).setOnClickListener(navigationDrawerClickListener);
totalPortals = (TextView)v.findViewById(R.id.navigation_drawer_total_portals);
totalEdit = (TextView)v.findViewById(R.id.navigation_drawer_total_edit);
totalSubmission = (TextView)v.findViewById(R.id.navigation_drawer_total_submission);
// set the user avatar and account name
if (SettingUtil.getAccount() != null){
((TextView)v.findViewById(R.id.account_name)).setText(SettingUtil.getAccount());
((TextView)v.findViewById(R.id.user_avatar)).setText(
SettingUtil.getAccount().toUpperCase().substring(0 ,1)
);
}
countEverything = (TextView)v.findViewById(R.id.count_everything);
countAccepted = (TextView)v.findViewById(R.id.count_accepted);
countRejected = (TextView)v.findViewById(R.id.count_rejected);
countWaiting = (TextView)v.findViewById(R.id.count_waiting);
}
View.OnClickListener navigationDrawerClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.navigation_item_setting:
Intent setting = new Intent(getActivity(), SettingActivity.class);
startActivityForResult(setting, SettingActivity.REQUEST_SETTING);
getActivity().overridePendingTransition(R.animator.setting_swap_in_bottom, R.animator.setting_swap_out_bottom);
break;
case R.id.navigation_item_feedback:
Intent mailIntent = new Intent(
Intent.ACTION_SENDTO,
Uri.fromParts("mailto", getString(R.string.author_mail), null)
);
mailIntent.putExtra(Intent.EXTRA_SUBJECT,
getString(R.string.navigation_drawer_feedback_subject));
startActivity(Intent.createChooser(
mailIntent, getString(R.string.navigation_drawer_send)));
break;
}
}
};
View.OnClickListener sortClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.item_smart_order:
SettingUtil.setSortOrder(SettingUtil.SortOrder.SMART_ORDER);
break;
case R.id.item_last_asc_order:
SettingUtil.setSortOrder(SettingUtil.SortOrder.LAST_DATE_ASC);
break;
case R.id.item_last_desc_order:
SettingUtil.setSortOrder(SettingUtil.SortOrder.LAST_DATE_DESC);
break;
case R.id.item_alphabetical_order:
SettingUtil.setSortOrder(SettingUtil.SortOrder.ALPHABETICAL);
break;
case R.id.item_proposed_asc_order:
SettingUtil.setSortOrder(SettingUtil.SortOrder.PROPOSED_DATE_ASC);
break;
case R.id.item_proposed_desc_order:
SettingUtil.setSortOrder(SettingUtil.SortOrder.PROPOSED_DATE_DESC);
break;
default:
}
drawerLayout.closeDrawer(Gravity.RIGHT);
}
};
View.OnClickListener resultFilterClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.item_everything:
SettingUtil.setResultFilterMethod(SettingUtil.ResultFilterMethod.EVERYTHING);
break;
case R.id.item_accepted:
SettingUtil.setResultFilterMethod(SettingUtil.ResultFilterMethod.ACCEPTED);
break;
case R.id.item_rejected:
SettingUtil.setResultFilterMethod(SettingUtil.ResultFilterMethod.REJECTED);
break;
case R.id.item_waiting:
SettingUtil.setResultFilterMethod(SettingUtil.ResultFilterMethod.WAITING);
break;
default:
}
drawerLayout.closeDrawer(Gravity.RIGHT);
}
};
View.OnClickListener typeFilterClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.navigation_item_all:
SettingUtil.setTypeFilterMethod(SettingUtil.TypeFilterMethod.ALL);
break;
case R.id.navigation_item_submission:
SettingUtil.setTypeFilterMethod(SettingUtil.TypeFilterMethod.SUBMISSION);
break;
case R.id.navigation_item_edit:
SettingUtil.setTypeFilterMethod(SettingUtil.TypeFilterMethod.EDIT);
break;
default:
}
// reset selected before
if (selectedType != null)
selectedType.setSelected(false);
// select this
v.setSelected(true);
selectedType = v;
drawerLayout.closeDrawer(Gravity.LEFT);
}
};
private void switchActionBarColorBySetting(){
int actionBarBg;
int statusBarBg;
switch (SettingUtil.getResultFilterMethod()){
case EVERYTHING:
actionBarBg = R.color.portal_list_action_bar_bg_everything;
statusBarBg = R.color.portal_list_status_bar_bg_everything;
break;
case ACCEPTED:
actionBarBg = R.color.portal_list_action_bar_bg_accepted;
statusBarBg = R.color.portal_list_status_bar_bg_accepted;
break;
case REJECTED:
actionBarBg = R.color.portal_list_action_bar_bg_rejected;
statusBarBg = R.color.portal_list_status_bar_bg_rejected;
break;
case WAITING:
actionBarBg = R.color.portal_list_action_bar_bg_waiting;
statusBarBg = R.color.portal_list_status_bar_bg_waiting;
break;
default:
actionBarBg = R.color.primary;
statusBarBg = R.color.primary_dark;
}
toolbar.setBackgroundResource(actionBarBg);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
drawerLayout.setStatusBarBackground(statusBarBg);
}
private void setToolbar(View v){
toolbar = (Toolbar)v.findViewById(R.id.action_bar_in_list);
setTitleBySetting();
((ActionBarActivity)getActivity()).setSupportActionBar(toolbar);
((ActionBarActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
private void setTitleBySetting(){
String title = "";
switch (SettingUtil.getTypeFilterMethod()){
case ALL:
title = getResources().getString(R.string.navigation_drawer_all_portals);
break;
case EDIT:
title = getResources().getString(R.string.navigation_drawer_edit_portals);
break;
case SUBMISSION:
title = getResources().getString(R.string.navigation_drawer_submission_portals);
break;
}
title += " - ";
switch (SettingUtil.getSortOrder()){
case SMART_ORDER:
title += getString(R.string.smart_order);
break;
case LAST_DATE_ASC:
title += getString(R.string.last_asc_order);
break;
case LAST_DATE_DESC:
title += getString(R.string.last_desc_order);
break;
case ALPHABETICAL:
title += getString(R.string.alphabetical_order);
break;
case PROPOSED_DATE_ASC:
title += getString(R.string.proposed_asc_order);
break;
case PROPOSED_DATE_DESC:
title += getString(R.string.proposed_desc_order);
break;
}
toolbar.setTitle(title);
}
private void setSwipeRefreshLayout(View v){
swipeRefreshLayout = (SwipeRefreshLayout)v.findViewById(R.id.swipe_layout);
swipeRefreshLayout.setColorSchemeResources(
R.color.refresh_circle_color_one,
R.color.refresh_circle_color_two,
R.color.refresh_circle_color_three);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
// execute the refresh task to query from GMail
new RefreshTask().execute();
}
});
}
private void setRecyclerView(View v){
recyclerView = (RecyclerView)v.findViewById(R.id.portal_list);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
final PortalListAdapter adapter = new PortalListAdapter(new ArrayList<PortalDetail>());
adapter.setOnItemClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PortalDetail clickedPortal = adapter.dataSet.get(recyclerView.getChildPosition(v));
mListener.portalItemClicked(clickedPortal, v);
}
});
recyclerView.setAdapter(adapter);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// when return from setting activity and the filter or sort related
// params are changed, do filter and sort.
if (requestCode == SettingActivity.REQUEST_SETTING
&& resultCode == SettingActivity.RESULT_OK){
getLoaderManager().getLoader(0).onContentChanged();
}
}
@Override
public Loader<PortalListLoader.PortalListViewModel> onCreateLoader(int id, Bundle args) {
return new PortalListLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<PortalListLoader.PortalListViewModel> loader,
PortalListLoader.PortalListViewModel data) {
((PortalListAdapter)recyclerView.getAdapter()).setDataSet(data.mPortals);
switch (data.mAction){
case GET_DATA:
updateCountText(data.counts);
if (account != null && data.mPortals.size() == 0){
showToast(R.string.alert_to_refresh);
swipeRefreshLayout.setRefreshing(true);
new RefreshTask().execute();
}
totalPortalDetails = data.totalPortals;
break;
case SORT:
setTitleBySetting();
break;
case FILTER:
setTitleBySetting();
switchActionBarColorBySetting();
break;
}
}
@Override
public void onLoaderReset(Loader<PortalListLoader.PortalListViewModel> loader) {
}
private void showToast(final int resId){
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity().getApplicationContext(), resId, Toast.LENGTH_SHORT).show();
}
});
}
/**
* The refresh task to run once user calls to refresh data.
*/
private class RefreshTask extends AsyncTask<Void, Void, Void>{
String refreshResult;
@Override
protected Void doInBackground(Void...params) {
if (getToken())
return null;
// Initial Utils
GMailServiceUtil fetchUtil = GMailServiceUtil.getInstance(token, false);
MailProcessUtil processUtil = MailProcessUtil.getInstance();
PortalEventHelper dbHelper = new PortalEventHelper(getActivity());
// query and convert all new messages.
ArrayList<Message> newMessages;
try{
newMessages = fetchUtil.getPortalMessages(dbHelper);
}
catch (Exception e){
if (handleException(e))
return null;
else {
// if the exception is not fatal, as the invalid token
// handleException will clear the token, and getToken here again.
if (getToken())
return null;
// now the new token is obtained
// regenerate the util
fetchUtil = GMailServiceUtil.getInstance(token, true);
// reFetch the messages
try {
newMessages = fetchUtil.getPortalMessages(dbHelper);
}
catch (Exception e1){
// no matter the exception is fatal, refresh stop.
handleException(e1, true);
return null;
}
}
}
ArrayList<PortalEvent> newEvents = MailProcessUtil.getInstance().analysisMessages(newMessages);
PortalEventHelper mHelper = new PortalEventHelper(getActivity());
mHelper.bulkInsert(newEvents);
int[] eventCounts = MailProcessUtil.getInstance().getEventCountsInLastProcess();
generateRefreshResultSummary(eventCounts);
return null;
}
private void generateRefreshResultSummary(int[] eventCounts) {
// generate the refresh result text
refreshResult = getString(R.string.refresh_done);
boolean anythingNew = false;
if (eventCounts[0] != 0){
anythingNew = true;
refreshResult += "\n"
+ Integer.toString(eventCounts[0])
+ getString(R.string.refresh_result_proposed);
}
if (eventCounts[1] != 0){
anythingNew = true;
refreshResult += "\n"
+ Integer.toString(eventCounts[1])
+ getString(R.string.refresh_result_accepted);
}
if (eventCounts[2] != 0){
anythingNew = true;
refreshResult += "\n"
+ Integer.toString(eventCounts[2])
+ getString(R.string.refresh_result_rejected);
}
if (!anythingNew){
refreshResult += "\n"
+ getString(R.string.refresh_result_nothing);
}
}
private boolean getToken() {
try{
token = GoogleAuthUtil.getToken(getActivity().getApplicationContext(), account, AuthActivity.SCOPE);
}catch (Exception e){
handleException(e);
return true;
}
return false;
}
/**
* Handle the exception during the refresh.
* @param e the exception.
* @return true if the exception is fatal, otherwise false
*/
private boolean handleException(final Exception e, boolean retry){
e.printStackTrace();
if (e instanceof GoogleAuthException) {
showToast(R.string.auth_error);
mListener.doAuthInActivity();
} else if (e instanceof IOException) {
showToast(R.string.network_error);
} else if (e instanceof RetrofitError) {
if (((RetrofitError) e).getResponse() == null){
handleException(new IOException());
}
else if (((RetrofitError) e).getResponse().getStatus() == 401) {
try {
GoogleAuthUtil.clearToken(getActivity().getApplicationContext(), token);
// if this is the retry, call the auth activity.
if (retry){
showToast(R.string.auth_error);
mListener.doAuthInActivity();
}
return false;
} catch (Exception e1) {
handleException(e1);
}
}
}
return true;
}
private boolean handleException(final Exception e){
return handleException(e, false);
}
private void showRefreshResult(String result){
Toast toast = Toast.makeText(getActivity().getApplicationContext(),
result,
Toast.LENGTH_LONG);
TextView v = (TextView) toast.getView().findViewById(android.R.id.message);
v.setGravity(Gravity.CENTER);
toast.show();
}
@Override
protected void onPostExecute(Void param){
// update UI
swipeRefreshLayout.setRefreshing(false);
showRefreshResult(refreshResult);
}
}
private class SearchTask extends AsyncTask<String, Void, Void>{
@Override
protected void onPreExecute (){
if (searchTask != null){
searchTask.cancel(true);
}
searchTask = this;
}
@Override
protected Void doInBackground(String... params) {
SearchUtil.searchByPortalName(
totalPortalDetails,
((PortalListAdapter)recyclerView.getAdapter()).dataSet,
params[0]
);
return null;
}
@Override
protected void onPostExecute(Void param){
super.onPostExecute(param);
recyclerView.getAdapter().notifyDataSetChanged();
searchTask = null;
}
@Override
protected void onCancelled(Void result){
searchTask = null;
}
}
private void updateCountText(int[] counts){
if (counts!= null){
countEverything.setText(Integer.toString(counts[0]));
countAccepted.setText(Integer.toString(counts[1]));
countRejected.setText(Integer.toString(counts[2]));
countWaiting.setText(Integer.toString(counts[3]));
// navigation
totalPortals.setText(Integer.toString(counts[0]));
totalSubmission.setText(Integer.toString(counts[4]));
totalEdit.setText(Integer.toString(counts[5]));
}
}
public interface OnFragmentInteractionListener {
public void doAuthInActivity();
public void portalItemClicked(PortalDetail clickedPortal, View clickedView);
}
}