/**
* Copyright (C) 2013 Romain Guefveneu.
*
* This file is part of naonedbus.
*
* Naonedbus 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.
*
* Naonedbus 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.naonedbus.activity.impl;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.List;
import net.naonedbus.BuildConfig;
import net.naonedbus.R;
import net.naonedbus.bean.Arret;
import net.naonedbus.bean.Commentaire;
import net.naonedbus.bean.Ligne;
import net.naonedbus.bean.Sens;
import net.naonedbus.manager.impl.ArretManager;
import net.naonedbus.manager.impl.LigneManager;
import net.naonedbus.manager.impl.SensManager;
import net.naonedbus.rest.controller.impl.CommentaireController;
import net.naonedbus.security.KeyType;
import net.naonedbus.security.RSAUtils;
import net.naonedbus.utils.ColorUtils;
import net.naonedbus.utils.InfoDialogUtils;
import net.naonedbus.validator.CommentaireContentTypeValidator;
import net.naonedbus.validator.CommentaireSizeValidator;
import net.naonedbus.validator.CommentaireValidator;
import net.naonedbus.widget.adapter.impl.ArretArrayAdapter;
import net.naonedbus.widget.adapter.impl.LignesArrayAdapter;
import net.naonedbus.widget.adapter.impl.SensArrayAdapter;
import org.apache.http.HttpException;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.bugsense.trace.BugSenseHandler;
public class CommentaireActivity extends SherlockActivity {
public static final String ACTION_COMMENTAIRE_SENT = "net.naonedbus.action.COMMENTAIRE_SENT";
public static final String PARAM_LIGNE = "ligne";
public static final String PARAM_SENS = "sens";
public static final String PARAM_ARRET = "arret";
private static final String LOG_TAG = "CommentaireActivity";
private static final boolean DBG = BuildConfig.DEBUG;
private static final String BUNDLE_KEY_LIGNE = "ligne";
private static final String BUNDLE_KEY_SENS = "sens";
private static final String BUNDLE_KEY_ARRET = "arret";
private EditText mCommentText;
private LigneManager mLigneManager;
private SensManager mSensManager;
private ArretManager mArretManager;
private Ligne mLigne;
private Sens mSens;
private Arret mArret;
private Ligne mAllLignes;
private Sens mAllSens;
private Arret mAllArrets;
private View mBtnChangeLigne;
private View mBtnChangeSens;
private View mBtnChangeArret;
private TextView mTextLigne;
private TextView mTextSens;
private TextView mTextArret;
private ListAdapter mLignesAdapter;
private ListAdapter mSensAdapter;
private ListAdapter mArretsAdapter;
private int mSelectedLignePosition;
private int mSelectedSensPosition;
private int mSelectedArretPosition;
private SendTask mSendTask;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comment);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mLigneManager = LigneManager.getInstance();
mSensManager = SensManager.getInstance();
mArretManager = ArretManager.getInstance();
mAllLignes = Ligne.buildAllLigneItem(this);
mAllSens = new Sens(-1, getString(R.string.target_tous_sens));
mAllArrets = new Arret.Builder().setId(-1).setNomArret(getString(R.string.target_tous_arrets)).build();
mLignesAdapter = getLignesAdapter();
mCommentText = (EditText) findViewById(android.R.id.input);
mTextLigne = (TextView) findViewById(R.id.commentaireLigne);
mTextSens = (TextView) findViewById(R.id.commentaireSens);
mTextArret = (TextView) findViewById(R.id.commentaireArret);
mBtnChangeLigne = findViewById(R.id.commentaireLigneSpinner);
mBtnChangeLigne.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
showSelectLigneDialog();
}
});
mBtnChangeSens = findViewById(R.id.commentaireSens);
mBtnChangeSens.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
showSelectSensDialog(mLigne.getCode());
}
});
mBtnChangeArret = findViewById(R.id.commentaireArret);
mBtnChangeArret.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
showSelectArretDialog(mLigne.getCode(), mSens.code);
}
});
Ligne ligne;
Sens sens;
Arret arret;
if (savedInstanceState != null && savedInstanceState.containsKey(BUNDLE_KEY_LIGNE)) {
ligne = (Ligne) savedInstanceState.getParcelable(BUNDLE_KEY_LIGNE);
sens = (Sens) savedInstanceState.getParcelable(BUNDLE_KEY_SENS);
arret = (Arret) savedInstanceState.getParcelable(BUNDLE_KEY_ARRET);
} else {
ligne = getIntent().getParcelableExtra(PARAM_LIGNE);
sens = getIntent().getParcelableExtra(PARAM_SENS);
arret = getIntent().getParcelableExtra(PARAM_ARRET);
}
if (ligne != null) {
setLigne(ligne);
}
if (sens != null) {
setSens(sens);
}
if (arret != null) {
setArret(arret);
}
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
final MenuInflater menuInflater = getSupportMenuInflater();
menuInflater.inflate(R.menu.activity_commentaire, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(BUNDLE_KEY_LIGNE, mLigne);
outState.putParcelable(BUNDLE_KEY_SENS, mSens);
outState.putParcelable(BUNDLE_KEY_ARRET, mArret);
}
@Override
public boolean onMenuItemSelected(final int featureId, final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
break;
case R.id.menu_send:
prepareAndSendComment();
break;
}
return super.onMenuItemSelected(featureId, item);
}
private ListAdapter getLignesAdapter() {
final List<Ligne> lignes = mLigneManager.getAll(getContentResolver());
lignes.add(0, mAllLignes);
final LignesArrayAdapter adapter = new LignesArrayAdapter(this, lignes);
adapter.setHideDivider(true);
return adapter;
}
private ListAdapter getSensAdapter(final String codeLigne) {
final List<Sens> sens = mSensManager.getAll(getContentResolver(), codeLigne);
sens.add(0, mAllSens);
return new SensArrayAdapter(this, sens);
}
private ListAdapter getArretsAdapter(final String codeLigne, final String codeSens) {
final List<Arret> arrets = mArretManager.getAll(getContentResolver(), codeLigne, codeSens);
arrets.add(0, mAllArrets);
return new ArretArrayAdapter(this, arrets);
}
/**
* Afficher la dialog de sélection de la ligne.
*/
private void showSelectLigneDialog() {
showSelectDialog(R.string.target_ligne, mLignesAdapter, mSelectedLignePosition,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
mSelectedLignePosition = which;
setLigne((Ligne) mLignesAdapter.getItem(which));
dialog.dismiss();
}
});
}
/**
* Afficher la dialog de sélection du sens.
*/
private void showSelectSensDialog(final String codeLigne) {
if (!codeLigne.equals(mSens.codeLigne) || mSensAdapter == null) {
mSensAdapter = getSensAdapter(codeLigne);
mSelectedSensPosition = -1;
}
showSelectDialog(R.string.target_sens, mSensAdapter, mSelectedSensPosition,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
mSelectedSensPosition = which;
setSens((Sens) mSensAdapter.getItem(which));
dialog.dismiss();
}
});
}
/**
* Afficher la dialog de sélection de l'arret.
*/
private void showSelectArretDialog(final String codeLigne, final String codeSens) {
if (!(codeLigne.equals(mArret.getCodeLigne()) && codeSens.equals(mArret.getCodeSens()))
|| mArretsAdapter == null) {
mArretsAdapter = getArretsAdapter(codeLigne, codeSens);
mSelectedArretPosition = -1;
}
showSelectDialog(R.string.target_arret, mArretsAdapter, mSelectedArretPosition,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
mSelectedArretPosition = which;
setArret((Arret) mArretsAdapter.getItem(which));
dialog.dismiss();
}
});
}
/**
* Afficher une dialog de selection.
*
* @param title
* Le titre
* @param adapter
* L'adapter
* @param defaultPosition
* La position de l'élément sélectionné par défaut
* @param onClickListener
* Le callback
*/
private void showSelectDialog(final int title, final ListAdapter adapter, final int defaultPosition,
final DialogInterface.OnClickListener onClickListener) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(title);
builder.setSingleChoiceItems(adapter, defaultPosition, onClickListener);
builder.show();
}
/**
* Envoyer le commentaire s'il a passé les test de
* {@link #validateComment(Commentaire)}
*/
private void prepareAndSendComment() {
final Commentaire commentaireItem = new Commentaire();
if (mArret != null) {
commentaireItem.setCodeArret(mArret.getCodeArret());
}
if (mSens != null) {
commentaireItem.setCodeSens(mSens.code);
}
if (mLigne != null) {
commentaireItem.setCodeLigne(mLigne.getCode());
}
commentaireItem.setMessage(mCommentText.getText().toString().trim());
if (validateComment(commentaireItem)) {
sendComment(commentaireItem);
} else {
InfoDialogUtils.show(this, R.string.dialog_title_invalid_comment, R.string.msg_warning_send_comment);
}
}
/**
* Définir la ligne du commentaire et changer la valeur du selecteur.
*
* @param ligne
* Le nouvelle ligne
*/
private void setLigne(final Ligne ligne) {
this.mLigne = ligne;
mTextLigne.setText(ligne.getLettre());
mTextLigne.setTextColor(ligne.getCouleurTexte());
if (ligne.getCouleur() == 0) {
mTextLigne.setBackgroundResource(R.drawable.item_symbole_back);
} else {
mTextLigne.setBackgroundDrawable(ColorUtils.getCircle(ligne.getCouleur()));
}
setSens(mAllSens);
mBtnChangeSens.setEnabled(!ligne.equals(mAllLignes));
}
/**
* Définir le sens du commentaire et changer la valeur du selecteur.
*
* @param sens
* Le nouveau sens
*/
private void setSens(final Sens sens) {
this.mSens = sens;
mTextSens.setText(sens.text);
setArret(mAllArrets);
mBtnChangeArret.setEnabled(!sens.equals(mAllSens));
}
/**
* Définir l'arret du commentaire et changer la valeur du selecteur.
*
* @param arret
* Le nouvel arret
*/
private void setArret(final Arret arret) {
mArret = arret;
mTextArret.setText(arret.getNomArret());
}
/**
* @return vrai si validé, faux s'il semble louche
*/
private boolean validateComment(final Commentaire comment) {
boolean ret = true;
final List<CommentaireValidator> validators = new ArrayList<CommentaireValidator>();
validators.add(new CommentaireSizeValidator());
validators.add(new CommentaireContentTypeValidator());
for (final CommentaireValidator commentaireValidator : validators) {
ret &= commentaireValidator.validate(comment.getMessage());
}
return ret;
}
private void sendComment(final Commentaire commentaire) {
if (mSendTask == null || mSendTask.getStatus() == AsyncTask.Status.FINISHED) {
mSendTask = (SendTask) new SendTask().execute(commentaire);
}
}
/**
* Classe d'envoi du message
*
* @author romain
*
*/
private class SendTask extends AsyncTask<Commentaire, Void, Boolean> {
private ProgressDialog progressDialog;
private Exception exception;
@Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(CommentaireActivity.this, "",
getString(R.string.commentaire_action_transmission), true);
super.onPreExecute();
}
@Override
protected Boolean doInBackground(final Commentaire... args) {
try {
final Commentaire messageItem = args[0];
final PrivateKey privateKey = genKey();
final String messageHashCode = getMessageHashCode(messageItem, privateKey);
final CommentaireController commentaireController = new CommentaireController();
commentaireController.post(messageItem.getCodeLigne(), messageItem.getCodeSens(),
messageItem.getCodeArret(), messageItem.getMessage(), messageHashCode);
return true;
} catch (final Exception e) {
this.exception = e;
return false;
}
}
@Override
protected void onPostExecute(final Boolean success) {
progressDialog.dismiss();
if (!success) {
final int msgError = (this.exception instanceof HttpException) ? R.string.dialog_content_comment_sending_error
: R.string.dialog_content_key_error;
InfoDialogUtils.show(CommentaireActivity.this, R.string.dialog_title_comment_sending_error, msgError);
if (DBG)
Log.e(LOG_TAG, "Erreur lors de l'envoi du message.", this.exception);
BugSenseHandler.sendExceptionMessage("Erreur lors de l'envoi du message.", null, this.exception);
} else {
final Intent actionCommentaireSent = new Intent(ACTION_COMMENTAIRE_SENT);
sendBroadcast(actionCommentaireSent);
setResult(RESULT_OK, null);
finish();
}
super.onPostExecute(success);
}
/**
* Générer la clé privée
*/
private PrivateKey genKey() {
BigInteger mod;
BigInteger exp;
PrivateKey privateKey = null;
mod = new BigInteger(getApplicationContext().getString(R.string.mod));
exp = new BigInteger(getApplicationContext().getString(R.string.exp));
try {
privateKey = (PrivateKey) RSAUtils.genNaonedbusKey(KeyType.PRIVATE, mod, exp);
} catch (final GeneralSecurityException e) {
BugSenseHandler.sendExceptionMessage("Erreur lors de la génération de la clé.", null, e);
}
return privateKey;
}
/**
* Générer les hashCode du message
*
* @param messageItem
* @return
* @throws GeneralSecurityException
* @throws UnsupportedEncodingException
*/
private String getMessageHashCode(final Commentaire messageItem, final PrivateKey privateKey)
throws UnsupportedEncodingException, GeneralSecurityException {
String concatHashCode;
String result = null;
concatHashCode = RSAUtils.getConcatHashCode(messageItem.getCodeLigne(), messageItem.getCodeSens(),
messageItem.getCodeArret(), messageItem.getMessage());
result = RSAUtils.encryptBase64(concatHashCode, privateKey);
return result;
}
}
}