/*
* Copyright (c) 2015 Ushahidi Inc
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program in the file LICENSE-AGPL. If not, see
* https://www.gnu.org/licenses/agpl-3.0.html
*/
package com.ushahidi.platform.mobile.app.presentation.view.ui.activity;
import com.orhanobut.dialogplus.DialogPlus;
import com.orhanobut.dialogplus.Holder;
import com.ushahidi.platform.mobile.app.R;
import com.ushahidi.platform.mobile.app.data.api.account.PlatformSession;
import com.ushahidi.platform.mobile.app.data.api.account.SessionManager;
import com.ushahidi.platform.mobile.app.data.api.oauth.UshAccessTokenManager;
import com.ushahidi.platform.mobile.app.presentation.UshahidiApplication;
import com.ushahidi.platform.mobile.app.presentation.di.components.form.DaggerListFormComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.form.ListFormComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.AddPostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.DaggerAddPostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.DaggerListPostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.DaggerMapPostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.DaggerPostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.DaggerUserProfileComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.ListPostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.MapPostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.PostComponent;
import com.ushahidi.platform.mobile.app.presentation.di.components.post.UserProfileComponent;
import com.ushahidi.platform.mobile.app.presentation.model.DeploymentModel;
import com.ushahidi.platform.mobile.app.presentation.model.FormModel;
import com.ushahidi.platform.mobile.app.presentation.model.UserProfileModel;
import com.ushahidi.platform.mobile.app.presentation.presenter.form.ListFormPresenter;
import com.ushahidi.platform.mobile.app.presentation.presenter.post.PostPresenter;
import com.ushahidi.platform.mobile.app.presentation.state.LoadUserProfileEvent;
import com.ushahidi.platform.mobile.app.presentation.state.ReloadPostEvent;
import com.ushahidi.platform.mobile.app.presentation.util.Utility;
import com.ushahidi.platform.mobile.app.presentation.view.form.ListFormView;
import com.ushahidi.platform.mobile.app.presentation.view.post.PostView;
import com.ushahidi.platform.mobile.app.presentation.view.ui.adapter.FormAdapter;
import com.ushahidi.platform.mobile.app.presentation.view.ui.fragment.ListPostFragment;
import com.ushahidi.platform.mobile.app.presentation.view.ui.fragment.MapPostFragment;
import com.ushahidi.platform.mobile.app.presentation.view.ui.fragment.UserProfileFragment;
import com.ushahidi.platform.mobile.app.presentation.view.ui.widget.CustomGridHolder;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.OvershootInterpolator;
import java.util.ArrayList;
import java.util.List;
import butterknife.Bind;
import butterknife.OnClick;
import rx.android.schedulers.AndroidSchedulers;
/**
* @author Ushahidi Team <team@ushahidi.com>
*/
public class PostActivity extends BaseAppActivity implements PostView, ListFormView {
private static final String STATE_PARAM_SELECTED_TAB
= "com.ushahidi.android.presentation.view.ui.activity.SELECTED_TAB";
private static final String BUNDLE_STATE_PARAM_CURRENT_MENU
= "com.ushahidi.android.presentation.view.ui.activity.BUNDLE_STATE_PARAM_CURRENT_MENU";
private static final int DEPLOYMENTS_MENU_ITEMS_GROUP_ID = 1;
private static final int MISC_MENU_ITEMS = 2;
// Using negative values for the static nav drawer menu items because
// we want them to be unique from the dynamic menus
private static final int MANAGE_DEPLOYMENT_MENU_ID = -1;
private static final int FEEDBACK_MENU_ID = -2;
private static final int ABOUT_MENU_ID = -3;
private static final int LOGOUT_MENU_ID = -4;
private static final int START_DELAY_ANIM = 300;
private static final int DURATION_ANIM = 400;
private static int mCurrentItem;
@Bind(R.id.toolbar)
Toolbar mToolbar;
@Bind(R.id.drawer_layout)
DrawerLayout mDrawerLayout;
@Bind((R.id.main_navigation_drawer))
NavigationView mNavigationView;
@Bind(R.id.post_viewpager)
ViewPager mViewPager;
@Bind(R.id.post_fab)
FloatingActionButton mFab;
@Bind(R.id.post_tabs)
TabLayout mTabLayout;
private PostPresenter mPostPresenter;
private ListFormPresenter mListFormPresenter;
private UshAccessTokenManager mUshAccessTokenManager;
private SessionManager<PlatformSession> mSessionManager;
private ListPostComponent mListPostComponent;
private MapPostComponent mMapPostComponent;
private UserProfileComponent mUserProfileComponent;
private AddPostComponent mAddPostComponent;
private ActionBarDrawerToggle mDrawerToggle;
private int mCurrentMenu;
private List<DeploymentModel> mDeploymentModelList;
private boolean mPendingIntroAnimation;
private FormAdapter mFormAdapter;
private SubMenu mSubMenuMisc;
/**
* Default constructor
*/
public PostActivity() {
super(R.layout.activity_post, R.menu.main);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
injector();
initViews();
if (savedInstanceState != null) {
mCurrentItem = savedInstanceState.getInt(STATE_PARAM_SELECTED_TAB);
mCurrentMenu = savedInstanceState
.getInt(BUNDLE_STATE_PARAM_CURRENT_MENU, 0);
} else {
mPendingIntroAnimation = true;
}
UserProfileFragment userProfileFragment = (UserProfileFragment) getSupportFragmentManager()
.findFragmentById(R.id.user_profile_fragment);
if (userProfileFragment != null) {
userProfileFragment.setDrawerLayout(mDrawerLayout);
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(STATE_PARAM_SELECTED_TAB, mCurrentItem);
savedInstanceState.putInt(BUNDLE_STATE_PARAM_CURRENT_MENU, mCurrentMenu);
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
mPostPresenter.resume();
showLoginUserProfile();
mViewPager.setCurrentItem(mCurrentItem);
}
@Override
public void onPause() {
super.onPause();
mCurrentItem = mViewPager.getCurrentItem();
}
@Override
public void onDestroy() {
super.onDestroy();
mPostPresenter.destroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
if (mPendingIntroAnimation) {
mPendingIntroAnimation = false;
startIntroAnimation();
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
case R.id.menu_post_search:
mListPostComponent.launcher().launchSearchPost();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void initViews() {
setSupportActionBar(mToolbar);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.app_name,
R.string.app_name);
mDrawerToggle.setDrawerIndicatorEnabled(true);
mDrawerLayout.setDrawerListener(mDrawerToggle);
if (mNavigationView != null) {
setNavigationViewMenuItems(mNavigationView.getMenu());
setupDrawerContent(mNavigationView);
}
if (mViewPager != null) {
setupViewPager(mViewPager);
}
mTabLayout.setupWithViewPager(mViewPager);
mFormAdapter = new FormAdapter(this);
}
private void injector() {
PostComponent postComponent = DaggerPostComponent.builder()
.appComponent(getAppComponent())
.activityModule(getActivityModule())
.build();
ListFormComponent listFormComponent = DaggerListFormComponent.builder()
.appComponent(getAppComponent())
.activityModule(getActivityModule())
.build();
mListPostComponent = DaggerListPostComponent.builder()
.appComponent(getAppComponent())
.activityModule(getActivityModule())
.build();
mMapPostComponent = DaggerMapPostComponent.builder()
.appComponent(getAppComponent())
.activityModule(getActivityModule())
.build();
mUserProfileComponent = DaggerUserProfileComponent.builder()
.appComponent(getAppComponent())
.activityModule(getActivityModule())
.build();
mAddPostComponent = DaggerAddPostComponent.builder()
.appComponent(getAppComponent())
.activityModule(getActivityModule())
.build();
mUshAccessTokenManager = getAppComponent().ushahidiTokenManager();
mSessionManager = getAppComponent().platformSessionManager();
mPostPresenter = postComponent.postPresenter();
mPostPresenter.setPostView(this);
mListFormPresenter = listFormComponent.listFormPresenter();
mListFormPresenter.setView(this);
}
private void setupViewPager(ViewPager viewPager) {
TabPagerAdapter adapter = new TabPagerAdapter(getSupportFragmentManager(), viewPager);
adapter.addFragment(ListPostFragment.newInstance(), getString(R.string.list));
adapter.addFragment(MapPostFragment.newInstance(), getString(R.string.map));
viewPager.setAdapter(adapter);
}
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener(
menuItem -> {
switch (menuItem.getItemId()) {
// All static menu items have their id set to a negative value.
// We use that to determine which activity or fragment to launch.
case MANAGE_DEPLOYMENT_MENU_ID:
getListPostComponent().launcher().launchListDeployment();
break;
case FEEDBACK_MENU_ID:
getListPostComponent().launcher().launchFeedback();
break;
case ABOUT_MENU_ID:
getListPostComponent().launcher().launchAbout();
break;
case LOGOUT_MENU_ID:
getListPostComponent().launcher().launchLogout();
default:
if (menuItem.getGroupId() == DEPLOYMENTS_MENU_ITEMS_GROUP_ID) {
mCurrentMenu = menuItem.getItemId();
// Mark the deployment active if it's not
if (mDeploymentModelList.get(menuItem.getItemId()).getStatus()
.equals(DeploymentModel.Status.DEACTIVATED)) {
mPostPresenter.activateDeployment(mDeploymentModelList,
menuItem.getItemId());
menuItem.setChecked(true);
}
menuItem.setCheckable(true);
menuItem.setChecked(true);
mToolbar.setTitle(menuItem.getTitle());
}
}
mDrawerLayout.closeDrawers();
return true;
});
}
private void setNavigationViewMenuItems(@NonNull Menu menu) {
// Reset menu items to avoid duplicates because we reinitialize them
menu.clear();
if (!Utility.isCollectionEmpty(mDeploymentModelList)) {
SubMenu subMenu = menu
.addSubMenu(Menu.NONE, Menu.FIRST, Menu.NONE, R.string.deployments);
subMenu.setGroupCheckable(DEPLOYMENTS_MENU_ITEMS_GROUP_ID, true, true);
// Use item position as the menu item's id that way we can retrieve the individual
// deployment when user clicks on it to make it the active deployment
for (int pos = 0; pos < mDeploymentModelList.size(); pos++) {
subMenu.add(DEPLOYMENTS_MENU_ITEMS_GROUP_ID, pos, pos,
mDeploymentModelList.get(pos).getTitle()).setIcon(
R.drawable.ic_action_globe);
if (mDeploymentModelList.get(pos).getStatus()
.equals(DeploymentModel.Status.ACTIVATED)) {
mNavigationView.getMenu().findItem(pos).setChecked(true);
mToolbar.setTitle(mNavigationView.getMenu().findItem(pos).getTitle());
} else {
mNavigationView.getMenu().findItem(pos).setChecked(false);
}
}
}
mSubMenuMisc = menu.addSubMenu(Menu.NONE, Menu.FIRST, Menu.NONE, R.string.actions);
mSubMenuMisc.add(MISC_MENU_ITEMS, MANAGE_DEPLOYMENT_MENU_ID, 1, R.string.manage_deployments)
.setIcon(R.drawable.ic_action_map);
mSubMenuMisc.add(MISC_MENU_ITEMS, FEEDBACK_MENU_ID, 2, R.string.send_feedback)
.setIcon(R.drawable.ic_action_help);
mSubMenuMisc.add(MISC_MENU_ITEMS, ABOUT_MENU_ID, 3, R.string.about)
.setIcon(R.drawable.ic_action_info);
if (mSessionManager.getActiveSession() != null) {
mSubMenuMisc.add(MISC_MENU_ITEMS, LOGOUT_MENU_ID, 4, R.string.logout)
.setIcon(R.drawable.ic_action_logout);
}
mSubMenuMisc.setGroupCheckable(MISC_MENU_ITEMS, true, true);
}
/**
* Gets the list post component
*
* @return The list component
*/
public ListPostComponent getListPostComponent() {
return mListPostComponent;
}
/**
* Gets the map post component
*
* @return The map component
*/
public MapPostComponent getMapPostComponent() {
return mMapPostComponent;
}
/**
* User profile component
*
* @return The user profile component
*/
public UserProfileComponent getUserProfileComponent() {
return mUserProfileComponent;
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mDrawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
public void onBackPressed() {
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
private void startIntroAnimation() {
mFab.setTranslationY(2 * mFab.getHeight());
mToolbar.setTranslationY(-mToolbar.getHeight());
mToolbar.animate()
.translationY(0)
.setDuration(DURATION_ANIM)
.setStartDelay(START_DELAY_ANIM)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
startContentAnimation();
}
})
.start();
}
private void startContentAnimation() {
mFab.animate()
.translationY(0)
.setInterpolator(new OvershootInterpolator(1.f))
.setStartDelay(START_DELAY_ANIM)
.setDuration(DURATION_ANIM)
.start();
// Cause the post list to reload
UshahidiApplication.getRxEventBusInstance().send(new ReloadPostEvent(null));
}
private void showLoginUserProfile() {
mUshAccessTokenManager.getStorage().hasAccessToken()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(loggedIn -> {
if (loggedIn) {
Long profileId = mSessionManager.getActiveSession().getId();
mPostPresenter.getUserProfile(profileId);
} else {
UshahidiApplication.getRxEventBusInstance()
.send(new LoadUserProfileEvent(null));
}
}, x -> x.printStackTrace());
}
public void launchLogout() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.dialog_logout_message)
.setCancelable(false)
.setIcon(R.drawable.ic_action_globe)
.setPositiveButton(R.string.dialog_logout_btn_postive,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Clear active session
SessionManager sessionManager = getAppComponent()
.platformSessionManager();
sessionManager.clearActiveSession();
// Clear access token
UshAccessTokenManager ushAccessTokenManager = getAppComponent()
.ushahidiTokenManager();
ushAccessTokenManager.getStorage().removeAccessToken();
// Send an event
UshahidiApplication.getRxEventBusInstance()
.send(new LoadUserProfileEvent(null));
setNavigationViewMenuItems(mNavigationView.getMenu());
}
})
.setNegativeButton(R.string.dialog_logout_btn_negative,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
builder.create().show();
}
/**
* Handles FAB clicks
*
* @param view The FAB view
*/
@OnClick(R.id.post_fab)
void onFabClick(View view) {
Holder holder = new CustomGridHolder(3);
final DialogPlus dialog = DialogPlus.newDialog(this)
.setOnItemClickListener((dia, item, view1, position) -> {
final FormModel formModel = mFormAdapter.getItem(position);
mMapPostComponent.launcher()
.launchAddPost(formModel._id, formModel.getName());
dia.dismiss();
})
.setExpanded(true)
.setCancelable(true)
.setContentHolder(holder)
.setHeader(R.layout.form_dialog_header)
.setAdapter(mFormAdapter)
.setContentHeight(ViewGroup.LayoutParams.MATCH_PARENT)
.setOnBackPressListener(dialogPlus -> dialogPlus.dismiss())
.create();
dialog.show();
}
@Override
public void setActiveUserProfile(UserProfileModel userProfile) {
UshahidiApplication.getRxEventBusInstance().send(new LoadUserProfileEvent(userProfile));
}
@Override
public void setActiveDeployment(DeploymentModel deployment) {
UshahidiApplication.getRxEventBusInstance().send(new ReloadPostEvent(deployment));
}
@Override
public void deploymentList(List<DeploymentModel> deploymentModels) {
mDeploymentModelList = deploymentModels;
if (Utility.isCollectionEmpty(deploymentModels)) {
getListPostComponent().launcher().launchListDeployment();
} else {
markFirstDeploymentActive();
setNavigationViewMenuItems(mNavigationView.getMenu());
mListFormPresenter.loadFormFromDb();
}
}
@Override
public void showLoading() {
}
@Override
public void hideLoading() {
}
@Override
public void showRetry() {
}
@Override
public void hideRetry() {
}
@Override
public void showError(String s) {
}
@Override
public Context getAppContext() {
return getApplicationContext();
}
@Override
public void renderFormList(List<FormModel> formModel) {
if (!Utility.isCollectionEmpty(formModel)) {
mFormAdapter.setItems(formModel);
}
}
// This is to ensure there is always an active deployment
private void markFirstDeploymentActive() {
for (DeploymentModel deploymentModel : mDeploymentModelList) {
if (deploymentModel.getStatus().equals(DeploymentModel.Status.ACTIVATED)) {
break;
} else {
// Make the first item the active one
mPostPresenter.activateDeployment(mDeploymentModelList, 0);
}
}
}
/**
* An adapter that provides fragments for {@link ViewPager}
*/
static class TabPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments = new ArrayList<>();
private final List<String> mFragmentTitles = new ArrayList<>();
private ViewPager mPager;
/**
* Default constructor
*
* @param fm The fragment manager
* @param viewPager The view pager
*/
public TabPagerAdapter(FragmentManager fm, ViewPager viewPager) {
super(fm);
mPager = viewPager;
}
/**
* Adds a fragment to the view pager
*
* @param fragment The fragment to be added
* @param title The title of the view pager
*/
public void addFragment(Fragment fragment, String title) {
mFragments.add(fragment);
mFragmentTitles.add(title);
notifyDataSetChanged();
mPager.setCurrentItem(mCurrentItem);
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitles.get(position);
}
}
}