/*
* Copyright (C) 2010-2017 Stichting Akvo (Akvo Foundation)
*
* This file is part of Akvo Flow.
*
* Akvo Flow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Akvo Flow 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Akvo Flow. If not, see <http://www.gnu.org/licenses/>.
*/
package org.akvo.flow.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import org.akvo.flow.BuildConfig;
import org.akvo.flow.R;
import org.akvo.flow.app.FlowApp;
import org.akvo.flow.data.database.SurveyDbAdapter;
import org.akvo.flow.data.database.SurveyInstanceStatus;
import org.akvo.flow.data.preference.Prefs;
import org.akvo.flow.domain.Survey;
import org.akvo.flow.domain.SurveyGroup;
import org.akvo.flow.domain.User;
import org.akvo.flow.domain.apkupdate.ApkUpdateStore;
import org.akvo.flow.domain.apkupdate.GsonMapper;
import org.akvo.flow.domain.apkupdate.ViewApkData;
import org.akvo.flow.service.BootstrapService;
import org.akvo.flow.service.DataSyncService;
import org.akvo.flow.service.SurveyDownloadService;
import org.akvo.flow.service.SurveyedDataPointSyncService;
import org.akvo.flow.service.TimeCheckService;
import org.akvo.flow.ui.Navigator;
import org.akvo.flow.ui.fragment.DatapointsFragment;
import org.akvo.flow.ui.fragment.DrawerFragment;
import org.akvo.flow.ui.fragment.RecordListListener;
import org.akvo.flow.util.ConstantUtil;
import org.akvo.flow.util.PlatformUtil;
import org.akvo.flow.util.StatusUtil;
import org.akvo.flow.util.ViewUtil;
import java.lang.ref.WeakReference;
import timber.log.Timber;
public class SurveyActivity extends AppCompatActivity implements RecordListListener,
DrawerFragment.DrawerListener, DatapointsFragment.DatapointFragmentListener {
// Argument to be passed to list/map fragments
public static final String EXTRA_SURVEY_GROUP = "survey_group";
private static final String DATA_POINTS_FRAGMENT_TAG = "datapoints_fragment";
private static final String DRAWER_FRAGMENT_TAG = "f";
private SurveyDbAdapter mDatabase;
private SurveyGroup mSurveyGroup;
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
private DrawerFragment mDrawer;
private CharSequence mDrawerTitle, mTitle;
private Navigator navigator = new Navigator();
private Prefs prefs;
private ApkUpdateStore apkUpdateStore;
/**
* BroadcastReceiver to notify of surveys synchronisation. This should be
* fired from {@link SurveyDownloadService}.
*/
private final BroadcastReceiver mSurveysSyncReceiver = new SurveySyncBroadcastReceiver(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.survey_activity);
initializeToolBar();
mDatabase = new SurveyDbAdapter(this);
mDatabase.open();
mTitle = mDrawerTitle = getString(R.string.app_name);
// Init navigation drawer
initNavigationDrawer();
initDataPointsFragment(savedInstanceState);
prefs = new Prefs(getApplicationContext());
apkUpdateStore = new ApkUpdateStore(new GsonMapper(), prefs);
// Start the setup Activity if necessary.
boolean noDevIdYet = false;
if (!prefs.getBoolean(Prefs.KEY_SETUP, false)) {
noDevIdYet = true;
navigator.navigateToAddUser(this);
}
startServices(noDevIdYet);
//When the app is restarted we need to display the current user
if (savedInstanceState == null) {
displaySelectedUser();
}
}
private void initializeToolBar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
}
private void initNavigationDrawer() {
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
FragmentManager supportFragmentManager = getSupportFragmentManager();
mDrawer = (DrawerFragment) supportFragmentManager.findFragmentByTag(DRAWER_FRAGMENT_TAG);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.string.drawer_open, R.string.drawer_close) {
/** Called when a drawer has settled in a completely closed state. */
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
mDrawer.onDrawerClosed();
getSupportActionBar().setTitle(mTitle);
supportInvalidateOptionsMenu();
}
/**
* Called when a drawer has settled in a completely open state.
*/
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
//prevent the back icon from showing
super.onDrawerSlide(drawerView, 0);
getSupportActionBar().setTitle(mDrawerTitle);
supportInvalidateOptionsMenu();
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
//disable drawer animation
super.onDrawerSlide(drawerView, 0);
}
};
mDrawerLayout.addDrawerListener(mDrawerToggle);
// Automatically select the survey
SurveyGroup sg = mDatabase.getSurveyGroup(FlowApp.getApp().getSurveyGroupId());
if (sg != null) {
onSurveySelected(sg);
} else {
mDrawerLayout.openDrawer(GravityCompat.START);
}
}
private void initDataPointsFragment(Bundle savedInstanceState) {
FragmentManager supportFragmentManager = getSupportFragmentManager();
if (savedInstanceState == null
|| supportFragmentManager.findFragmentByTag(DATA_POINTS_FRAGMENT_TAG) == null) {
DatapointsFragment datapointsFragment = DatapointsFragment.newInstance(mSurveyGroup);
supportFragmentManager.beginTransaction()
.replace(R.id.content_frame, datapointsFragment, DATA_POINTS_FRAGMENT_TAG)
.commit();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case ConstantUtil.REQUEST_ADD_USER:
if (resultCode == RESULT_OK) {
displaySelectedUser();
prefs.setBoolean(Prefs.KEY_SETUP, true);
// Trigger the delayed services, so the first
// backend connections uses the new Device ID
startService(new Intent(this, SurveyDownloadService.class));
startService(new Intent(this, DataSyncService.class));
} else if (!prefs.getBoolean(Prefs.KEY_SETUP, false)) {
finish();
}
break;
}
}
@Override
public void onResume() {
super.onResume();
// Delete empty responses, if any
mDatabase.deleteEmptySurveyInstances();
mDatabase.deleteEmptyRecords();
registerReceiver(mSurveysSyncReceiver,
new IntentFilter(getString(R.string.action_surveys_sync)));
ViewApkData apkData = apkUpdateStore.getApkData();
boolean shouldNotifyUpdate = apkUpdateStore.shouldNotifyNewVersion();
if (apkData != null && shouldNotifyUpdate && PlatformUtil
.isNewerVersion(BuildConfig.VERSION_NAME, apkData.getVersion())) {
apkUpdateStore.saveAppUpdateNotifiedTime();
navigator.navigateToAppUpdate(this, apkData);
}
}
@Override
public void onPause() {
super.onPause();
unregisterReceiver(mSurveysSyncReceiver);
}
@Override
public void onDestroy() {
super.onDestroy();
mDatabase.close();
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
getSupportActionBar().setTitle(mTitle);
}
private void startServices(boolean waitForDeviceId) {
if (!StatusUtil.hasExternalStorage()) {
ViewUtil.showConfirmDialog(R.string.checksd, R.string.sdmissing, this,
false,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SurveyActivity.this.finish();
}
},
null);
} else {
if (!waitForDeviceId) {
startService(new Intent(this, SurveyDownloadService.class));
startService(new Intent(this, DataSyncService.class));
}
startService(new Intent(this, BootstrapService.class));
startService(new Intent(this, TimeCheckService.class));
}
}
@Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
String surveyedLocaleId = intent.getDataString();
onRecordSelected(surveyedLocaleId);
}
}
@Override
public void onUserSelected(User user) {
FlowApp.getApp().setUser(user);
mDrawer.load();
mDrawerLayout.closeDrawers();
displaySelectedUser();
}
@Override
public void onSurveySelected(SurveyGroup surveyGroup) {
mSurveyGroup = surveyGroup;
CharSequence title = mSurveyGroup != null ? mSurveyGroup.getName() : mDrawerTitle;
long id = mSurveyGroup != null ? mSurveyGroup.getId() : SurveyGroup.ID_NONE;
setTitle(title);
FlowApp.getApp().setSurveyGroupId(id);
DatapointsFragment f = (DatapointsFragment) getSupportFragmentManager().findFragmentByTag(
DATA_POINTS_FRAGMENT_TAG);
if (f != null) {
f.refresh(mSurveyGroup);
} else {
supportInvalidateOptionsMenu();
}
mDrawer.load();
mDrawerLayout.closeDrawers();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean showItems =
!mDrawerLayout.isDrawerOpen(GravityCompat.START) && mSurveyGroup != null;
for (int i = 0; i < menu.size(); i++) {
menu.getItem(i).setVisible(showItems);
}
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return mDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
}
@Override
public void onRecordSelected(String surveyedLocaleId) {
final User user = FlowApp.getApp().getUser();
// Ensure user is logged in
if (user == null) {
Toast.makeText(SurveyActivity.this, R.string.mustselectuser,
Toast.LENGTH_LONG).show();
return;
}
// Non-monitored surveys display the form directly
if (!mSurveyGroup.isMonitored()) {
Survey registrationForm = mDatabase.getRegistrationForm(mSurveyGroup);
if (registrationForm == null) {
Toast.makeText(this, R.string.error_missing_form, Toast.LENGTH_LONG).show();
return;
} else if (!registrationForm.isHelpDownloaded()) {
Toast.makeText(this, R.string.error_missing_cascade, Toast.LENGTH_LONG).show();
return;
}
final String formId = registrationForm.getId();
long formInstanceId;
boolean readOnly = false;
Cursor c = mDatabase.getFormInstances(surveyedLocaleId);
if (c.moveToFirst()) {
formInstanceId = c.getLong(SurveyDbAdapter.FormInstanceQuery._ID);
int status = c.getInt(SurveyDbAdapter.FormInstanceQuery.STATUS);
readOnly = status != SurveyInstanceStatus.SAVED;
} else {
formInstanceId = mDatabase
.createSurveyRespondent(formId, registrationForm.getVersion(), user,
surveyedLocaleId);
}
c.close();
navigator.navigateToFormActivity(this, surveyedLocaleId, user, formId, formInstanceId, readOnly,
mSurveyGroup);
} else {
navigator.navigateToRecordActivity(this, surveyedLocaleId, mSurveyGroup);
}
}
private void displaySelectedUser() {
User user = FlowApp.getApp().getUser();
if (user != null) {
Toast.makeText(this, getString(R.string.logged_in_as) + " " + user.getName(),
Toast.LENGTH_LONG).show();
}
}
@Override
public void refreshMenu() {
supportInvalidateOptionsMenu();
}
@Override
public void onSyncRecordsTap(long surveyGroupId) {
Toast.makeText(this, R.string.syncing_records, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(this, SurveyedDataPointSyncService.class);
intent.putExtra(SurveyedDataPointSyncService.SURVEY_GROUP, surveyGroupId);
startService(intent);
}
@Override
public boolean onSearchTap() {
return onSearchRequested();
}
private static class SurveySyncBroadcastReceiver extends BroadcastReceiver {
private final WeakReference<SurveyActivity> activityWeakReference;
private SurveySyncBroadcastReceiver(SurveyActivity activity) {
this.activityWeakReference = new WeakReference<>(activity);
}
@Override
public void onReceive(Context context, Intent intent) {
Timber.i("Surveys have been synchronised. Refreshing data...");
SurveyActivity surveyActivity = activityWeakReference.get();
if (surveyActivity != null) {
surveyActivity.reloadDrawer();
}
}
}
private void reloadDrawer() {
mDrawer.load();
}
}