package com.gaiagps.iburn.activity;
import android.Manifest;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.InputType;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import com.gaiagps.iburn.PrefsHelper;
import com.gaiagps.iburn.R;
import com.gaiagps.iburn.SECRETS;
import com.gaiagps.iburn.SearchQueryProvider;
import com.gaiagps.iburn.Searchable;
import com.gaiagps.iburn.Subscriber;
import com.gaiagps.iburn.database.DataProvider;
import com.gaiagps.iburn.database.Embargo;
import com.gaiagps.iburn.fragment.BrowseListViewFragment;
import com.gaiagps.iburn.fragment.ExploreListViewFragment;
import com.gaiagps.iburn.fragment.FavoritesListViewFragment;
import com.gaiagps.iburn.fragment.FeedbackFragment;
import com.gaiagps.iburn.fragment.GoogleMapFragment;
import com.gaiagps.iburn.fragment.MapPlaceHolderFragment;
import com.gaiagps.iburn.fragment.PlayaListViewFragment;
import com.gaiagps.iburn.service.DataUpdateService;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.UpdateManager;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import butterknife.BindView;
import butterknife.ButterKnife;
import permissions.dispatcher.NeedsPermission;
import permissions.dispatcher.OnPermissionDenied;
import permissions.dispatcher.RuntimePermissions;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import timber.log.Timber;
@RuntimePermissions
public class MainActivity extends AppCompatActivity implements SearchQueryProvider {
private static final String HOCKEY_ID = SECRETS.HOCKEY_ID;
private static final int REQUEST_CODE_RECOVER_PLAY_SERVICES = 1001;
private boolean googlePlayServicesMissing = false;
@BindView(R.id.parent)
ViewGroup mParent;
@BindView(R.id.pager)
ViewPager mViewPager;
@BindView(R.id.tabs)
TabLayout mTabs;
@BindView(R.id.fab)
FloatingActionButton mFab;
private PrefsHelper prefs;
private IBurnPagerAdapter mPagerAdapter;
private String mCurFilter;
private Snackbar embargoSnackbar;
/**
* Fragments to appear in main ViewPager
*/
private static List<IBurnPagerAdapter.IBurnTab> sTabs
= new ArrayList<IBurnPagerAdapter.IBurnTab>() {{
add(IBurnPagerAdapter.IBurnTab.MAP);
add(IBurnPagerAdapter.IBurnTab.EXPLORE);
add(IBurnPagerAdapter.IBurnTab.BROWSE);
add(IBurnPagerAdapter.IBurnTab.FAVORITES);
add(IBurnPagerAdapter.IBurnTab.FEEDBACK);
}};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (false) { //BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectCustomSlowCalls()
.penaltyLog()
.build());
// StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
// .detect
// .penaltyLog()
// .build());
}
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
prefs = new PrefsHelper(this);
if (checkPlayServices()) {
setupFragmentStatePagerAdapter();
if (prefs.didShowWelcome() && Build.VERSION.SDK_INT >= 23 /* Use version code */) {
mPagerAdapter.setWaitingForLocationPermission(true);
MainActivityPermissionsDispatcher.gotLocationPermissionWithCheck(MainActivity.this);
}
}
if (!checkPlayServices()) {
googlePlayServicesMissing = true;
}
Timber.d("onCreate");
if (!prefs.didShowWelcome()) {
showWelcome();
}
if (!prefs.didScheduleUpdate()) {
DataUpdateService.scheduleAutoUpdate(this);
prefs.setDidScheduleUpdate(true);
}
// Mock update (Comment out DataService.scheduleAutoUpdate above)
// IBurnService service = new IBurnService(this, new MockIBurnApi(this));
// service.updateData().subscribe();
if (Embargo.isEmbargoActive(prefs)) {
Observable.timer(1, TimeUnit.SECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(counter -> {
final SimpleDateFormat dayFormatter = new SimpleDateFormat("EEEE M/d", Locale.US);
embargoSnackbar = Snackbar.make(mParent, getString(R.string.embargo_snackbar_msg, dayFormatter.format(Embargo.EMBARGO_DATE)), Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.enter_unlock_code, view -> showUnlockDialog());
embargoSnackbar.show();
});
}
handleIntent(getIntent());
//checkForUpdates();
}
public void onSearchClick(View view) {
startActivity(new Intent(this, SearchActivity.class));
}
@Override
protected void onResume() {
super.onResume();
checkForCrashes();
if (googlePlayServicesMissing && checkPlayServices()) {
setupFragmentStatePagerAdapter();
googlePlayServicesMissing = false;
}
}
public void onStart() {
super.onStart();
if (mPagerAdapter != null) {
Fragment currentFrag = mPagerAdapter.getCurrentFragment();
if (currentFrag instanceof PlayaListViewFragment) {
Timber.d("Refreshing data on current fragment onStart");
((PlayaListViewFragment) currentFrag).reSubscribeToData();
}
}
}
@OnPermissionDenied(Manifest.permission.ACCESS_FINE_LOCATION)
@NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
void gotLocationPermission() {
// Add GoogleMapFragment (Needs to be added after permission acquired)
// If permission was denied, user location simply won't display
mPagerAdapter.setWaitingForLocationPermission(false);
}
private void showWelcome() {
Intent welcomeIntent = new Intent(getApplicationContext(), WelcomeActivity.class);
welcomeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(welcomeIntent);
finish();
}
public void showUnlockDialog() {
AlertDialog.Builder alert = new AlertDialog.Builder(this, R.style.Theme_Iburn_Dialog);
alert.setTitle(getString(R.string.enter_unlock_password));
// Set an EditText view to get user input
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
alert.setView(input);
alert.setPositiveButton(getString(R.string.ok), (dialog, whichButton) -> {
String pwGuess = input.getText().toString();
if (pwGuess.equals(SECRETS.UNLOCK_CODE)) {
prefs.setEnteredValidUnlockCode(true);
// Notify all observers that embargo is clear
DataProvider.getInstance(getApplicationContext()).subscribe(DataProvider::endUpgrade);
new AlertDialog.Builder(MainActivity.this, R.style.Theme_Iburn_Dialog)
.setTitle(getString(R.string.victory))
.setMessage(getString(R.string.location_data_unlocked))
.setPositiveButton(R.string.ok, (dialog1, which) -> {
})
.show();
} else {
dialog.cancel();
new AlertDialog.Builder(MainActivity.this, R.style.Theme_Iburn_Dialog)
.setTitle(getString(R.string.invalid_password))
.setMessage("Bummer.")
.show();
}
});
alert.setNegativeButton(getString(R.string.cancel), (dialog, whichButton) -> {
});
alert.show();
// No effect :(
// InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// imm.showSoftInput(input, InputMethodManager.SHOW_FORCED);
}
@Override
protected void onNewIntent(Intent intent) {
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
//use the query to search your data somehow
if (mPagerAdapter.getCurrentFragment() instanceof Searchable) {
dispatchSearchQuery(query);
} else
Timber.d("Current fragment does not implement Searchable");
}
}
/**
* Dispatch a search query to the current Fragment in the FragmentPagerAdapter
*/
private void dispatchSearchQuery(String query) {
mCurFilter = query;
if (mPagerAdapter.getCurrentFragment() instanceof Searchable) {
Timber.d("dispatch query '%s", query);
((Searchable) mPagerAdapter.getCurrentFragment()).onSearchQueryRequested(query);
}
}
private void setupFragmentStatePagerAdapter() {
mPagerAdapter = new IBurnPagerAdapter(this, sTabs, mFab);
mPagerAdapter.setSearchQueryProvider(this);
mViewPager.setAdapter(mPagerAdapter);
mTabs.setupWithViewPager(mViewPager);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public String getCurrentQuery() {
return mCurFilter;
}
/**
* Adapter that takes a List of Pairs representing a Fragment and Title
* for pairing with a tabbed ViewPager. Remove the IconTabProvider implementation
* <p>
* Each Fragment must have a no-arg newInstance() method.
*/
public static class IBurnPagerAdapter extends FragmentStatePagerAdapter {
private Context mContext;
private List<IBurnTab> mTabs;
private FloatingActionButton mFab;
private Fragment mCurrentPrimaryItem;
private SearchQueryProvider mSearchQueryProvider;
private int mLastPosition = -1;
private boolean waitingForLocationPermission;
public enum IBurnTab {
// Icons currently unused
MAP(R.string.map_tab, R.drawable.ic_brc, GoogleMapFragment.class),
EXPLORE(R.string.explore_tab, R.drawable.ic_calendar, ExploreListViewFragment.class),
BROWSE(R.string.browse_tab, R.drawable.ic_camp, BrowseListViewFragment.class),
FAVORITES(R.string.fav_tab, R.drawable.ic_heart, FavoritesListViewFragment.class),
FEEDBACK(R.string.feedback_tab, R.drawable.ic_heart, FeedbackFragment.class);
private final Class<? extends Fragment> mFragClass;
private final Integer mTitleResId;
private final Integer mIconResId;
IBurnTab(final Integer titleResId,
final Integer iconResId,
final Class<? extends Fragment> fragClass) {
mTitleResId = titleResId;
mIconResId = iconResId;
mFragClass = fragClass;
}
public Class<? extends Fragment> getFragmentClass() {
return mFragClass;
}
public Integer getTitleResId() {
return mTitleResId;
}
public Integer getIconResId() {
return mIconResId;
}
}
public IBurnPagerAdapter(FragmentActivity host, List<IBurnTab> tabs, FloatingActionButton fab) {
super(host.getSupportFragmentManager());
mContext = host;
mTabs = tabs;
mFab = fab;
}
public void setWaitingForLocationPermission(boolean waitingForLocationPermission) {
this.waitingForLocationPermission = waitingForLocationPermission;
Timber.d("NotifyingDataSetChanged for permission status %b", waitingForLocationPermission);
notifyDataSetChanged();
}
public void setSearchQueryProvider(SearchQueryProvider provider) {
mSearchQueryProvider = provider;
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public int getItemPosition (Object object) {
int resultValue = POSITION_UNCHANGED;
if (object instanceof MapPlaceHolderFragment) {
resultValue = POSITION_NONE;
}
Timber.d("getItemPosition for %s result %d",
object.getClass().getSimpleName(), resultValue);
return resultValue;
}
@Override
public void destroyItem(ViewGroup collection, int position, Object o) {
super.destroyItem(collection, position, o);
}
@Override
public Fragment getItem(int position) {
try {
Fragment newFrag = null;
Class<? extends Fragment> fragmentClass = mTabs.get(position).getFragmentClass();
if (fragmentClass.equals(GoogleMapFragment.class) && waitingForLocationPermission) {
newFrag = new MapPlaceHolderFragment();
} else {
newFrag = fragmentClass.newInstance();
}
Timber.d("get Item %d. Returned class %s", position, newFrag.getClass().getSimpleName());
return newFrag; //.getMethod("newInstance", null).invoke(null, null);
} catch (Exception e) {
// Actually (InstantiationException | IllegalAccessException), but we don't have
// Java7 multi-catch pre API 19. We don't treat these exceptions separately,
// so here we are catching Exception for now
Timber.w("Failed to getItem for position %d", position);
e.printStackTrace();
throw new IllegalStateException("Unexpected ViewPager item requested: " + position);
}
}
@Override
public void setPrimaryItem(ViewGroup container, int position, final Object object) {
super.setPrimaryItem(container, position, object);
mCurrentPrimaryItem = (Fragment) object;
if (mLastPosition != position) {
// Hide Fab on last page
if (position == getCount() - 1) {
mFab.hide();
} else {
mFab.show();
}
if (mCurrentPrimaryItem instanceof Subscriber) {
Timber.d("Subscribing %d to data", position);
// We delay data subscription for a few milliseconds to allow
// the tab-switch transition to complete before layout occurs
Observable.timer(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe(time -> {
((Subscriber) object).subscribeToData();
}, throwable -> Timber.e(throwable, "Failed to subscribe fragment to data"));
}
//if (mCurrentPrimaryItem instanceof Searchable && mSearchQueryProvider != null) {
// Remove for now -- With a dedicated search screen we'll focus the list fragments
// on browsing, not showing search results. If we decide to re-enable this
// we should not deliver an unchanged search query for performance
// Update the fragment with the current query
//((Searchable) mCurrentPrimaryItem).onSearchQueryRequested(mSearchQueryProvider.getCurrentQuery());
//}
//String title = mContext.getString(sTabs.get(position).getTitleResId());
//Log.i(TAG, "Setting tab title " + title);
mLastPosition = position;
}
}
public Fragment getCurrentFragment() {
return mCurrentPrimaryItem;
}
@Override
public CharSequence getPageTitle(int position) {
return mContext.getString(mTabs.get(position).getTitleResId());
}
public int getPageIconResId(int i) {
return mTabs.get(i).getIconResId();
}
}
private boolean checkPlayServices() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (status != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(status)) {
showErrorDialog(status);
} else {
Toast.makeText(this, getString(R.string.device_not_supported),
Toast.LENGTH_LONG).show();
finish();
}
return false;
}
return true;
}
void showErrorDialog(int code) {
GooglePlayServicesUtil.getErrorDialog(code, this,
REQUEST_CODE_RECOVER_PLAY_SERVICES).show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_RECOVER_PLAY_SERVICES:
if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, getString(R.string.requres_google_play),
Toast.LENGTH_SHORT).show();
finish();
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
private void checkForCrashes() {
CrashManager.register(this, HOCKEY_ID);
}
private void checkForUpdates() {
// Remove this for store builds!
UpdateManager.register(this, HOCKEY_ID);
}
public void clearEmbargoSnackbar() {
if (embargoSnackbar != null) {
embargoSnackbar.dismiss();
embargoSnackbar = null;
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
}