/**
* Copyright (C) 2013 - 2015 the enviroCar community
* <p>
* This file is part of the enviroCar app.
* <p>
* The enviroCar app 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.
* <p>
* The enviroCar app 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.
* <p>
* You should have received a copy of the GNU General Public License along
* with the enviroCar app. If not, see http://www.gnu.org/licenses/.
*/
package org.envirocar.app.handler;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import com.squareup.otto.Bus;
import org.envirocar.app.R;
import org.envirocar.app.exception.NotAcceptedTermsOfUseException;
import org.envirocar.app.exception.NotLoggedInException;
import org.envirocar.app.exception.ServerException;
import org.envirocar.app.views.ReactiveTermsOfUseDialog;
import org.envirocar.core.entity.TermsOfUse;
import org.envirocar.core.entity.User;
import org.envirocar.core.exception.DataRetrievalFailureException;
import org.envirocar.core.exception.DataUpdateFailureException;
import org.envirocar.core.exception.NotConnectedException;
import org.envirocar.core.exception.UnauthorizedException;
import org.envirocar.core.injection.InjectApplicationScope;
import org.envirocar.core.logging.Logger;
import org.envirocar.remote.DAOProvider;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import rx.Observable;
import rx.exceptions.OnErrorThrowable;
import rx.functions.Func1;
/**
* TODO JavaDoc
*
* @author dewall
*/
@Singleton
public class TermsOfUseManager {
private static final Logger LOGGER = Logger.getLogger(TermsOfUseManager.class);
// Mutex for locking when downloading.
private final Object mMutex = new Object();
protected List<TermsOfUse> list;
// Injected variables.
private final Context mContext;
private final Bus mBus;
private final UserHandler mUserManager;
private final DAOProvider mDAOProvider;
private TermsOfUse current;
/**
* Constructor.
*
* @param context
*/
@Inject
public TermsOfUseManager(@InjectApplicationScope Context context, Bus bus, UserHandler
userHandler, DAOProvider daoProvider) {
this.mContext = context;
this.mBus = bus;
this.mUserManager = userHandler;
this.mDAOProvider = daoProvider;
}
public Observable<TermsOfUse> verifyTermsOfUse(Activity activity) {
LOGGER.info("verifyTermsOfUse()");
return getCurrentTermsOfUseObservable()
.flatMap(checkTermsOfUseAcceptance(activity));
}
public <T> Observable<T> verifyTermsOfUse(Activity activity, T t) {
return verifyTermsOfUse(activity)
.map(termsOfUse -> {
LOGGER.info("User has accepted terms of use.");
return t;
});
}
public Observable<TermsOfUse> getCurrentTermsOfUseObservable() {
LOGGER.info("getCurrentTermsOfUseObservable()");
return current != null ? Observable.just(current) : getRemoteTermsOfUseObservable();
}
private Observable<TermsOfUse> getRemoteTermsOfUseObservable() {
LOGGER.info("getRemoteTermsOfUse() TermsOfUse are null. Try to fetch the last TermsOfUse.");
return mDAOProvider.getTermsOfUseDAO()
.getAllTermsOfUseObservable()
.map(termsOfUses -> {
if (termsOfUses == null || termsOfUses.isEmpty())
throw OnErrorThrowable.from(new NotConnectedException(
"Error while retrieving terms of use: " +
"Result set was null or empty"));
// Set the list of terms of uses.
TermsOfUseManager.this.list = termsOfUses;
try {
// Get the id of the first terms of use instance and fetch
// the terms of use
String id = termsOfUses.get(0).getId();
TermsOfUse inst = mDAOProvider.getTermsOfUseDAO().getTermsOfUse(id);
return inst;
} catch (DataRetrievalFailureException | NotConnectedException e) {
LOGGER.warn(e.getMessage(), e);
throw OnErrorThrowable.from(e);
}
});
}
private Func1<TermsOfUse, Observable<TermsOfUse>> checkTermsOfUseAcceptance(Activity activity) {
LOGGER.info("checkTermsOfUseAcceptance()");
return new Func1<TermsOfUse, Observable<TermsOfUse>>() {
@Override
public Observable<TermsOfUse> call(TermsOfUse termsOfUse) {
User user = mUserManager.getUser();
if (user == null) {
throw OnErrorThrowable.from(new NotLoggedInException(
mContext.getString(R.string.trackviews_not_logged_in)));
}
LOGGER.info(String.format("Retrieved terms of use for user [%s] with terms of" +
" use version [%s]", user.getUsername(), user.getTermsOfUseVersion()));
boolean hasAccepted = termsOfUse
.getIssuedDate().equals(user.getTermsOfUseVersion());
// If the user has accepted, then just return the generic type
if (hasAccepted) {
return Observable.just(termsOfUse);
}
// If the input activity is not null, then create an dialog observable.
else if (activity != null) {
return createTermsOfUseDialogObservable(user, termsOfUse, activity);
}
// Otherwise, throw an exception.
else {
throw OnErrorThrowable.from(new NotAcceptedTermsOfUseException(
"The user has not accepted the terms of use"));
}
}
};
}
public Observable createTermsOfUseDialogObservable(
User user, TermsOfUse currentTermsOfUse, Activity activity) {
return new ReactiveTermsOfUseDialog(activity, user, currentTermsOfUse)
.asObservable()
.map(new Func1<TermsOfUse, TermsOfUse>() {
@Override
public TermsOfUse call(TermsOfUse termsOfUse) {
LOGGER.info("TermsOfUseDialog: the user has accepted the ToU.");
try {
// set the terms of use
user.setTermsOfUseVersion(termsOfUse.getIssuedDate());
mDAOProvider.getUserDAO().updateUser(user);
mUserManager.setUser(user);
LOGGER.info("TermsOfUseDialog: User successfully updated");
return termsOfUse;
} catch (DataUpdateFailureException | UnauthorizedException e) {
LOGGER.warn(e.getMessage(), e);
throw OnErrorThrowable.from(e);
}
}
});
}
// /**
// * Checks if the Terms are accepted. If not, open Dialog. On positive
// * feedback, update the User.
// *
// * @param user
// * @param callback
// */
// public void askForTermsOfUseAcceptance(final User user, final PositiveNegativeCallback
// callback) {
// boolean verified = false;
// try {
// verified = verifyTermsUseOfVersion(user.getTermsOfUseVersion());
// } catch (ServerException e) {
// LOGGER.warn(e.getMessage(), e);
// return;
// }
// if (!verified) {
//
// final TermsOfUse current;
// try {
// current = getCurrentTermsOfUse();
// } catch (ServerException e) {
// LOGGER.warn("This should never happen!", e);
// return;
// }
//
// new MaterialDialog.Builder(mContext)
// .title(R.string.terms_of_use_title)
// .content((user.getTermsOfUseVersion() == null) ?
// R.string.terms_of_use_sorry :
// R.string.terms_of_use_info)
// .onPositive((materialDialog, dialogAction) -> {
// userAcceptedTermsOfUse(user, current.getIssuedDate());
// Toast.makeText(mContext, R.string.terms_of_use_updating_server, Toast
// .LENGTH_LONG).show();
// if (callback != null) {
// callback.positive();
// }
// })
// .onNegative((materialDialog, dialogAction) -> {
// LOGGER.info("User did not accept the ToU.");
// Toast.makeText(mContext, R.string.terms_of_use_cant_continue, Toast
// .LENGTH_LONG).show();
// if (callback != null) {
// callback.negative();
// }
// })
// .show();
// } else {
// LOGGER.info("User has accpeted ToU in current version.");
// }
// }
public TermsOfUse getCurrentTermsOfUse() throws ServerException {
if (this.current == null) {
mDAOProvider.getTermsOfUseDAO()
.getAllTermsOfUseObservable()
.map(new Func1<List<TermsOfUse>, TermsOfUse>() {
@Override
public TermsOfUse call(List<TermsOfUse> termsOfUses) {
if (termsOfUses != null) {
list = termsOfUses;
String id = termsOfUses.get(0).getId();
try {
TermsOfUse inst = mDAOProvider.getTermsOfUseDAO()
.getTermsOfUse(id);
current = inst;
} catch (DataRetrievalFailureException e) {
LOGGER.warn(e.getMessage(), e);
throw OnErrorThrowable.from(e);
} catch (NotConnectedException e) {
LOGGER.warn(e.getMessage(), e);
throw OnErrorThrowable.from(e);
}
} else {
LOGGER.warn("Could not retrieve latest instance as their is no " +
"list available!");
}
LOGGER.info("Successfully retrieved the current terms of use.");
return current;
}
})
.toBlocking()
.first();
}
LOGGER.info("Returning the current terms of use.");
return current;
}
// private void retrieveTermsOfUse() throws ServerException {
//
// mDAOProvider.getTermsOfUseDAO()
// .getAllTermsOfUseObservable()
// .subscribeOn(Schedulers.io())
// .obser
//
//
// new Thread(new Runnable() {
// @Override
// public void run() {
// List<TermsOfUse> response;
// try {
// response = mDAOProvider.getTermsOfUseDAO().getAllTermsOfUse();
// setList(response);
// retrieveLatestInstance();
// } catch (DataRetrievalFailureException e) {
// LOGGER.warn(e.getMessage(), e);
// } catch (NotConnectedException e) {
// LOGGER.warn(e.getMessage(), e);
// }
// }
// }).start();
//
// synchronized (mMutex) {
// while (current == null) {
// try {
// mMutex.wait(5000);
//
// if (current == null) {
// throw new ServerException(new TimeoutException("Waiting to long for
// a " +
// "response."));
// }
// } catch (InterruptedException e) {
// throw new ServerException(e);
// }
// }
// }
// }
// private void retrieveLatestInstance() {
// if (list != null && list != null && list.size() > 0) {
// String id = list.get(0).getId();
// try {
// TermsOfUse inst = mDAOProvider.getTermsOfUseDAO().getTermsOfUse(id);
// setCurrent(inst);
// } catch (DataRetrievalFailureException e) {
// LOGGER.warn(e.getMessage(), e);
// } catch (NotConnectedException e) {
// LOGGER.warn(e.getMessage(), e);
// }
// } else {
// LOGGER.warn("Could not retrieve latest instance as their is no list available!");
// }
// }
// private void setCurrent(TermsOfUse t) {
// LOGGER.info("Current Terms Of Use: " + t.getIssuedDate());
// current = t;
//
// synchronized (mMutex) {
// mMutex.notifyAll();
// }
// }
public void userAcceptedTermsOfUse(final User user, final String issuedDate) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
// set the terms of use in the user of the normal preferences.
user.setTermsOfUseVersion(issuedDate);
mDAOProvider.getUserDAO().updateUser(user);
mUserManager.setUser(user);
LOGGER.info("User successfully updated.");
} catch (DataUpdateFailureException e) {
LOGGER.warn(e.getMessage(), e);
} catch (UnauthorizedException e) {
LOGGER.warn(e.getMessage(), e);
}
return null;
}
}.execute();
}
public static class TermsOfUseValidator<T> implements Observable.Transformer<T, T> {
private final TermsOfUseManager termsOfUseManager;
private final Activity activity;
public static <T> TermsOfUseValidator<T> create(
TermsOfUseManager termsOfUseManager,
Activity activity) {
return new TermsOfUseValidator<T>(termsOfUseManager, activity);
}
/**
* Constructor.
*
* @param termsOfUseManager the manager for the terms of use.
*/
public TermsOfUseValidator(TermsOfUseManager termsOfUseManager) {
this(termsOfUseManager, null);
}
/**
* Constructor.
*
* @param termsOfUseManager the manager for the terms of use.
* @param activity the activity for the case when the user has not accepted the
* terms of use. Then it creates a Dialog for acceptance.
*/
public TermsOfUseValidator(TermsOfUseManager termsOfUseManager, Activity activity) {
this.termsOfUseManager = termsOfUseManager;
this.activity = activity;
}
@Override
public Observable<T> call(Observable<T> tObservable) {
return tObservable.flatMap(t ->
termsOfUseManager.getCurrentTermsOfUseObservable()
.flatMap(termsOfUseManager.checkTermsOfUseAcceptance(activity))
.flatMap(termsOfUse -> Observable.just(t)));
}
}
}