Merge branch 'develop' of http://scm.ujaen.es/softuno/pictogram into fernando_branch

parent c01274f1
Showing with 6392 additions and 0 deletions
......@@ -52,3 +52,9 @@ src/main/assets/core_vocabulary/57236.png
src/main/assets/core_vocabulary/57384.png
src/main/createdoc.bat
src/main/resources/
/tablet/tablet.iml
/commonlibrary/commonlibrary.iml
/watch/watch.iml
gen/
package com.yottacode.net;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* Created by amontejo on 27/01/16.
*/
public class FakeSSLTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(final X509Certificate[] arg0,
final String arg1) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted(final X509Certificate[] arg0,
final String arg1) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public final X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
}
\ No newline at end of file
package com.yottacode.net;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InterfaceAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Hashtable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import android.os.AsyncTask;
import android.os.StrictMode;
import android.util.Log;
import com.google.gson.JsonParser;
import com.koushikdutta.async.parser.JSONObjectParser;
import javax.net.ssl.HttpsURLConnection;
/**
* Wrapper for connecting with a server using its tokenized RESTful API.
* Connections are implemented asynchronous, and responses are handled by an instance of
* iRestapiListener.
*
* @author dofer
*/
/**
*
* LastUpdate: 22 de enero de 2016
*/
public class RestapiWrapper {
String server;
String token;
public static final int TIME_OUT=20000;
private static final String SERVER_RESULT="result";
private static final String SERVER_ERROR="error";
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
public RestapiWrapper(String server, String token) {
this.server=server;
this.token=token;
}
public void setToken(String token) {
this.token=token;
}
public String getToken() {
if (token==null) throw new java.lang.NullPointerException("Token has no value. Use constructor properly or RestapiWrapper.setToken must be invoked previously");
return token;
}
public String getServer() {
return server;
}
public void ask(String operation, Hashtable<String, String> params, String postOrGet, iRestapiListener listener) {
ask(operation, params, postOrGet, false, listener);
}
public void ask(String operation, Hashtable<String, String> params, String postOrGet, boolean json, iRestapiListener listener) {
// call preExecute listener to show loading window
listener.preExecute();
// call AsynTask to perform network operation on separate thread
if (this.token != null) {
if (params == null)
params = new Hashtable<>(1);
params.put("token", this.token);
}
HttpAsyncTaskParams httpAsyncTaskParams=new HttpAsyncTaskParams();
httpAsyncTaskParams.request_method=postOrGet.toUpperCase();
httpAsyncTaskParams.listener=listener;
httpAsyncTaskParams.url_params=params;
httpAsyncTaskParams.url=this.server + '/' + operation;
httpAsyncTaskParams.json_params=json;
new HttpAsyncTask().executeOnExecutor(HttpAsyncTask.THREAD_POOL_EXECUTOR,httpAsyncTaskParams);
}
public void ask(String operation, iRestapiListener listener) {
this.ask(operation, null, "get", listener);
}
/**
* asynchronous ping
* @param ping_op
* @param error_listener
* @return
*/
/**
* synchronous ping
* @param ping_op
* @param error_listener
* @return
*/
/*
public boolean ping_session(String ping_op, iRestapiListener error_listener) {
return ping(this.server,ping_op+"?token="+this.token, error_listener);
}
*/
/**
* synchronous ping
* @param ping_op
* @return
*/
public static boolean ping(String server, String ping_op) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
boolean pingResult = false;
try {
pingResult = GET(server + "/" + ping_op, null)!=null;
} catch (UnknownHostException e){
//e.printStackTrace();
Log.i(RestapiWrapper.class.getName(), "ping failed at "+ping_op);
return false;
} catch (IOException e) {
e.printStackTrace();
Log.i(com.yottacode.net.RestapiWrapper.class.getName(), "ping failed at"+ping_op);
//error_listener.error(e);
}
return pingResult;
}
private static JSONObject resultToJSON(HttpURLConnection urlConnection) throws IOException {
int responseCode=urlConnection.getResponseCode();
String response="'";
String line;
JSONObject JSONresponse;
BufferedReader br = new BufferedReader(new InputStreamReader(responseCode == HttpsURLConnection.HTTP_OK
? urlConnection.getInputStream()
: urlConnection.getErrorStream()));
while ((line=br.readLine()) != null) {
response+=line;
}
response+="'";
try {
JSONresponse = new JSONObject("{ "+SERVER_RESULT+": " + response + (responseCode == HttpsURLConnection.HTTP_OK
? "}"
: ", "+SERVER_ERROR+": " + responseCode +"}"));
} catch (JSONException e) {
JSONresponse = null;
Log.e(RestapiWrapper.class.getCanonicalName(),e.getMessage());
}
Log.i(com.yottacode.net.RestapiWrapper.class.getName(), "server answer: " + JSONresponse.toString());
return JSONresponse;
}
public static JSONObject GET(String surl, Hashtable<String, String> params) throws IOException {
URL url;
if (params!=null) {
surl += '?';
for (String param : params.keySet()) {
String value = params.get(param);
surl += param + '=' + value + '&';
}
surl=surl.substring(0,surl.length()-1);
}
url = new URL(surl);
HttpURLConnection urlConnection = null;
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setReadTimeout(TIME_OUT);
urlConnection.setConnectTimeout(TIME_OUT);
urlConnection.setRequestMethod("GET");
urlConnection.setDoInput(true);
urlConnection.connect();
return RestapiWrapper.resultToJSON(urlConnection);
}
public JSONObject POST(String surl, String request_method, Hashtable<String, String> params, boolean json_params) throws IOException {
URL url = new URL(surl);
HttpURLConnection urlConnection = null;
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setReadTimeout(TIME_OUT);
urlConnection.setConnectTimeout(TIME_OUT);
urlConnection.setRequestMethod(request_method);
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
if (json_params) {
urlConnection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
urlConnection.setRequestProperty("Accept", "application/json, text/plain, */*");
}
String sparams;
if (json_params) {
sparams = params.get("json");
params.remove("json");
} else
sparams="";
for (String param : params.keySet()) {
String value = params.get(param);
if (param.equals("token"))
urlConnection.setRequestProperty("Authorization", "Bearer " + value);
else {
if (sparams.length() > 0) sparams += '&';
sparams += param + '=' + value;
}
}
//Send request
DataOutputStream wr = new DataOutputStream (
urlConnection.getOutputStream ());
wr.writeBytes(sparams);
wr.flush();
wr.close();
return RestapiWrapper.resultToJSON(urlConnection);
}
private class HttpAsyncTaskParams {
protected String request_method;
protected String url;
protected Hashtable<String, String> url_params;
protected boolean json_params;
protected iRestapiListener listener;
protected String result;
protected Exception error;
}
private class HttpAsyncTask extends AsyncTask<HttpAsyncTaskParams, Void, HttpAsyncTaskParams> {
@Override
protected HttpAsyncTaskParams doInBackground(HttpAsyncTaskParams... params) {
try {
Log.i(com.yottacode.net.RestapiWrapper.class.getName(), " asking to the server for " + params[0].url+" params:"+params[0].url_params +" JSON?"+params[0].json_params);
JSONObject jresult = params[0].request_method.equalsIgnoreCase("GET")
? GET(params[0].url, params[0].url_params)
: POST(params[0].url, params[0].request_method, params[0].url_params, params[0].json_params);
params[0].result=jresult.getString(SERVER_RESULT);
if (jresult.has(SERVER_ERROR))
params[0].error= new Exception(params[0].result+" (err code "+jresult.getString(SERVER_ERROR)+")");
} catch (Exception e) {
Log.e(com.yottacode.net.RestapiWrapper.class.getName(), "Error: '" + e.getLocalizedMessage() + "' when asking for " + params[0].url);
params[0].result=null;
params[0].error=e;
}
return params[0];
}
// onPostExecute displays the results of the AsyncTask.
@Override
protected void onPostExecute(HttpAsyncTaskParams params) {
try {
if (params.error!=null) params.listener.error(params.error);
else
if(params.result.length()>0) {
Log.i(LOG_TAG, "Picto JSON Result: " + params.result);
JSONTokener tokener=new JSONTokener(params.result);
Object jsonResult = new JSONTokener(params.result).nextValue();
if (jsonResult instanceof JSONObject) {
// The result is an object
params.listener.result((JSONObject) jsonResult);
} else if (jsonResult instanceof JSONArray) {
// The result is an array
params.listener.result((JSONArray) jsonResult);
} else
params.listener.result((JSONObject) null);
}else{
params.listener.result((JSONObject) null);
}
} catch (JSONException e) {
params.listener.error(e);
}
if (params.url_params!=null) params.url_params.clear();
}
}
}
package com.yottacode.net;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import android.util.Log;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
class RelaxedHostNameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
public class SSLDummyContext {
static SSLContext mySSLContext = null;
public static void init(boolean ssl) {
try {
mySSLContext = SSLContext.getInstance("TLS");
if (!ssl) {
mySSLContext.init(null, new TrustManager[]{new FakeSSLTrustManager()}, new SecureRandom());
HttpsURLConnection.setDefaultHostnameVerifier(new RelaxedHostNameVerifier());
HttpsURLConnection.setDefaultSSLSocketFactory(mySSLContext.getSocketFactory());
} else
mySSLContext.init(null, null, null);
} catch (NoSuchAlgorithmException e) {
Log.e(e.getClass().getName(), e.getClass().getCanonicalName() + ": " + e.getMessage());
} catch (KeyManagementException e) {
Log.e(e.getClass().getName(), e.getClass().getCanonicalName() + ": " + e.getMessage());
}
}
public static SSLContext get() {
return mySSLContext;
}
}
\ No newline at end of file
package com.yottacode.net;
import android.util.Log;
import com.github.nkzawa.socketio.client.Ack;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;
import com.github.nkzawa.emitter.Emitter;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.security.SecureRandom;
/**
* This listener logs all received answers from the message it is registered to
* It is usually not used
*/
class DefaultListener implements Emitter.Listener {
private String msg;
public DefaultListener(String msg) {
super();
this.msg = msg;
}
@Override
public void call(Object... args) {
Log.d(this.getClass().getName(),"Received message:"+this.msg);
for (Object arg : args)
Log.d(this.getClass().getName(), "Argument:" + arg);
}
}
/**
* Websocket Room based on Socket IO
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class SailsSocketsIO {
private Socket socket=null;
private static final String EMIT_METHOD="post";
/**
* Registers a connection listener to default connection messages
* @param socket
* @param connectListener
*/
private void registerMessages(Socket socket, Emitter.Listener connectListener, Emitter.Listener errorListener) {
socket.on(Socket.EVENT_CONNECT, connectListener)
.on(Socket.EVENT_CONNECT_ERROR, errorListener)
.on(Socket.EVENT_CONNECT_TIMEOUT, errorListener)
.on(Socket.EVENT_ERROR, errorListener)
.on(Socket.EVENT_DISCONNECT, errorListener);
}
/**
* Registers a connection listener to a given message
* @param message
* @param listener
*/
public void registerMessage(String message, Emitter.Listener listener) {
Log.d(this.getClass().getName(), " Listening:"+message);
socket.on(message, listener);
}
public SailsSocketsIO(String sails_socket_io_url, String ptransport, Emitter.Listener connectListener, Emitter.Listener errorListener) {
final String sdk_version= "0.11.0";
final String sdk_module="node";
final String sdk_language="java";
final String transport=ptransport;
final String query_version =
"__sails_io_sdk_version"+"=" + sdk_version + '&' +
"__sails_io_sdk_platform"+"=" + sdk_module+ '&' +
"__sails_io_sdk_language" + '=' + sdk_language;
try {
SSLContext mySSLContext = SSLDummyContext.get();
IO.setDefaultSSLContext(mySSLContext);
IO.Options opts = new IO.Options();
opts.forceNew = true;
opts.reconnection = false;
opts.sslContext = mySSLContext;
opts.secure=true;
opts.query = query_version;
opts.transports = new String[] {transport};
this.socket = IO.socket(sails_socket_io_url, opts);
this.socket.connect();
this.registerMessages(socket, connectListener, errorListener);
} catch (URISyntaxException e) {
Log.e(e.getClass().getName(), e.getClass().getCanonicalName()+": "+e.getMessage() );
}
}
public void emit(String msg, Object params, Ack ack) throws JSONException {
JSONObject obj=new JSONObject().put("url", msg).put("data", params);
Log.d(this.getClass().getName(), "Emitted messsage:" + obj);
socket.emit(this.EMIT_METHOD, obj, ack);
}
public void destroy() {
this.socket.disconnect();
this.socket.off();
}
}
package com.yottacode.net;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* This interface specifies which are the methods to be implemented by classes receiving
* responses to API requests.
*
* Generally, it is extended by Activities from which API requests are performed
*
* @author dofer
*/
public interface iRestapiListener {
public void preExecute();
public void result(JSONArray result);
public void result(JSONObject result);
public void error(Exception e);
}
package com.yottacode.pictogram.action;
import android.util.Log;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
/**
* User actions that happens when the user is online --> they are sent to the server by websockets (Room class)
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
* @see Room
*/
public abstract class Action {
protected String type;
/**
* It creates an action for any user, both online and offline actions.
*
* @param type
*/
public Action(String type){
this.type=type;
}
public String get_type() { return this.type;}
public abstract String get_action();
protected JSONObject get_json() {
final String param_id_stu="id_stu";
final String param_id_sup="id_sup";
final String param_id_dev="id_dev";
final String param_timestamp="timestamp";
final Date currentTime = new Date();
SimpleDateFormat datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
try {
JSONObject jsonObject = new JSONObject()
.put(param_id_stu, PCBcontext.getPcbdb().getCurrentUser().get_id_stu())
.put(param_timestamp, datetime.format(currentTime));
Log.d("TIMESTAMP-----------> ", datetime.format(currentTime));
if (PCBcontext.getPcbdb().getCurrentUser().has_supervisor())
jsonObject.put(param_id_sup,PCBcontext.getPcbdb().getCurrentUser().get_id_sup());
//TODO Decidir qué almacenar con DEVICE
//if (PCBcontext.getDevice().getDeviceID()!=null)
//jsonObject.put(param_id_dev, PCBcontext.getDevice().getDeviceID());
return jsonObject;
}catch(JSONException e) {
Log.e(this.getClass().getCanonicalName(),e.getMessage());
return null;
}
}
/**
*
* @return the whole description of the action, including the action type (required for sending batch actions,
* more info at http://scm.ujaen.es/softuno/pictogram/wikis/RestapivalidActionsBatch)
*/
public JSONObject getDescription() {
final String param_action= "action";
final String param_attributes= "attributes";
try {
return (new JSONObject().put(param_action, this.type)
.put(param_attributes, this.get_json()));
} catch (JSONException e) {
Log.e(this.getClass().getCanonicalName(),e.getMessage());
}
return null;
}
}
\ No newline at end of file
package com.yottacode.pictogram.action;
import android.util.Log;
import com.yottacode.net.iRestapiListener;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Hashtable;
import java.util.Vector;
/**
* Created by emblanco on 5/10/15.
* Modified by Fernando on 10/11/15
* More info at http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
*/
public class ActionLog implements iRestapiListener {
Vector<JSONObject> actions_buffer;
public ActionLog() {
actions_buffer=PCBcontext.getPcbdb().loadActions();
if (PCBcontext.getNetService().online()) batch();
}
// String constant for logs
private static final String LOG_TAG = ActionLog.class.getCanonicalName();
/**
* Online action. It is send by using web sockets
* @param action
*/
public void log(Action action) {
Log.i(this.getClass().getCanonicalName(), " Registered Action: " + action.getDescription());
if (PCBcontext.getRoom().inRoom())
PCBcontext.getRoom().emit(action);
else {
// If there is no room, the action is stored into the local DB
PCBcontext.getPcbdb().insertAction(action);
actions_buffer.add(action.getDescription());
Log.i(this.getClass().getCanonicalName(), " Batch action included: " + action.getDescription());
}
}
public void batch() {
if (!actions_buffer.isEmpty()) {
Hashtable<String, String> params=new Hashtable<>(1);
String url="stu/actions_batch";
JSONArray actions= new JSONArray();
for (JSONObject action: actions_buffer)
actions.put(action);
//actions= "{actions: [" + actions.substring(1) + "]}";
try {
params.put("json",new JSONObject().put("actions",actions).toString());
} catch (JSONException e) {
e.printStackTrace();
Log.e(this.getClass().getCanonicalName(), " Batch action error: " + e.getMessage());
}
Log.i(this.getClass().getCanonicalName()," Sending batch actions: "+url+": "+actions);
PCBcontext.getRestapiWrapper().ask(url, params, "post", true, this);
}
}
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
}
@Override
public void result(JSONObject result) {
try {
Log.i(this.getClass().getName(),
result.has("total") ? "Uploaded actions (batch):" + result.getString("total")
: "Error uploading actions. Message from server: "+result.getString("error"));
}catch (JSONException e) {
Log.e(this.getClass().getCanonicalName(),e.getLocalizedMessage());
}
PCBcontext.getPcbdb().deleteActions();
actions_buffer.clear();
}
@Override
public void error(Exception e) {
Log.e(this.getClass().getCanonicalName(),"Error:"+e.getMessage()+" on actions="+this.actions_buffer);
}
}
package com.yottacode.pictogram.action;
import android.util.Log;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
/**
* User actions regarding a pictogram that happens when the user is online --> they are sent to the server by websockets (Room class)
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
* @see Room
*/
public abstract class PictoAction extends Action {
private Picto picto;
private float gps_lat;
private float gps_lon;
/**
* It creates an action for any user regarding a picto, both online and offline actions.
*
* @param type
* @param picto
*/
public PictoAction(String type, Picto picto){
super(type);
this.picto=picto;
}
@Override
public JSONObject get_json(){
final String param_picto="picto";
try {
return super.get_json().put(param_picto, this.get_json_picto());
}catch(JSONException e) {
Log.e(this.getClass().getCanonicalName(),e.getLocalizedMessage());
return null;
}
}
public JSONObject get_json_picto() throws JSONException {
final String param_id_json="id";
final String param_picto="picto";
final String param_expression="expression";
final String param_expr_lang="lang";
final String param_expr_text="text";
final String param_attrs="attributes";
final String param_picto_id="id";
final String param_picto_uri="uri";
final String param_picto_cat="category";
JSONObject subsubPicto = new JSONObject().put(param_picto_id, picto.get_id())
.put(param_picto_uri, picto.get_url())
.put(param_picto_cat, picto.get_category());
JSONObject attributes = new JSONObject(picto.get_json_attrs());
JSONObject expression = new JSONObject().put(param_expr_lang,PCBcontext.getPcbdb().getCurrentUser().get_lang_stu())
.put(param_expr_text,picto.get_translation());
JSONObject subPicto = new JSONObject().put(param_id_json, 1470)
.put(param_picto, subsubPicto)
.put(param_expression,expression)
.put(param_attrs, attributes);
return subPicto;
}
}
\ No newline at end of file
package com.yottacode.pictogram.action;
import android.util.Log;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
/**
* User actions regarding a pictogram that happens when the user is online --> they are sent to the server by websockets (Room class)
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
* @see Room
*/
public class PictosAction extends Action {
LinkedList<Picto> pictos;
private float gps_lat;
private float gps_lon;
//Pictos Action types
private static final String SHOW="Show";
private static final String ACTION="/stu/action";
/**
* It creates an action for any user regarding a set of pictos, both online and offline actions.
*
* @param pictos
*/
public PictosAction( LinkedList<Picto> pictos){
super(SHOW);
this.pictos=pictos;
}
@Override
public JSONObject get_json() {
final String param_pictos="pictos";
try{
JSONArray ja = new JSONArray();
for(Picto picto : pictos) {
JSONObject jsonpicto = get_json_picto(picto);
ja.put(jsonpicto);
}
return super.get_json().put(param_pictos, ja);
} catch (JSONException e) {
Log.e(PictosAction.class.getCanonicalName(),e.getMessage());
return null;
}
}
private JSONObject get_json_picto(Picto picto) throws JSONException {
final String param_id_json="id";
final String param_picto="picto";
final String param_attrs="attributes";
final String param_picto_id="id";
final String param_picto_uri="uri";
final String param_picto_cat="category";
JSONObject subsubPicto = new JSONObject().put(param_picto_id, picto.get_id())
.put(param_picto_uri, picto.get_url())
.put(param_picto_cat, picto.get_category());
JSONObject attributes = new JSONObject(picto.get_json_attrs());
// Here add sup, dev...
JSONObject subPicto = new JSONObject().put(param_id_json, 1470)
.put(param_picto, subsubPicto)
.put(param_attrs, attributes);
return subPicto;
}
public String get_action() {return ACTION;}
}
package com.yottacode.pictogram.action;
import android.util.Log;
import com.github.nkzawa.emitter.Emitter;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.github.nkzawa.socketio.client.Ack;
import com.github.nkzawa.socketio.client.SocketIOException;
import com.yottacode.pictogram.dao.PCBDBHelper;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.net.SailsSocketsIO;
import com.yottacode.pictogram.tools.PCBcontext;
import java.io.IOException;
import java.sql.SQLDataException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedList;
/**
* Websocket Room based on SailsSocketsIO
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class Room {
protected SailsSocketsIO socket=null;
protected boolean inRoom=false;
protected Hashtable<String, Emitter.Listener> listeners;
public Room( ) {
Log.i(this.getClass().getName(), "Entering room");
listeners=new Hashtable<>();
reconnect();
}
private JSONObject common_data(String action, JSONObject attributes) throws JSONException {
final String action_param="action";
final String attributes_param="attributes";
return new JSONObject().put(action_param, action).put(attributes_param, attributes);
}
public void emit(final Action action) {
String token=PCBcontext.getRestapiWrapper().getToken();
final String token_param="token";
Log.i(this.getClass().getName(), "Action: " + action.get_type() + " / Attributes emitted: " + action.get_json().toString());
try{
this.socket.emit(action.get_action(), this.common_data(action.get_type(), action.get_json()).put(token_param, token), new Ack() {
@Override
public void call(Object... args) {
for (Object arg : args)
Log.d(this.getClass().getName(), "Ack messsage:" + arg);
}
});
} catch (JSONException e) {
Log.e(this.getClass().getCanonicalName(), e.getClass().getCanonicalName() + e.getMessage() + "--" + e.getLocalizedMessage());
}
}
/**
* Reconnect to the room. It is useful if the server connection is lost for a time
*/
public void reconnect() {
final String transport="polling";
exit();
this.socket=new SailsSocketsIO(PCBcontext.getRestapiWrapper().getServer(), transport, new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.i(this.getClass().getName(), "Reconnect successful");
subscribe();
listen_again();
inRoom=true;
}
}, new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.e(this.getClass().getName(), "IO sockects error: "+args[0]);
inRoom=false;
PCBcontext.getNetService().setOffline(new SocketIOException(args[0].toString()));
}
});
}
public boolean inRoom() {return this.inRoom;}
public void listen(String msg, Emitter.Listener listener) {
this.socket.registerMessage(msg, listener);
this.listeners.put(msg, listener);
}
private void listen_again() {
for (String msg: this.listeners.keySet()) {
Log.i(this.getClass().getName(), "Listening to "+msg);
this.socket.registerMessage(msg, this.listeners.get(msg));
}
}
void subscribe() {
try {
SubscribeAction action = new SubscribeAction();
this.emit(action);
}catch (Exception e) {
Log.e(this.getClass().getCanonicalName(), "subscribe room failed:"+e.getMessage());
}
}
void unsubscribe() {
try {
UnsubscribeAction action = new UnsubscribeAction();
this.emit(action);
}catch (Exception e) {
Log.e(this.getClass().getCanonicalName(), "unsubscribe room failed:"+e.getMessage());
}
}
public void exit() {
if (this.socket!=null) {
Log.i(this.getClass().getName(), "Leaving room");
unsubscribe();
this.socket.destroy();
this.socket=null;
}
}
public void finalize() throws java.lang.Throwable {
super.finalize();
exit();
}
}
package com.yottacode.pictogram.action;
import com.yottacode.pictogram.dao.Picto;
/**
* User actions regarding a pictogram that happens when the user is online --> they are sent to the server by websockets (Room class)
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
* @see Room
*/
public class SubscribeAction extends Action {
//Picto Action types
public static final String SUBSCRIBE="subscribe";
public static final String ACTION="/stu/subscribe";
public SubscribeAction( ){
super(SUBSCRIBE);
}
public String get_action() {return ACTION;}
}
\ No newline at end of file
package com.yottacode.pictogram.action;
import android.util.Log;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONException;
import org.json.JSONObject;
/**
* User actions regarding a pictogram that happens when the user is online --> they are sent to the server by websockets (Room class)
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
* @see Room
*/
public class TalkAction extends PictoAction {
//Picto Action types
public static final String ADD="Add";
public static final String DELETE="Delete";
public static final String SELECT="Select";
private static final String ACTION="/stu/action";
public TalkAction(String type, Picto picto){
super(type, picto);
}
public String get_action() {return ACTION;}
}
\ No newline at end of file
package com.yottacode.pictogram.action;
/**
* User actions regarding a pictogram that happens when the user is online --> they are sent to the server by websockets (Room class)
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
* @see Room
*/
public class UnsubscribeAction extends Action {
//Picto Action types
public static final String UNSUBSCRIBE="unsubscribe";
public static final String ACTION="/stu/unsubscribe";
public UnsubscribeAction( ){
super(UNSUBSCRIBE);
}
public String get_action() {return ACTION;}
}
package com.yottacode.pictogram.action;
import android.util.Log;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONException;
import org.json.JSONObject;
/**
* User actions regarding a pictogram that happens when the user is online --> they are sent to the server by websockets (Room class)
* It is required, inter alia, to support session state diagram such
* as is depicted at http://scm.ujaen.es/files/note/1042/Estados_y_acciones.pdf
* and http://scm.ujaen.es/softuno/pictogram/wikis/LUActions
* @author Fernando Martinez Santiago
* @version 1.0
* @see Room
*/
public class VocabularyAction extends PictoAction {
//Picto Action types
public static final String ADD="add";
public static final String ALTERATTRS="update";
private static final String ACTION="/stu/vocabulary";
public VocabularyAction(String type, Picto picto){
super(type, picto);
}
public String get_action() {return ACTION;}
}
\ No newline at end of file
package com.yottacode.pictogram.dao;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.AsyncTask;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.tools.Img;
import org.json.JSONException;
/**
* Data Access Object to manage Pictogram Communicator Board database regarding App information that is not user-dependent
* This class requires:
* The script to create the DB allocated in res/raw/pcbdb_create.sql
* The entries db_name and db_script_error in strings.xml
*
* @author Fernando Martinez Santiago
* @version 1.1
*/
public class Device extends SQLiteOpenHelper {
Context context;
final static class PARAMS {
static String keyword="key";
static String deviceID="deviceID";
static String stu_id="last__stu_id";
static String sup_id="last__sup_id";
}
/**
* Create a helper object to create, open, and/or manage a database.
*
* @param context the context of the activity
* @param factory null
* @param version 1
*/
public Device(Context context, CursorFactory factory, int version) {
super(context, DeviceHelper.getDBName(context), factory, version);
if (DeviceHelper.force_create(context)) {
Log.i(this.getClass().getCanonicalName(),"Forcing create new Database "+DeviceHelper.getDBName(context));
context.deleteDatabase(DeviceHelper.getDBName(context));
Log.i(this.getClass().getCanonicalName(), "Database dropped");
}
this.context = context;
}
/**
* Set the value of a param.
*
* @return serial the official Yotta device ID or null if it is not specified
*/
private void setParam(String param, String value) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values=new ContentValues(2);
values.put("key", param);
values.put("value", value);
long result=db.replace("params", null, values);
if (result==-1) Log.e(this.getClass().getCanonicalName(),"Error when updating param "+param+"="+value);
db.close();
getParamValue(param);
}
/**
* Retrieve the value of a param.
*
* @return value of key param
*/
public String getParamValue(String key) {
SQLiteDatabase db = this.getReadableDatabase();
String value;
Cursor cursor = db.query("params", new String[]{"value"}, "key=?", new String[]{key}, null, null, null, null);
if (cursor.getCount() == 0){
value = null;
Log.e(this.getClass().getCanonicalName(), "Error when getting param " + key);
}else {
cursor.moveToFirst();
value = cursor.getString(0);
}
db.close();
return value;
}
/**
* Save the keyord to escape from the kiosk mode
* @param keyword to chage in DB
*/
public void setKeyword(String keyword) {
setParam(PARAMS.keyword, keyword);
}
/**
* Retrieve the keyword use to escape the kiosk mode
* @author emblanco,fernando
* @return keyword
*/
public String getKeyword() {
return getParamValue(PARAMS.keyword);
}
/**
* Save the device ID, such as the Android ID.
* @param deviceID the platform dependent ID
* @see Device#getDeviceID()
*/
public void setDeviceID(String deviceID) {
setParam(PARAMS.deviceID, deviceID);
}
/**
* @return the platform device ID, such as Android ID, if it is registered, or null if it is not specified.
* @see Device#setDeviceID(String)
*/
public String getDeviceID() {
return getParamValue(PARAMS.deviceID);
}
/**
* Save the id of the student selected
* @param stu_id of the device.
*/
public void setLastStuId(int stu_id) {
setParam(PARAMS.stu_id, String.valueOf(stu_id));
}
/**
* Save the id of the last supervisor
* @param sup_id of the device.
*
*/
public void setLastSupId(int sup_id) {
setParam(PARAMS.sup_id, String.valueOf(sup_id));
}
/**
* Retrieve de last logged user
* @author Miguel Ángel
* @version 1.0
* @return user_id selected or 0.
*/
public int getLastStuId() {
return Integer.valueOf(getParamValue(PARAMS.stu_id));
}
/**
* Retrieve the last supervisor id selected in Login
*
* @return sup_id selected or 0.
*/
public int getLastSupId() {
return Integer.parseInt(getParamValue(PARAMS.sup_id));
}
/**
* @param id_stu
* @param id_sup
* @return the information of an specific user, if they are available in the local DB
* @see Device#getUsers()
*/
public User findUser(int id_stu, int id_sup) throws JSONException {
SQLiteDatabase db = this.getReadableDatabase();
User user = null;
Cursor cursor = db.query("users_detail", null, null, null, null, null, null, null);
while (cursor.moveToNext() && user == null)
if (cursor.getInt(0) == id_stu && cursor.getInt(9) == id_sup)
user = new User(cursor.getInt(0), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getString(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getInt(9), cursor.getString(10), cursor.getString(11), cursor.getString(12), cursor.getString(13), cursor.getString(14), cursor.getString(15), cursor.getString(16), cursor.getString(17));
cursor.close();
db.close();
return user;
}
public Vector<User> recoverStudents(Integer id_sup) throws JSONException{
SQLiteDatabase db = this.getReadableDatabase();
Vector<User> users = new Vector<>();
Cursor cursor = db.query("users_detail", null, null, null, null, null, null, null);
while (cursor.moveToNext())
if (cursor.getInt(9) == id_sup)
users.add(new User(cursor.getInt(0), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getString(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getInt(9), cursor.getString(10), cursor.getString(11), cursor.getString(12), cursor.getString(13), cursor.getString(14), cursor.getString(15), cursor.getString(16), cursor.getString(17)));
cursor.close();
db.close();
return users;
}
/**
* @param auser
* @param apwd
* @return information of a specific user, if it is available in the local DB
*/
public Vector<User> findUser(String auser, String apwd) throws JSONException, LoginException {
SQLiteDatabase db = this.getReadableDatabase();
boolean user_found=false;
Vector<User> users=null;
Cursor cursor = db.query("supervisor", null, null, null, null, null, null, null);
while (cursor.moveToNext() && users == null && !user_found) {
user_found = cursor.getString(1).equalsIgnoreCase(auser);
if (user_found)
if (cursor.getString(2)!=null && cursor.getString(2).equals(apwd)) {
users = recoverStudents(cursor.getInt(0));
if (users.size() == 0)
throw new LoginException("Supervisor hasn't got students", LoginException.NO_STUDENTS);
}
else throw new LoginException("Supervisor password incorrect", LoginException.BAD_LOGIN);
}
cursor.close();
if (!user_found) {
cursor = db.query("student", null, null, null, null, null, null, null);
while (cursor.moveToNext() && users == null && !user_found) {
user_found = cursor.getString(1).equalsIgnoreCase(auser);
if (user_found)
if (cursor.getString(2)!=null && cursor.getString(2).equals(apwd)) {
users = new Vector<>(1);
users.add(new User(cursor.getInt(0), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getString(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8),
User.NO_SUPERVISOR, "", "", "", "", "", "", "", ""));
}
else throw new LoginException("Student password incorrect", LoginException.BAD_LOGIN);
}
cursor.close();
}
if (!user_found) throw new LoginException("User not found", LoginException.BAD_LOGIN);
db.close();
return users;
}
/**
* @return a Vector<User> with the whole user who has been registered.
* @see User
* @see com.yottacode.pictogram.dao.PCBDBHelper
*/
public Vector<User> getUsers() throws JSONException {
SQLiteDatabase db = this.getReadableDatabase();
User user;
Cursor cursor = db.query("users_detail", null, null, null, null, null, null, null);
Vector<User> users = new Vector<User>(cursor.getCount());
while (cursor.moveToNext()) {
user = new User(cursor.getInt(0), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getString(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getInt(9), cursor.getString(10), cursor.getString(11), cursor.getString(12), cursor.getString(13), cursor.getString(14), cursor.getString(15), cursor.getString(16), cursor.getString(17));
users.add(user);
}
cursor.close();
db.close();
return users;
}
public void insertUser(User user) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("INSERT INTO users_detail values (" +
user.get_id_stu() + ", " +
"'" + user.get_nickname_stu() + "', " +
(user.get_pwd_stu()==null || user.get_pwd_stu().length()==0 ? "NULL" : "'"+user.get_pwd_stu()+"'") + ", "+
"'" + user.get_name_stu() + "', " +
"'" + user.get_surname_stu() + "', " +
"'" + user.get_url_img_stu() + "', " +
"'" + user.get_gender_stu() + "', " +
"'" + user.get_lang_stu() + "', " +
"'" + user.get_json_attrs() + "', " +
"'" + user.get_id_sup() + "', " +
"'" + user.get_email_sup() + "', " +
"'" + user.get_pwd_sup() + "', " +
"'" + user.get_name_sup() + "', " +
"'" + user.get_surname_sup() + "', " +
"'" + user.get_url_img_sup() + "'," +
"'" + user.get_gender_sup() + "'," +
"'" + user.get_lang_sup() + "'," +
"'" + user.get_tts_engine_sup() + "'" +
")");
db.close();
}
/**
* delete the list of images (students, supervisors, pictograms) which are no longer used
*/
public void deleteDeprecatedImgs() {
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.query("deprecated_images", null, null, null, null, null, null);
while (cursor.moveToNext()) {
String type = cursor.getString(1);
String folder = type.equals("stu") ? Img.STUDENT : type.equals("sup") ? Img.SUPERVISOR : Img.VOCABULARY;
(new Img(cursor.getInt(0), null, folder)).delete_bitmap(this.context);
Log.i(this.getClass().getCanonicalName(), "Image file " + cursor.getString(1) + "." + cursor.getInt(0) + " deleted");
}
cursor.close();
db.delete("deprecated_images", null, null);
db.close();
}
/**
* Execute the script of the database
* IMPORTANT: every DDL sentence has to finish with the sequence: ;--
*
* @param database picto.db helper
* @param inputStream the stream where the ddl of picto.db is depicted
* @throws IOException
*/
protected static void executeSQLScript(SQLiteDatabase database, InputStream inputStream)
throws IOException {
Vector<String> commands = new Vector<String>(50);
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String command = "";
do {
String line = r.readLine();
if (line == null) break;
if (line.equals(";--")) {
commands.add(command + ";");
command = "";
} else if (!line.startsWith("--")) command += line + " ";
} while (true);
r.close();
for (String sqlStatement : commands)
database.execSQL(sqlStatement);
Log.i(Device.class.getName(), "Database created");
}
/**
* @param url
* @return the local parth where the picto is located or null
*/
public String getPictoPath(String url) {
String path;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query("picto", new String[]{"path"}, "'url'=?", new String[]{url}, null, null, null, null);
path = cursor.getCount() > 0 ? cursor.getString(0) : null;
cursor.close();
db.close();
return path;
}
/**
* get the next local id for a new local pictogram
*
* @see com.yottacode.pictogram.dao.Picto
*/
public int getNextLocalPictoID() {
SQLiteDatabase db = this.getReadableDatabase();
int next_key;
Cursor cursor = db.query("picto", new String[]{"(SELECT MIN(id) FROM picto) AS MIN"}, null, null, null, null, null, "1");
cursor.moveToFirst();
next_key=cursor.getInt(cursor.getColumnIndex("MIN"))-1;
if (next_key>=0) next_key=-1;
cursor.close();
db.close();
return next_key;
}
@Override
public void onCreate(SQLiteDatabase db) throws RuntimeException {
try {
executeSQLScript(db, DeviceHelper.getDBScriptStream(this.context));
Img.mkDirs(this.context);
copyCoreVocabulary();
} catch (java.io.IOException io) {
throw new RuntimeException("Update database ir cached Images error", io);
}
}
private void copyCoreVocabulary() {
AssetManager assetManager = this.context.getAssets();
String[] files = null;
int size=0;
int filesok=0;
try {
files = assetManager.list(context.getResources().getString(R.string.core_vocabulary));
} catch (IOException e) {
Log.e(this.getClass().getCanonicalName(), "Failed to get asset file list.", e);
}
if (files != null) for (String filename : files) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(context.getResources().getString(R.string.core_vocabulary)+File.separatorChar+filename);
File outFile = new File(Img.path(this.context, Img.VOCABULARY)+filename);
out = new FileOutputStream(outFile);
size+=copyFile(in, out);
} catch(IOException e) {
Log.e(this.getClass().getCanonicalName(), "Failed to copy asset Image: " + context.getResources().getString(R.string.core_vocabulary)+File.separatorChar+filename, e);
}
finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
Log.e(this.getClass().getCanonicalName(), "Failed to copy asset Image: " + filename, e);
}
}
if (out != null) {
try {
out.close();
filesok++;
} catch (IOException e) {
Log.e(this.getClass().getCanonicalName(), "Failed to copy asset Image: " + filename, e);
}
}
}
}
Log.i(Device.class.getName(), "Core vocabulary Images installed ("+filesok+" pictos,"+(size/1024)+" KB)");
}
private int copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[8192];
int read;
int size=0;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
size+=read;
}
return size;
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
package com.yottacode.pictogram.dao;
/**
* Created by Fernando on 15/03/2016.
*/
public class LoginException extends Exception{
public static final int BAD_LOGIN=1;
public static final int NO_STUDENTS=2;
public static final int TIME_OUT=3;
int code;
public LoginException(String msg, int code) {
super(msg);
this.code=code;
}
public boolean login_failed() {return this.code==LoginException.BAD_LOGIN;}
public boolean no_supervisor_students() {return this.code==LoginException.NO_STUDENTS;}
public boolean time_out() {return this.code==LoginException.TIME_OUT;}
public String getLocalizedMessage() {
return super.getLocalizedMessage()+" Login ok:"+!login_failed()+" Supervisor without students:"+no_supervisor_students();
}
}
package com.yottacode.pictogram.dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Vector;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.grammar.Vocabulary;
import com.yottacode.pictogram.action.Action;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Platform abstraction (Android)
* @see PCBDBHelper
* *
*
* @author Fernando Martinez Santiago
* @version 1.0
*/
class DeviceHelper {
static String getTimestamp() {
return (new android.text.format.Time()).toString();
}
static String getDBName(Context context) {
return context.getResources().getString(R.string.db_name);
}
static InputStream getDBScriptStream(Context context) {
return context.getResources().openRawResource(R.raw.pcbdb_create);
}
static boolean force_create(Context context) {
return context.getResources().getBoolean(R.bool.force_db_create);
}
}
/**
* Data Access Object to manage Pictogram Communicator Board database regarding a logged user
* This class requires:
* The script to create the DB allocated in res/raw/pcbdb_create.sql
* The entries db_name and db_script_error in strings.xml
*
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class PCBDBHelper extends SQLiteOpenHelper {
User currentUser;
boolean user_online; //true if the given user logged into the server
/**
* Create a helper object to create, open, and/or manage a database.
*
* @param factory null
* @param version 1
* @param user the user of the PCB, if any. If not, last user is loaded from the DB
*/
public PCBDBHelper(CursorFactory factory, int version, User user) {
super(PCBcontext.getContext(), DeviceHelper.getDBName(PCBcontext.getContext()), factory, version);
this.user_online=false;
this.setCurrentUser(user == null ? this.getCurrentUser() : user);
}
public boolean isUser_online() {return this.user_online;}
public void user_online(boolean user_online) {this.user_online=user_online;}
/**
* Save the current user of the PCB. It is required to retrieve the last user.
*
* @param user the current PCB user (Student or Supervisor with a given Student collection)
* @see PCBDBHelper#getCurrentUser()
*/
private void setCurrentUser(User user) {
this.currentUser = user;
PCBcontext.getDevice().setLastStuId(user.get_id_stu());
PCBcontext.getDevice().setLastSupId(user.get_id_sup());
}
/**
* @return the last user stored into the DB
* @see PCBDBHelper#getCurrentUser()
*/
public User loadLastUser() {
String stu=String.valueOf(PCBcontext.getDevice().getLastStuId()), sup=String.valueOf(PCBcontext.getDevice().getLastSupId());
User last_user = null;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query("users_detail", null, "id_stu=? AND id_sup=?", new String[]{stu, sup}, null, null, null, null);
if (cursor.getCount() > 0) {
cursor.moveToFirst();
try {
last_user = new User(cursor.getInt(0), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getString(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8),
cursor.getInt(9), cursor.getString(10), cursor.getString(11), cursor.getString(12), cursor.getString(13), cursor.getString(14), cursor.getString(15), cursor.getString(16), cursor.getString(17));
} catch (JSONException e) {
Log.e(this.getClass().getName(), e.getMessage() + " BAD FORMED JSON: " + cursor.getString(5));
System.exit(-1);
}
}
cursor.close();
db.close();
return last_user;
}
/**
* @return the current user who has been logged into the PCB
* @see PCBDBHelper#setCurrentUser(User)
*/
public User getCurrentUser() {
if (this.currentUser == null) this.currentUser = loadLastUser();
return this.currentUser;
}
/**
* Insert a new action
*
* @param action to be inserted
* @see Action
*/
public void insertAction(Action action) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values=new ContentValues(1);
values.put("json_action", action.getDescription().toString());
db.insert("action", null, values);
db.close();
}
/**
* Load saved actions
* @see Action
*/
public Vector<JSONObject> loadActions() {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query("action", null, null, null, null, null, null, null);
Vector<JSONObject> actions= new Vector(cursor.getCount());
Log.i(this.getClass().getName(), "Local action recovering " + cursor.getCount() + " actions from local DB");
cursor.moveToFirst();
while (cursor.moveToNext())
try {
actions.add(new JSONObject(cursor.getString(0)));
} catch (JSONException e) {
Log.e(this.getClass().getName(), "Recovering batch actions:"+e.getMessage());
}
cursor.close();
db.close();
return actions;
}
/**
* Delete de locally storaged action
* @return the number of deleted rows
*/
public int deleteActions() {
SQLiteDatabase db = this.getWritableDatabase();
return db.delete("action",null,null);
}
/**
* the collection (set of pictos) of the current student
*
* @see com.yottacode.pictogram.dao.Picto
*/
public Vocabulary getStudentVocabulary(Vocabulary vocabulary ) throws JSONException {
int id_stu = this.getCurrentUser().get_id_stu();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query("collection_detail", null, "id_stu=?", new String[]{String.valueOf(id_stu)}, null, null, null, null);
Log.i(this.getClass().getName(), "Local recovering " + cursor.getCount() + " pictos for student " + id_stu + " from local DB");
cursor.moveToFirst();
while (cursor.moveToNext()) {
Picto picto = new Picto(cursor.getInt(1), cursor.getString(2), cursor.getString(3), cursor.getString(4));
vocabulary.loadPicto(picto);
}
cursor.close();
db.close();
return vocabulary;
}
/**
* Set/update the set of pictos of the current student. Pictos which are no longer used are dropped from the DB
*
* @param vocabulary the vocabulary, the configuration of the Student
* @see com.yottacode.pictogram.dao.Picto
*/
public void setStudentVocabulary(Vocabulary vocabulary) {
SQLiteDatabase db = this.getWritableDatabase();
int id_stu = this.getCurrentUser().get_id_stu();
int seconds1 = Calendar.getInstance().get(Calendar.SECOND);
db.delete("collection", "id_stu=" + id_stu, null);
int newsize=0;
ContentValues values=new ContentValues(5);
values.put("id_stu", id_stu);
for (Picto picto : vocabulary) {
newsize++;
values.put("id_picto", picto.get_id());
values.put("url", picto.get_url());
values.put("translation",picto.get_translation());
values.put("attributes",picto.get_json_attrs());
db.insert("collection_detail", null, values);
}
int seconds2 = Calendar.getInstance().get(Calendar.SECOND);
Log.i(this.getClass().getName(), " Local student vocabulary updated, id:" + id_stu + ", cats: " + vocabulary.size() + " time:" + (seconds2 - seconds1) + " secs. Size: " + newsize + " read only?" + db.isReadOnly());
db.close();
}
/**
* Insert/update a picto of the current student.
*
* @param picto added to the Student collection
* @see com.yottacode.pictogram.dao.Picto
*/
public void addPicto(Picto picto) {
int id_stu = this.getCurrentUser().get_id_stu();
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values=new ContentValues(6);
values.put("id_stu", id_stu);
values.put("id_picto", picto.get_id());
values.put("url", picto.get_url());
values.put("translation",picto.get_translation());
values.put("attributes",picto.get_json_attrs());
db.insert("collection_detail", null, values);
db.close();
}
/**
* Delete a picto of the current collection's student. Pictos which are no longer used are dropped from the DB
*
* @param picto_id to be deleted from the configuration of the Student
* @see com.yottacode.pictogram.dao.Picto
*/
public void deletePicto(int picto_id) {
int id_stu = this.getCurrentUser().get_id_stu();
SQLiteDatabase db = this.getWritableDatabase();
db.delete("collection","id_stu=? AND id_picto=?",
new String[]{ Integer.toString(this.currentUser.get_id_stu()),
Integer.toString(picto_id)});
db.close();
}
/**
* Update a picto of the current collection's student.
*
* @param picto_id to be modified from the configuration of the Student
* @param attrs new picto attributes
* @see com.yottacode.pictogram.dao.Picto
*/
public void modifyPicto(int picto_id, String attrs) {
int id_stu = this.getCurrentUser().get_id_stu();
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues(1);
values.put("attributes",attrs);
int updates=db.update("collection",values, "id_stu=? AND id_picto=?", new String[] {Integer.toString(id_stu), Integer.toString(picto_id)});
Log.i(this.getClass().getCanonicalName(),"Modify "+updates+" Picto, id. "+picto_id+" attributes="+attrs);//+ "::"+ Arrays.toString(Thread.currentThread().getStackTrace()));
db.close();
}
@Override
public void onCreate(SQLiteDatabase db) throws RuntimeException {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
\ No newline at end of file
package com.yottacode.pictogram.dao;
import android.graphics.Color;
import android.util.Log;
import com.yottacode.pictogram.action.VocabularyAction;
import com.yottacode.pictogram.net.PictoUploader;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
/**
* A object which represents a pictogram
* *
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class Picto extends Img {
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
public final static class JSON_ATTTRS {
public static String CATEGORY = "id_cat";
public static String COLUMN = "coord_x";
public static String ROW = "coord_y";
public static String FREE_COLUMN = "free_category_coord_x";
public static String FREE_ROW = "free_category_coord_y";
public static String MAGNIFY = "magnify";
public static String HIGHLIGHT = "highlight";
public static String STATUS = "status";
public static String COLOR = "color";
public static String PCB_STATUS_MODIFICATION="pcb_status_modification";
}
public final static class JSON_ATTTR_STATUS_VALUES {
static String ENABLED = "enabled";
static String DISABLED = "disabled";
static String INVISIBLE = "invisible";
}
public final static int NO_CATEGORY=-1;
public final static int ROW_UNCATEGORIZED_CONCEPTS=0;
private JSONObject attributes;
private String translation;
public Picto(int id, String url, String translation, int cat, int row, int column, int freeRow, int freeColumn) throws JSONException {
this(id, url, translation, new JSONObject()
.put(JSON_ATTTRS.CATEGORY, cat)
.put(JSON_ATTTRS.COLUMN, column)
.put(JSON_ATTTRS.ROW, row)
.put(JSON_ATTTRS.FREE_ROW, freeRow)
.put(JSON_ATTTRS.FREE_COLUMN, freeColumn)
.put(JSON_ATTTRS.STATUS, JSON_ATTTR_STATUS_VALUES.ENABLED));
}
public Picto(int id, String url,String translation, String attributes) throws JSONException {
this(id, url, translation, new JSONObject(attributes));
}
public Picto(int id, String url,String translation, JSONObject attributes) {
super(id,url,Img.VOCABULARY);
this.translation=translation;
this.attributes = attributes;
try {
if (this.attributes.get(JSON_ATTTRS.CATEGORY)==null)
this.attributes.put(JSON_ATTTRS.CATEGORY, Picto.NO_CATEGORY);
} catch (JSONException e) {
Log.e(this.getClass().getName(), e.toString());
}
}
/**
*
* @return true if it's a local pictogram
*/
public boolean is_local() {return this.get_id()<0;}
/**
*
* @return de id of the picto
*/
public int get_id() {return this.id;}
public void update_id(int id) {
this.id = id;
}
/**
*
* @return the location of the picto
*/
public String get_url() {return this.url;}
/**
*
* @return the translation of the picto to a given language
*/
public String get_translation() {return this.translation;}
/**
*
* @return a JSON attributes of the picto
*/
public String get_json_attrs() {
return this.attributes.toString();
}
/**
* JSON attrs of the picto except the param regarding is locally modified since this is not saved into the server
*
*
*/
public String get_json_server_attrs() {
JSONObject json = null;
try {
json = new JSONObject(this.attributes.toString());
} catch (JSONException e) {
e.printStackTrace();
}
json.remove(JSON_ATTTRS.PCB_STATUS_MODIFICATION);
return json.toString();
}
/**
* Set JSON attribute of the picto
* @param attr attributes modified
*/
public void set_json_attr(JSONObject attr) {
this.attributes=attr;
}
/**
*
* @return the category of the picto
*/
public int get_category() {
try {
return this.attributes.getInt(JSON_ATTTRS.CATEGORY);
} catch (JSONException e) {
return Picto.NO_CATEGORY;
}
}
/**
*
* @return the magnifiy property of a picto
*/
public boolean is_magnify() {
try {
return Boolean.parseBoolean(this.attributes.getString(JSON_ATTTRS.MAGNIFY));
} catch (JSONException e) {
return false;
}
}
/**
*
* @return if the picto is enabled
*/
public boolean is_enabled() {
try {
return this.attributes.getString(JSON_ATTTRS.STATUS).equals(JSON_ATTTR_STATUS_VALUES.ENABLED);
} catch (JSONException e) {
return false;
}
}
/**
*
* @return if the picto is disabled
*/
public boolean is_disabled() {
try {
return this.attributes.getString(JSON_ATTTRS.STATUS).equals(JSON_ATTTR_STATUS_VALUES.DISABLED);
} catch (JSONException e) {
return false;
}
}
/**
*
* @return if the picto is visible
*/
public boolean is_invisible() {
try {
return this.attributes.getString(JSON_ATTTRS.STATUS).equals(JSON_ATTTR_STATUS_VALUES.INVISIBLE) ||
(this.is_category() && !PCBcontext.getVocabulary().isVisibleCategory(this.get_id()));
} catch (JSONException e) {
return false;
}
}
/**
*
* @return the highlight property of a picto
*/
public boolean is_highlight() {
try {
return Boolean.parseBoolean(this.attributes.getString(JSON_ATTTRS.HIGHLIGHT));
} catch (JSONException e) {
return false;
}
}
/**
*
* @return the status of a picto (enabled | disabled | invisible)
*/
public String get_status() {
try {
return this.attributes.getString(JSON_ATTTRS.STATUS);
} catch (JSONException e) {
return "enabled"; // By default
}
}
/**
*
* @return the row of the picto
*/
public int get_row() {
return this.attributes.optInt(JSON_ATTTRS.ROW, -1);
}
/**
*
* @return the column of the picto
*/
public int get_column() {
return this.attributes.optInt(JSON_ATTTRS.COLUMN, -1);
}
/**
*
* @return the free row of the picto
*/
public int getFreeRow() {
return this.attributes.optInt(JSON_ATTTRS.FREE_ROW, -1);
}
/**
*
* @return the free column of the picto
*/
public int getFreeColumn() {
return this.attributes.optInt(JSON_ATTTRS.FREE_COLUMN, -1);
}
/**
*
* @return the color of the picto
*/
public int get_color() {
try {
String color = this.attributes.getString(JSON_ATTTRS.COLOR);
int icolor = Color.parseColor(color);
return icolor;
} catch (JSONException e) {
return -1;
}
}
/**
*
* @return the lighter color of the picto
*/
public int get_lighter_color() {
final int LIGHT=0x22;
int color = this.get_color();
int red = Color.red(color)+LIGHT, blue = Color.blue(color)+LIGHT, green = Color.green(color)+LIGHT;
return Color.rgb(red, green, blue);
}
/**
*
* @return the darkner color of the picto
*/
public int get_darkner_color() {
/*
final int DARK=-0x22;
int color = this.get_color();
int red = Color.red(color)+DARK, blue = Color.blue(color)+DARK, green = Color.green(color)+DARK;
return Color.rgb(red,green,blue);
*/
/*
String hexColor = String.format("#%06X", (0xFFFFFF & this.get_color()));
String hexColorDarker = String.format("#%06X", (0xFFFFFF & darker(this.get_color(), (float)0.9)));
Log.d(LOG_TAG, "Color actual: " + hexColor);
Log.d(LOG_TAG, "Color darker: " + hexColorDarker);
*/
return darker(this.get_color(), (float) 0.9);
}
public static int darker (int color, float factor) {
int a = Color.alpha(color);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
return Color.argb(a,
Math.max((int) (r * factor), 0),
Math.max((int) (g * factor), 0),
Math.max((int) (b * factor), 0));
}
/**
* A picto is a category iif:
* (i) it has not got category
* (ii) it is not an uncategorized concept (such as "yes","no","thank you")
* @return
*/
public boolean is_category() {
return this.get_category()==Picto.NO_CATEGORY &&
this.get_column() != Picto.ROW_UNCATEGORIZED_CONCEPTS &&
this.getFreeColumn() == -1 &&
this.getFreeRow() == -1;
}
/**
*
* @overide
* Two user are equals whenever they have the same id
*/
public boolean equals(Object o) {
return ((Picto)o).id==this.id;
}
/**
* toString method
*/
public String toString(){
return "(" + get_id() + ") - ["+ get_row() +","+ get_column()+"]" + get_translation() + "(Cat: " + get_category() + ") - " + get_url() + " --- " + get_json_attrs();
}
/**
* modify locally the status of the picto
* @return true if current status is enabled. False in other case.
*/
public boolean alter_status() {
String status=is_enabled() ? JSON_ATTTR_STATUS_VALUES.DISABLED : JSON_ATTTR_STATUS_VALUES.ENABLED;
Log.i(this.getClass().getCanonicalName(),"Picto id. "+get_id()+" status enabled/disabled modified to "+is_enabled());
try {
this.attributes.put(JSON_ATTTRS.STATUS, status);
set_local_status(true);
if (!is_local()) {
new PictoUploader(this).uploadState();
PCBcontext.getActionLog().log(new VocabularyAction(VocabularyAction.ALTERATTRS, this));
}
} catch (JSONException e) {
e.printStackTrace();
Log.e(this.getClass().getCanonicalName(),e.getMessage());
}
return is_enabled();
}
/**
*
* @return true if the status of picto was modified from the PCB
*/
public boolean local_status() {
return !this.attributes.isNull(JSON_ATTTRS.PCB_STATUS_MODIFICATION);
}
/**
* when the status is locally modified, it is required to remove when it is uploaded since it is no longer a local modification
*/
public void set_local_status(boolean local) {
if (local)
try {
this.attributes.put(JSON_ATTTRS.PCB_STATUS_MODIFICATION, true);
PCBcontext.getPcbdb().modifyPicto(this.get_id(), this.get_json_attrs());
} catch (JSONException e) {
e.printStackTrace();
Log.e(this.getClass().getCanonicalName(), e.getMessage());
}
else {
this.attributes.remove(JSON_ATTTRS.PCB_STATUS_MODIFICATION);
PCBcontext.getPcbdb().modifyPicto(this.get_id(), this.get_json_attrs());
}
}
}
package com.yottacode.pictogram.dao;
import java.io.IOException;
import org.json.JSONObject;
import org.json.JSONException;
import android.content.Context;
import android.graphics.Bitmap;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.grammar.Vocabulary;
/**
* A user of the PCB. A user can be an Student or a Supervisor using a student configuration
* *
* @author Fernando Martinez Santiago
* @version 1.1
*/
public class User {
public static final int NO_SUPERVISOR = -1;
public boolean has_supervisor() {
return this.get_id_sup()!=User.NO_SUPERVISOR;
}
public final static class JSON_STUDENT_ATTTRS{
static String CATEGORIES = "categories";
static String INPUT_FEEDBACK = "input feedback";
static String INPUT_SELECTION = "input selection";
static String PICTOGRAM_SIZE = "pictogram size";
static String TTS_ENGINE = "tts engine";
static String TTS_VOICE = "tts voice";
}
private Img img_stu;
private String nickname_stu, pwd_stu, name_stu, surname_stu, gender_stu, lang_stu;
private JSONObject attributes_stu;
private Img img_sup;
private String email_sup, pwd_sup, name_sup, surname_sup, gender_sup, lang_sup, tts_engine_sup;
public User(int id_stu, String nickname_stu, String pwd_stu, String name_stu, String surname_stu, String url_img_stu, String gender_stu, String lang_stu, String attributes_stu) throws JSONException {
this(id_stu, nickname_stu, pwd_stu, name_stu, surname_stu, url_img_stu, gender_stu, lang_stu, attributes_stu,User.NO_SUPERVISOR,"","","","","","M","es-es",null);
}
public User(int id_stu, String nickname_stu, String pwd_stu, String name_stu, String surname_stu, String url_img_stu, String gender_stu, String lang_stu, String attributes_stu,
int id_sup, String email_sup, String pwd_sup, String name_sup, String surname_sup, String url_img_sup, String gender_sup, String lang_sup, String tts_engine_sup) throws JSONException {
this.img_stu=new Img(id_stu,url_img_stu,Img.STUDENT);
this.nickname_stu=nickname_stu;
this.pwd_stu=pwd_stu;
this.name_stu=name_stu;
this.surname_stu=surname_stu;
this.gender_stu=gender_stu;
this.lang_stu=lang_stu;
this.attributes_stu= attributes_stu!=null && attributes_stu.trim().length()>0 ? new JSONObject(attributes_stu) : new JSONObject();
this.img_sup=new Img(id_sup,url_img_sup,Img.SUPERVISOR);
this.email_sup=email_sup;
this.pwd_sup=pwd_sup;
this.name_sup=name_sup;
this.surname_sup=surname_sup;
this.gender_sup=gender_sup;
this.lang_sup=lang_sup;
this.tts_engine_sup=tts_engine_sup;
}
/**
*
* @return id of the student
*/
public int get_id_stu() {return this.img_stu.get_id();}
/**
*
* @return nickname (username) of the student
*/
public String get_nickname_stu() {return this.nickname_stu;}
/**
*
* @return pwd of the student
*/
public String get_pwd_stu() {return this.pwd_stu;}
/**
* @ return the RESTAPI operator for the current student
*/
public String get_restapi_operation_stu() {
return "stu/"+this.get_id_stu();
}
/**
*
* @return Img of the student
*/
public Img get_img_stu() {return this.img_stu;}
/**
*
* @return name of the student
*/
public String get_name_stu() {return this.name_stu;}
/**
*
* @return surname of the student
*/
public String get_surname_stu() {return this.surname_stu;}
/**
*
* @return url of the picture of the student profile
*/
public String get_url_img_stu() {return this.img_stu.get_url();}
/**
*
* @return Img of the student
*/
public Bitmap get_bitmap_stu(Context context) throws IOException {return this.img_stu.get_bitmap(context);}
/**
*
* @return Img of the student
*/
public Img get_Img_stu() {return this.img_stu;}
/**
*
* @return gender of the student
*/
public String get_gender_stu() {return this.gender_stu;}
/**
*
* @return language of the student
*/
public String get_lang_stu() {return this.lang_stu;}
/**
*
* @return id of the supervisor
*/
public int get_id_sup() {return this.img_sup.get_id();}
/**
*
* @return nickname (username) of the student
*/
public String get_email_sup() {return this.email_sup;}
/**
*
* @return pwd of the student
*/
public String get_pwd_sup() {return this.pwd_sup;}
/**
*
* @return name of the supervisor
*/
public String get_name_sup() {return this.name_sup;}
/**
*
* @return surname of the supervisor
*/
public String get_surname_sup() {return this.surname_sup;}
/**
*
* @return url of image of the supervisor profile
*/
public String get_url_img_sup() {return this.img_sup.get_url();}
/**
*
* @return Bitmap of the supervisor
*/
public Bitmap get_bitmap_sup(Context context) throws IOException {return this.img_sup.get_bitmap(context);}
/**
*
* @return Img of the supervisor
*/
public Img get_Img_sup() {return this.img_sup;}
/**
*
* @return gender of the supervisor
*/
public String get_gender_sup() {return this.gender_sup;}
/**
*
* @return language of the supervisor
*/
public String get_lang_sup() {return this.lang_sup;}
/**
*
* @return TTS engine of the supervisor
*/
public String get_tts_engine_sup() {return this.tts_engine_sup;}
/**
*
* @return a JSON attributes of the user
*/
public String get_json_attrs() {
return this.attributes_stu.toString();
}
/**
*
* @return a JSON attribute of the student or NULL if the given attribute is not available
*/
public String get_json_attr(String attr) {
try {
return this.attributes_stu.getString(attr);
} catch (JSONException e) {
return null;
}
}
/**
*
* @return true if the collection is organized by categories (default: True)
*/
public boolean has_categories() {
return this.attributes_stu.optBoolean(JSON_STUDENT_ATTTRS.CATEGORIES, true);
}
/**
*
* @return input feedback of the student configuration (default: "vibration")
*/
public String get_input_feedback() {
try {
return this.attributes_stu.getString(JSON_STUDENT_ATTTRS.INPUT_FEEDBACK);
} catch (JSONException e) {
return "vibration";
}
}
/**
*
* @return input selection of the student configuration (default: "click")
*/
public String get_input_selection() {
try {
return this.attributes_stu.getString(JSON_STUDENT_ATTTRS.INPUT_SELECTION);
} catch (JSONException e) {
return "click";
}
}
/**
*
* @return pictogram size of the student configuration (default: "normal")
*/
public String get_pictogram_size() {
try {
return this.attributes_stu.getString(JSON_STUDENT_ATTTRS.PICTOGRAM_SIZE);
} catch (JSONException e) {
return "normal";
}
}
public boolean is_supervisor() {
return get_id_sup()!=User.NO_SUPERVISOR;
}
/**
*
* @overide
* Two user are equals whenever they have the same student and supervisor id
*/
public boolean equals(Object o) {
return ((User)o).img_stu.get_id()==this.img_stu.get_id() && ((User)o).img_sup.get_id()==this.img_sup.get_id();
}
}
\ No newline at end of file
package com.yottacode.pictogram.grammar;
import android.os.AsyncTask;
import android.util.Log;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.action.VocabularyAction;
import com.yottacode.pictogram.dao.PCBDBHelper;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.net.ImgDownloader;
import com.yottacode.pictogram.net.PictoUploader;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.net.iRestapiListener;
import com.yottacode.pictogram.action.Room;
import com.yottacode.pictogram.net.iImgDownloaderListener;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.tools.GUITools;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.Vector;
/**
* PCB Vocabulary manager
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class Vocabulary implements Iterable<Picto> {
Hashtable<Integer,LinkedList<Picto>> pictos;
static int DEFAULT_VOCABULARY_SIZE=200;
VocabularyTalk talk;
iImgDownloaderListener imgListener;
/**
* Creates a new vocabulary and download/load the vocabulary and the corresponding pictos
* @param listener
*/
public Vocabulary(iImgDownloaderListener listener) {
this.pictos = new Hashtable<>(Vocabulary.DEFAULT_VOCABULARY_SIZE);
this.imgListener=listener;
if (PCBcontext.getNetService().online()) {
synchronize();
}else
try {
PCBcontext.getPcbdb().getStudentVocabulary(this);
if (listener!=null)
listener.loadComplete();
} catch (JSONException e) {
Log.e(this.getClass().getName(),"Local vocabulary recover failed: "+e.getMessage());
listener.error(e);
}
}
public void listen(Room room, iVocabularyListener listener) {
iVocabularyListener listeners[] = {new iVocabularyListener() {
@Override
public void change(action action, int picto_cat, int picto_id, JSONObject args) {
switch (action) {
case delete: {
removePicto(picto_cat, picto_id);
break;
}
case update:{
Log.i(this.getClass().getCanonicalName(), "Picto update "+args.toString());
try {
modifyAttsPicto(picto_cat, picto_id, args.getJSONObject("attributes"));
} catch (JSONException e) {
e.printStackTrace();
}
break;
}
case add:{
try{
String text= args.getJSONObject("expression").getString("text");
String uri=args.getJSONObject("picto").getString("uri");
JSONObject attrs_picto = args.getJSONObject("attributes");
addPicto(new Picto(picto_id, uri, text, attrs_picto),ImgDownloader.tsource.remote);
} catch (JSONException e) {
Log.e(this.getClass().getCanonicalName(), e.getClass().getCanonicalName() + "--" + e);
// System.exit(-1);
}
break;
}
}
}
},listener};
talk = new VocabularyTalk(room, listeners);
}
/**
* UPload local status modifications and new pictos. Note that when
* a picto is uploaded is not required to delete from the local PCB
* because the PCB will get the remote vocabulary at the end of the process
* The only issue is: what happens whether the upload fails? --> The image will be lost.
* TODO: keep record of failed uploaded images to re-include as local pictos
*/
private void synchronize_upload() {
try {
PCBcontext.getPcbdb().getStudentVocabulary(this);
} catch (JSONException e) {
Log.e(this.getClass().getName(), " Picto json error from local storage: " + e.getMessage());
}
for (Picto picto: this) {
if (picto.local_status()) {
new PictoUploader(picto).uploadState();
Log.i(this.getClass().getCanonicalName(), "Picto status modified while offline. Picto translation: '" +
picto.get_translation() + "', id:" + picto.get_id() + " Local status?" + picto.local_status());
}
if (picto.is_local()) //id<0 iif it is a local id
try {
Log.i(this.getClass().getCanonicalName(), "Picto added while offline. Picto translation: '" +
picto.get_translation() + "', id:" + picto.get_id() + " Local status?" + picto.local_status());
new PictoUploader(picto).upload(PCBcontext.getContext());
} catch (IOException e) {
e.printStackTrace();
Log.e(this.getClass().getName(), " Picto json error from server: " + picto.toString());
}
}
}
/**
* the vocabulary is (i) updated to and (ii) downloaded from the server.
*/
public void synchronize() {
synchronize_upload();
//download
final String picto_str="/pictos";
String operation=PCBcontext.getPcbdb().getCurrentUser().get_restapi_operation_stu()+picto_str;
PCBcontext.getRestapiWrapper().ask(operation, new iRestapiListener() {
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
if (result != null) {
final String jpicto = "picto";
final String jid = "id";
final String juri = "uri";
final String jattributes = "attributes";
final String jexpression = "expression";
final String jexpression_text = "text";
Picto[] pictos = new Picto[result.length()];
JSONObject picto = null, expression = null, attributes = null;
JSONObject ojpicto=null;
try {
for (int i=0; i < result.length(); i++) {
ojpicto=result.getJSONObject(i);
picto = ojpicto.getJSONObject(jpicto);
expression = ojpicto.getJSONObject(jexpression);
attributes = ojpicto.getJSONObject(jattributes);
pictos[i] = new Picto(picto.getInt(jid),
picto.getString(juri),
expression.getString(jexpression_text),
attributes);
}
synchronizeImgs(pictos);
if (PCBcontext.is_user_logged()) PCBcontext.getPcbdb().setStudentVocabulary(Vocabulary.this);
else Log.i(this.getClass().getName(),"Downloaded images ended when the user comes to logout");
Log.i(this.getClass().getName(), " Pictos downloaded: " + result.length());
} catch (JSONException e) {
StackTraceElement traces[] = e.getStackTrace();
for (StackTraceElement s: traces)
Log.e(s.getClassName()+"."+s.getFileName()+"."+s.getLineNumber(),s.toString());
Log.e(this.getClass().getName(), " Picto JSON error from server: " + ojpicto.toString());
this.error(e);
}
}
}
@Override
public void result(JSONObject result) {
}
@Override
public void error(Exception e) {
Log.e(this.getClass().getName(), " Server RESTAPI error: " + e.getLocalizedMessage());
Vocabulary.this.imgListener.error(e);
}
});
}
/**
* synchronize the local collection with the downloaded remote collection
*
* @param updated_collection is the downloaded remote collection
* @throws JSONException in case of user.attributes json parsing error
*/
private void synchronizeImgs(Picto updated_collection[]) throws JSONException {
Vector<Img> imgs=new Vector<Img>(updated_collection.length);
this.pictos.clear();
for (Picto updated_picto: updated_collection) {
LinkedList<Picto> pictos_cat;
Picto picto = new Picto(updated_picto.get_id(),
updated_picto.get_url(),
updated_picto.get_translation(),
updated_picto.get_json_attrs());
if (pictos.containsKey(picto.get_category()))
pictos_cat = pictos.get(picto.get_category());
else {
pictos_cat = new LinkedList<>();
pictos.put(new Integer(picto.get_category()),pictos_cat);
}
pictos_cat.add(picto);
imgs.add(new Img(picto.get_id(), picto.get_url(), Img.VOCABULARY));
}
Log.d(this.getClass().getName(), "Vocabulary size: " + updated_collection.length);
ImgDownloader downloader = new ImgDownloader(PCBcontext.getContext(), imgListener,ImgDownloader.tsource.remote);
downloader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imgs);
}
public int size() { return this.pictos.size();}
/**
* It includes/updates a new picto to the user collection: download the image from remote or local storage, add the picto to the current vocabulary and update the database
* @param pic
*/
public void addPicto(Picto pic, ImgDownloader.tsource source){
addPicto(pic, source, this.imgListener);
}
public void addPicto(Picto pic, ImgDownloader.tsource source, iImgDownloaderListener imgListener){
Vector<Img> imgs=new Vector<Img>(1);
imgs.add(new Img(pic.get_id(), pic.get_url(), Img.VOCABULARY));
ImgDownloader downloader = new ImgDownloader(PCBcontext.getContext(), imgListener,source);
downloader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imgs);
loadPicto(pic);
PCBcontext.getPcbdb().addPicto(pic);
}
/**
*
* @param pic_cat
* @return
*/
private boolean empty_category(int pic_cat) {return this.pictos.get(pic_cat)==null;}
private int find_picto_index(int pic_cat, int pic_id) {
LinkedList<Picto> pictos_cat=this.pictos.get(pic_cat);
int index=-1;
for (int i=0; i<pictos_cat.size() && index==-1; i++)
if (pictos_cat.get(i).get_id()==pic_id) index=i;
return index;
}
/**
* It removes de given pic from the user collection
* @param pic_id
*/
public void removePicto(int pic_cat, int pic_id){
LinkedList<Picto> pictos_cat=this.pictos.get(pic_cat);
int index = find_picto_index(pic_cat, pic_id);
if (index>=0) { //puede ocurrir que se intente borrar un pictograma dos veces
pictos_cat.remove(index);
PCBcontext.getPcbdb().deletePicto(pic_id);
}
else
Log.i(this.getClass().getCanonicalName(),"Trying to delete an unregistered picto:"+pic_id+" cat:"+pic_cat);
}
/**
* It modifies de given pic from the user collection
* @param pic_cat
* @param pic_id
* @param attrs
*/
public void modifyAttsPicto(int pic_cat, int pic_id, JSONObject attrs) {
int index=find_picto_index(pic_cat, pic_id);
if (index>=0) { //puede ocurrir que se intente modificar un pictograma que fue borrado
Picto picto = this.pictos.get(pic_cat).get(index);
picto.set_json_attr(attrs);
PCBcontext.getPcbdb().modifyPicto(pic_id, attrs.toString());
}
else
Log.i(this.getClass().getCanonicalName(),"Trying to modify an unregistered picto:"+pic_id+" cat:"+pic_cat);
}
/**
* It loads into the vocabulary a given picto which was stored in the local db.
* This method is called by the PCDBHelper when the collection is loaded
* @param picto
* @seealso com.yottacode.pictogram.dao.PCBDBHelper.getStudentVocabulary
*/
public void loadPicto(Picto picto) {
LinkedList<Picto> pictos_cat;
if (this.pictos.containsKey(picto.get_category()))
pictos_cat = pictos.get(picto.get_category());
else {
pictos_cat = new LinkedList();
this.pictos.put(new Integer(picto.get_category()),pictos_cat);
}
pictos_cat.add(picto);
}
/**
*
* @return list of pictos which should be selectable at the beginning of a sentence. Empty categories are removed
*/
public LinkedList<Picto> startSentence(){
if (PCBcontext.getPcbdb().getCurrentUser().has_categories()) {
return this.pictos.get(new Integer(Picto.NO_CATEGORY));
} else {
LinkedList<Picto> freePictos = new LinkedList<>();
for (LinkedList<Picto> category : pictos.values()) {
for (Picto picto : category) {
if (picto.getFreeRow() != -1 && picto.getFreeColumn() != -1) {
freePictos.add(picto);
}
}
}
return freePictos;
}
}
/**
* A category is visible iif it has at least an enabled concept
* @param id
* @return
*/
public boolean isVisibleCategory(int id) {
if (empty_category(id)) return false;
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) return true;
boolean visible=false;
for (Picto picto : this.pictos.get(id)) {
visible=!picto.is_invisible();
if (visible) break;
}
return visible;
}
/*
* It saves locally a new picto obtained from the PCB
*/
public Picto saveLocalPicto(String url, String exp, int cat, int coord_x, int coord_y, int free_category_coord_x, int free_category_coord_y, final iLocalPicto listener) {
int id= PCBcontext.getDevice().getNextLocalPictoID();
final Picto picto[]=new Picto[1];
try {
picto[0] = new Picto(id, url, exp, cat, coord_x, coord_y, free_category_coord_x, free_category_coord_y);
addPicto(picto[0], ImgDownloader.tsource.local, new iImgDownloaderListener() {
@Override
public void loadComplete() {
listener.saved(picto[0]);
}
@Override
public void loadImg(Img image) {
}
@Override
public void error(Exception e) {
GUITools.show_alert(PCBcontext.getContext(), R.string.serverError, e.getMessage());
Log.e(this.getClass().getCanonicalName(), "Server error:"+ e.getLocalizedMessage());
}
});
} catch (Exception e) {
picto[0]=null;
e.printStackTrace();
Log.e(Vocabulary.class.getCanonicalName(), e.getMessage());
}
PCBcontext.getActionLog().log(new VocabularyAction(VocabularyAction.ADD, picto[0]));
return picto[0];
}
/**
*
* @param picto
* @return set of pictos which should be selectable in a given sentence state
*/
public LinkedList<Picto> next(Picto picto){
return picto.is_category() ? this.pictos.get(picto.get_id()) : startSentence() ;
}
@Override
public Iterator<Picto> iterator() {
return new VocabularyIterator(this.pictos);
}
@Override
public String toString() {
String vocabulary = "{";
Set<Integer> cats = this.pictos.keySet();
for (Integer cat : cats) {
vocabulary += "{\"id category\" : \"" + cat + "\", \"picto\": [";
int i = 0;
for (Picto picto : this.pictos.get(cat)) {
i++;
vocabulary += "{\"id\" : \"" + picto.get_id() + "\"" + ", \"translation\" : \"" + picto.get_translation() + "\"}";
if (i < this.pictos.size()) vocabulary += ", ";
}
vocabulary += "]";
}
vocabulary += "}";
return vocabulary;
}
}
package com.yottacode.pictogram.grammar;
import android.util.Log;
import com.yottacode.pictogram.dao.Picto;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
/**
* PCB Vocabulary manager
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class VocabularyIterator implements Iterator<Picto> {
Enumeration<Integer> keys;
Hashtable<Integer,LinkedList<Picto>> pictos;
Integer category;
int location;
VocabularyIterator(Hashtable<Integer,LinkedList<Picto>> pictos) {
this.keys = pictos.keys();
if (pictos.size()>1) {
this.pictos=pictos;
this.category = this.keys.nextElement();
}
else {
this.category=-1;
this.pictos=new Hashtable<>(1);
this.pictos.put(this.category, new LinkedList<Picto>());
}
this.location=0;
}
@Override
public boolean hasNext() {
return this.location<pictos.get(category).size() || keys.hasMoreElements();
}
@Override
public Picto next() {
Picto picto;
if ( this.location==pictos.get(category).size()) {
this.location = 0;
this.category = this.keys.nextElement();
}
picto=pictos.get(category).get(location++);
return picto;
}
@Override
public void remove() {
}
}
\ No newline at end of file
package com.yottacode.pictogram.grammar; import android.util.Log; import com.github.nkzawa.emitter.Emitter; import org.json.JSONException;import org.json.JSONObject; import com.yottacode.pictogram.dao.Picto;import com.yottacode.pictogram.action.Room; /** * Websocket Vocabulary Room based on Room * @author Fernando Martinez Santiago * @version 1.0 */public class VocabularyTalk implements Emitter.Listener { private static final String URL ="vocabulary"; private Room room; iVocabularyListener listeners[]; public VocabularyTalk(Room room, iVocabularyListener listeners[]) { this.room = room; this.room.listen(URL, this); this.listeners=listeners; } @Override public void call(Object... args) { final String param_action="action"; final String param_attributes="attributes"; final String param_picto="picto"; final String param_stu_picto="stu_picto"; final String param_picto_id="id"; final String param_picto_cat="id_cat"; final String action_update="update"; final String action_add="add"; final String action_delete="delete"; JSONObject msg = (JSONObject) args[0]; try { Log.i(this.getClass().getName(), "raw Received message " +msg.toString()); String action = msg.getString(param_action).toLowerCase(); JSONObject stu_picto= msg.getJSONObject(param_attributes).getJSONObject(param_stu_picto); JSONObject attrs_stu_picto = stu_picto.optJSONObject(param_attributes); JSONObject attrs_picto = stu_picto.optJSONObject(param_picto); int picto_id = attrs_picto.getInt(param_picto_id); int picto_cat = attrs_stu_picto!=null ? attrs_stu_picto.optInt(param_picto_cat, Picto.NO_CATEGORY) : 0; Log.i(this.getClass().getName(), "Received message '" + action + "' for picto " + picto_id + " (cat " + picto_cat + ", attrs: " + attrs_picto); for (iVocabularyListener listener: this.listeners) listener.change(action.equals(action_update) ? iVocabularyListener.action.update : action.equals(action_add) ? iVocabularyListener.action.add : iVocabularyListener.action.delete , picto_cat, picto_id, stu_picto); } catch (JSONException e) { Log.e(this.getClass().getCanonicalName(), e.getClass().getCanonicalName() + "--" + e); } }}
\ No newline at end of file
package com.yottacode.pictogram.grammar;
import com.yottacode.pictogram.dao.Picto;
/**
* Created by Fernando on 14/03/2016.
*/
public interface iLocalPicto {
public void saved(Picto localPicto);
}
package com.yottacode.pictogram.grammar;
import com.yottacode.pictogram.dao.Picto;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.Hashtable;
import java.util.LinkedList;
/**
* Vocabulary Listener
* @author Fernando Martinez Santiago
* @version 1.0
*/
public interface iVocabularyListener {
public enum action {delete,add,update}
public void change(iVocabularyListener.action action, int picto_cat, int picto_id, JSONObject args);
}
package com.yottacode.pictogram.net;
import android.app.ActivityManager;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Vector;
/**
* Image downloader from the server or local for pictograms and student's and supervisor's profile pictures.
* Permissions required in AndroidManifest.xml:
* <uses-permission android:name="android.permission.INTERNET" />
*/
public class ImgDownloader extends AsyncTask<Vector<Img>, Void, Img> {
iImgDownloaderListener imgListener;
public static enum status {downloading, downloaded_ok, downloaded_failed}
public static enum tsource{remote,local}
public status current;
private boolean force_download;
Context context;
ActivityManager.MemoryInfo mi;
ActivityManager activityManager;
tsource source;
public ImgDownloader(Context context, iImgDownloaderListener listener, tsource source) {
this.imgListener = listener;
this.context=context;
this.mi = new ActivityManager.MemoryInfo();
this.activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
this.force_download=context.getResources().getBoolean(R.bool.force_img_download);
this.source=source;
}
@Override
protected void onPreExecute() {
}
protected Img doInBackground( Vector<Img> imgs) {
InputStream is=null;
Log.d(this.getClass().getCanonicalName(), "Required images: " + imgs.size());
this.current= ImgDownloader.status.downloading;
int i=0,j=0,allsize=0;
Long availableMegs = mi.availMem / 1048576L;
int seconds = Calendar.getInstance().get(Calendar.SECOND);
try {
for (Img img: imgs) {
if (!img.exists_bitmap(this.context) || this.force_download) try {
this.activityManager.getMemoryInfo(mi);
if (this.source==source.remote) {
String surl = context.getResources().getString(R.string.server) + "/" + img.get_url();
URL url = new URL(surl);
URLConnection ucon = url.openConnection();
is = ucon.getInputStream();
}else {
File file=new File(img.get_url());
is=new FileInputStream(file);
}
int size=img.save_bitmap(this.context, is);
allsize+=size;
i++;
} catch (IOException e) {
j++;
Log.e(this.getClass().getCanonicalName(), img.get_url() + " was not downloaded: "+e);
} finally {
if (is != null) is.close();
}
}
this.current= status.downloaded_ok;
} catch (IOException e) {
Log.d(this.getClass().getCanonicalName(), "Error: " + e);
this.current= status.downloaded_failed;
}
seconds=Calendar.getInstance().get(Calendar.SECOND)-seconds;
Log.i(this.getClass().getCanonicalName(),
"Images Downloaded: " + i+"/"+imgs.size()+ " (size:"+allsize/1024+" Kbytes)"+
". Cached: "+ (imgs.size()-i)+"/"+imgs.size()+
". Download failed: "+ j+"/"+imgs.size()+
". Memory required:"+((mi.availMem / 1048576L)-availableMegs)+" MB"+
". Used time: "+seconds+" seconds at "+new SimpleDateFormat("HH:mm:ss"));
return imgs.size() > 1 ? null
: imgs.get(0);
}
@Override
protected Img doInBackground(Vector... params) {
doInBackground(params[0]);
return null;
}
@Override
protected void onPostExecute(Img img) {
if (imgListener!=null)
if(img == null) imgListener.loadComplete();
else imgListener.loadImg(img);
}
}
package com.yottacode.pictogram.net;
import android.util.Log;
import com.yottacode.net.RestapiWrapper;
import com.yottacode.net.iRestapiListener;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.dao.LoginException;
import com.yottacode.pictogram.dao.User;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Background services to be executed every "delay" seconds. Tasks to be executed:
* If offline mode (there is no server connection):
* - keep local record of actions
* If online mode:
* - reload vocabulary
* - move records into the server of actions and purge the actions pool
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class NetService implements Runnable {
static final String ping_session="server/ping";
private boolean updated;
private iNetServiceDevice device;
public NetService(int delay, iNetServiceDevice device) {
this.updated=RestapiWrapper.ping(PCBcontext.getContext().getResources().getString(R.string.server), ping_session);
this.device = device;
this.device.build();
Log.i(this.getClass().getName(), "Checking Pictogram server access...");
Log.i(this.getClass().getName(), this.updated ? "Pictogram server access ok" : "Pictogram server access failed, Internet connection available?");
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
exec.scheduleWithFixedDelay(this, 0, delay, TimeUnit.SECONDS);
}
public void login() {
User user=PCBcontext.getPcbdb().getCurrentUser();
if (user.is_supervisor())
ServerLogin.login_supervisor(user.get_email_sup(), user.get_pwd_sup(), new iRestapiListener() {
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
PCBcontext.getPcbdb().user_online(true);
}
@Override
public void result(JSONObject result) {
}
@Override
public void error(Exception e) {
Log.e(this.getClass().getSimpleName(), "Error un when server login:" + e.getMessage());
if (e instanceof LoginException) NetService.this.device.restart_app();
}
});
else ServerLogin.login_student(user.get_nickname_stu(), user.get_pwd_stu(), new iRestapiListener() {
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
PCBcontext.getPcbdb().user_online(true);
}
@Override
public void result(JSONObject result) {
}
@Override
public void error(Exception e) {
Log.e(this.getClass().getSimpleName(),"Error un when server login:"+e.getMessage());
if (e instanceof LoginException) NetService.this.device.restart_app();
}
});
}
public boolean online() {return updated;}
public void restart_app() {this.device.restart_app();}
/**
* ping to the server by using a restapi call. If ok, the call will return the empty set
*/
@Override
public void run() {
PCBcontext.getRestapiWrapper().ask(ping_session, new iRestapiListener() {
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
}
@Override
public void result(JSONObject result) {
if (!updated) {
updated = true;
if (PCBcontext.is_user_logged()) //si el usuario aun no hizo login, en realidad no es necesario hacer nada
// Comprobar si hay usuario offline, para hacer login transparente
if (PCBcontext.is_user_offline()){
login();
} else if (PCBcontext.is_user_online()){
Log.e(this.getClass().getName(), "PCB reconnect");
PCBcontext.getRoom().reconnect();
PCBcontext.getVocabulary().synchronize();
PCBcontext.getActionLog().batch();
}
}
Log.i(this.getClass().getName(), "PCB status checked. Updated? " + updated);
notifyStatus();
}
@Override
public void error(Exception e) {
setOffline(e);
}
});
}
public void notifyStatus() {
String user;
if (PCBcontext.getPcbdb()!=null) {
user=PCBcontext.getPcbdb().getCurrentUser().get_name_stu();
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor())
user += " (" + PCBcontext.getPcbdb().getCurrentUser().get_name_sup() + ")";
}
device.notifyStatus(this.updated);
}
public void closeNotifyStatus(){
device.closeNotifyStatus();
}
public void setOffline(Exception e) {
this.updated=false;
Log.e(this.getClass().getName(), "PCB offline because exception happens: " + e.getMessage());
notifyStatus();
}
}
package com.yottacode.pictogram.net;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import com.google.gson.JsonObject;
import com.koushikdutta.ion.Ion;
import com.koushikdutta.ion.Response;
import com.yottacode.net.iRestapiListener;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.action.VocabularyAction;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.tools.BitmapTools;
import com.yottacode.tools.GUITools;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.concurrent.ExecutionException;
/**
* Created by Fernando on 02/03/2016.
*/
public class PictoUploader {
Picto picto=null;
public PictoUploader(Picto picto) {
this.picto=picto;
}
private int uploadImg( Img img) throws UnsupportedEncodingException {
int img_id;
Bitmap bmp=null;
if (!img.get_filetype().equalsIgnoreCase("png"))
throw new UnsupportedEncodingException("Extension "+img.get_filetype()+" is not supported. Only png files");
Ion ion = Ion.getDefault(PCBcontext.getContext());
try {
bmp=img.get_bitmap(PCBcontext.getContext());
} catch (IOException e) {
Log.e(Img.class.getCanonicalName(), "Error when decoding "+img.file_name());
}
Log.i(this.getClass().getCanonicalName(), "Uploading Picto img " + img.file_name() + " from " + img.get_type() + "- Size:" + bmp.getWidth() + " " + bmp.getHeight());
File file = img.file(PCBcontext.getContext());
Response<JsonObject> response=null;
try {
response=ion.with(PCBcontext.getContext())
.load("POST", PCBcontext.getContext().getResources().getString(R.string.server) + "/picto/upload")
.setMultipartParameter("filename", Integer.toString(img.get_id()))
.setMultipartParameter("extension", img.get_filetype())
.setMultipartParameter("owner", Integer.toString(PCBcontext.getPcbdb().getCurrentUser().get_id_sup()))
.setMultipartParameter("folder", "custompictos")
.setMultipartParameter("source", "2")
.setMultipartParameter("token", PCBcontext.getRestapiWrapper().getToken())
.setMultipartFile("file", "image/png", file)
.asJsonObject()
.withResponse().get();
if (response != null && response.getHeaders().code() == 200) {
Log.i(this.getClass().getCanonicalName(), "Uploaded image result: " + response.getHeaders() + ":" + response.getResult());
img_id=response.getResult().get("id").getAsInt();
} else {
img_id=-1;
Log.i(this.getClass().getCanonicalName(), "Uploaded image failed ");
if (response != null)
Log.i(this.getClass().getCanonicalName(), "Uploaded image failed, headers: " + response.getHeaders());
}
} catch (InterruptedException e) {
Log.e(this.getClass().getCanonicalName(), "Image upload error: " + e.getMessage()+ "Code: "+
(response == null ? -1 : response.getHeaders().code()));
img_id=-1;
} catch (ExecutionException e) {
Log.e(this.getClass().getCanonicalName(), "Image upload error: " + e.getMessage()+
(response == null ? -1 : response.getHeaders().code()));
img_id=-1;
}
// ion.dump();
return img_id;
}
/**
* if a picto was included from the PCB, the translation is uploaded to the server
*/
private void uploadAttributes(int id_picto, final iPictoUploaderListener listener) {
Hashtable<String, String> params = new Hashtable<String, String>(4);
try {
JSONObject json_attrs =new JSONObject().put("status",picto.get_status());
if (PCBcontext.getPcbdb().getCurrentUser().has_categories())
json_attrs.put(Picto.JSON_ATTTRS.CATEGORY, picto.get_category())
.put(Picto.JSON_ATTTRS.COLUMN, picto.get_column())
.put(Picto.JSON_ATTTRS.ROW, picto.get_row());
else
json_attrs.put(Picto.JSON_ATTTRS.CATEGORY, picto.NO_CATEGORY)
.put(Picto.JSON_ATTTRS.FREE_COLUMN, picto.getFreeColumn())
.put(Picto.JSON_ATTTRS.FREE_ROW, picto.getFreeRow());
params.put("json", new JSONObject().put("attributes",json_attrs).toString());
} catch (JSONException e) {
e.printStackTrace();
Log.e(this.getClass().getCanonicalName(), " Error: " + e.getLocalizedMessage());
}
PCBcontext.getRestapiWrapper().ask(PCBcontext.getPcbdb().getCurrentUser().get_restapi_operation_stu() + "/picto/"+id_picto, params, "post", true, new iRestapiListener() {
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
}
@Override
public void result(JSONObject result) {
Log.i(this.getClass().getCanonicalName(), " Attributes uploaded: " + result.toString());
listener.success(true);
}
@Override
public void error(Exception e) {
Log.e(this.getClass().getCanonicalName(), " Error uploading attributes: " + e.getLocalizedMessage());
listener.success(false);
}
});
}
/**
* if the a picto was included from the PCB, the translation is uploaded to the server
*/
private void uploadTranslation( int id_picto, final iPictoUploaderListener listener) {
final Hashtable<String, String> params = new Hashtable<String, String>(3);
params.put("picto", Integer.toString(id_picto));
params.put("lang", PCBcontext.getPcbdb().getCurrentUser().get_lang_stu());
params.put("text", picto.get_translation());
PCBcontext.getRestapiWrapper().ask("picto/exp", params, "post", new iRestapiListener() {
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
}
@Override
public void result(JSONObject result) {
Log.i(this.getClass().getCanonicalName(), "Uploaded translation result: " + result.toString());
listener.success(true);
}
@Override
public void error(Exception e) {
Log.e(this.getClass().getCanonicalName(), "Error uploading translation: " + e.getLocalizedMessage()+" Picto:"+params.get("picto")+" Lang:"+params.get("lang")+" Text:"+params.get("text"));
listener.success(false);
}
});
}
/**
*Try to Upload local picto. It requires:
* i) to upload the image,
* ii) to upload the attributes
* iii) to upload the expression
*
**/
public void upload(final Context context) throws IOException {
final int local_img_id = this.picto.get_id();
final int img_id = uploadImg(this.picto);
iPictoUploaderListener listener = new iPictoUploaderListener() {
int elements_uploaded = 0;
@Override
public void success(boolean success) {
if (success) elements_uploaded++;
else
GUITools.show_alert(context, R.string.upload_error,PictoUploader.this.picto.get_translation());
if (elements_uploaded == 2) {
PCBcontext.getPcbdb().deletePicto(local_img_id);
PictoUploader.this.picto.delete_bitmap(PCBcontext.getContext());
PictoUploader.this.picto.update_id(img_id);
PCBcontext.getRoom().emit(new VocabularyAction(VocabularyAction.ADD, PictoUploader.this.picto));
GUITools.show_alert(context, R.string.upload_ok,PictoUploader.this.picto.get_translation()+":"+PictoUploader.this.picto.get_id());
}
}
};
if (img_id > 0) {
uploadAttributes(img_id, listener);
uploadTranslation(img_id, listener);
}
else {
GUITools.show_alert(context, R.string.upload_error, PictoUploader.this.picto.get_translation());
}
}
/**
* if the status of a given picto was modifed from the PCB it is uploaded to the server
*
*/
public void uploadState( ){
Hashtable<String, String> params = new Hashtable<String, String>(1);
params.put("attributes", picto.get_json_server_attrs());
params.put("id_stu", Integer.toString(PCBcontext.getPcbdb().getCurrentUser().get_id_stu()));
params.put("id_pic", Integer.toString(this.picto.get_id()));
Log.i(this.getClass().getCanonicalName(), "Picto Uploading " + params.toString());
PCBcontext.getRestapiWrapper().ask(
PCBcontext.getPcbdb().getCurrentUser().get_restapi_operation_stu() + "/picto",
params, "put", new iRestapiListener() {
@Override
public void preExecute() {
}
@Override
public void result(JSONArray result) {
}
@Override
public void result(JSONObject result) {
Log.i(PictoUploader.this.getClass().getCanonicalName(), "Upload ok Picto " + PictoUploader.this.picto.get_id());
picto.set_local_status(false);
PCBcontext.getRoom().emit(new VocabularyAction(VocabularyAction.ALTERATTRS,PictoUploader.this.picto));
}
@Override
public void error(Exception e) {
Log.e(this.getClass().getCanonicalName(), "Picto Error: " + e.getLocalizedMessage());
}
}
);
}
}
package com.yottacode.pictogram.net;
import android.util.Log;
import com.yottacode.net.RestapiWrapper;
import com.yottacode.net.iRestapiListener;
import com.yottacode.pictogram.dao.LoginException;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Hashtable;
/**
* Created by Fernando on 01/04/2016.
*/
public class ServerLogin {
public static void login_student(String username, String password, iRestapiListener listener) {
login("stu/login", null, username, password, listener);
}
public static void login_supervisor(String email, String password, iRestapiListener listener) {
login("sup/login", email, null, password, listener);
}
private static void login(String operation, String email, String username, String password, final iRestapiListener listener){
Hashtable<String, String> postDataParams = new Hashtable<String, String>();
if (email!=null) postDataParams.put("email", email);
if (username!=null) postDataParams.put("username", username);
postDataParams.put("password", password);
PCBcontext.getRestapiWrapper().ask(operation, postDataParams, "post", new iRestapiListener() {
@Override
public void preExecute() {
listener.preExecute();
}
@Override
public void result(JSONArray result) {
listener.result(result);
}
@Override
public void result(JSONObject result) {
try {
if (PCBcontext.is_user_offline()) {
final String TAG_TOKEN="token";
PCBcontext.getPcbdb().user_online(true);
PCBcontext.getRestapiWrapper().setToken(result.getString(TAG_TOKEN));
PCBcontext.getVocabulary().synchronize();
}
listener.result(result);
} catch (JSONException e) {
listener.error(e);
}
}
@Override
public void error(Exception e) {
listener.error(new LoginException(e.getMessage(),e.getMessage().contains("User without students")
? LoginException.NO_STUDENTS
: e.getMessage().contains("failed to connect")
? LoginException.TIME_OUT
: LoginException.BAD_LOGIN));
}
});
}
}
package com.yottacode.pictogram.net;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.Img;
import java.util.LinkedList;
/**
* Created by emblanco on 24/09/15.
* MOdified by dofer on 27/06/16.
*/
public interface iImgDownloaderListener {
public void loadComplete(); // for loading the vocabulary
public void loadImg(Img image); // for loading one image
public void error(Exception err); //error happens
}
package com.yottacode.pictogram.net;
/**
* Created by Fernando on 28/07/2016.
*/
public interface iPictoUploaderListener {
void success(boolean success);
}
package com.yottacode.pictogram.tools;
/**
* Created by miguelangelgarciacumbreras on 25/7/15.
*/
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
/**
* Image with support for filtering.
*/
public class FilteredImage {
private Bitmap image, mutableBitmap;
private int width;
private int height;
private Bitmap.Config config;
private int[] colorArray;
/**
* Constructor.
*
* @param img the original image
*/
public FilteredImage(Bitmap img) {
this.image = img;
width = img.getWidth();
height = img.getHeight();
config = img.getConfig();
colorArray = new int[width * height];
image.getPixels(colorArray, 0, width, 0, 0, width, height);
mutableBitmap = image.copy(Bitmap.Config.ARGB_8888, true);
applyHighlightFilter();
//addBorder(5, Color.RED);
}
/**
* Get the color for a specified pixel.
*
* @param x x
* @param y y
* @return color
*/
public int getPixelColor(int x, int y) {
return colorArray[y * width + x];
}
/**
* Gets the image.
*
* @return Returns the image.
*/
public Bitmap getImage() {
return mutableBitmap;
}
/**
* Applies highlight filter to the image.
*/
private void applyHighlightFilter() {
int a;
int r;
int g;
int b;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int c = getPixelColor(x, y);
a = Color.alpha(c);
r = Color.red(c);
g = Color.green(c);
b = Color.blue(c);
r = (int) (r * 0.4);
g = (int) (g * 0.4);
b = (int) (b * 1.6);
if (r > 255) {
r = 255;
}
if (r < 0) {
r = 0;
}
if (g > 255) {
g = 255;
}
if (g < 0) {
g = 0;
}
if (b > 255) {
b = 255;
}
if (b < 0) {
b = 0;
}
int resultColor = Color.argb(a, r, g, b);
//Log.e("applyHighlightFilter", "x:" + x + "/y:" + y + "/resultColor:" + resultColor);
mutableBitmap.setPixel(x, y, resultColor);
}
}
}
private void addBorder(int borderSize, int color) {
mutableBitmap = Bitmap.createBitmap(this.width + borderSize * 2, this.height + borderSize * 2, this.config);
Canvas canvas = new Canvas(mutableBitmap);
canvas.drawColor(color);
canvas.drawBitmap(image, borderSize, borderSize, null);
}
}
\ No newline at end of file
package com.yottacode.pictogram.tools;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import com.yottacode.tools.FileTools;
import com.yottacode.tools.BitmapTools;
/**
* Img
* @author Fernando
*/
public class Img {
static public String VOCABULARY="vocabulary";
static public String STUDENT="student";
static public String SUPERVISOR="supervisor";
static public float MAX_WIDTH=100;
public static final void mkDirs(Context context) {
File file;
file = new File(path(context, Img.VOCABULARY));
FileTools.deleteDirectory(file);
file.mkdirs();
file = new File(path(context, Img.STUDENT));
FileTools.deleteDirectory(file);
file.mkdirs();
file = new File(path(context, Img.SUPERVISOR));
FileTools.deleteDirectory(file);
file.mkdirs();
}
protected int id;
protected String url;
String type;
final static String FILETYPE="png";
Bitmap bitmap;
/**
*
* @param id
* @param url
* @param type is Vocabulary, Student or Supervisor
*/
public Img(int id, String url, String type) {
this.id = id;
this.url = url;
this.type=type;
this.bitmap=null;
}
public String file_name() { return this.id+"."+Img.FILETYPE; }
public int get_id() { return this.id;}
public String get_url() { return this.url;}
public void set_url(String url) {this.url=url;}
public String get_type() { return this.type;}
public String get_filetype() { return Img.FILETYPE;}
/**
* Load if necessary the bitmap from disk, and it is returned. IIf it is not available, return null
* @param context
* @return the bitmap
* @throws FileNotFoundException
*/
public Bitmap get_bitmap(Context context) throws IOException {
if (this.bitmap==null) {
File file = file(context);
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
this.bitmap = BitmapFactory.decodeStream(is);
is.close();
}
}
return this.bitmap;
}
/**
* Delete the bitmap from disk
* @param context
*/
public void delete_bitmap(Context context) {
File file = file(context);
file.delete();
}
/**
*
* @param context
* @return true if the bitmap is locally stored
*/
public boolean exists_bitmap(Context context) {
return file(context).exists();
}
public static String path(Context context, String type) {
String path = context.getFilesDir()+File.separator +"pictures" + File.separatorChar + type + File.separatorChar;
return path;
}
/**
* The local file where the img is saved. It depends on the image type
* @param context
* @return
*/
public File file(Context context) {
String path=path(context, this.type);
return new File(path, file_name());
}
/**
* Save an image in disk
* @param context
* @param is the stream where the image is available
* @throws IOException
*/
public int save_bitmap(Context context, InputStream is) throws IOException {
File file = file(context);
FileOutputStream os = new FileOutputStream(file);
try {
this.bitmap=BitmapFactory.decodeStream(is);
if (this.bitmap.getWidth()>MAX_WIDTH) {
this.bitmap = new BitmapTools(this.bitmap)
.rescale(MAX_WIDTH / (float) this.bitmap.getWidth())
.get();
}
}catch(java.lang.OutOfMemoryError err) {
Log.e(Img.class.getCanonicalName(), "Out of memory when decoding "+this.get_url());
}
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
this.bitmap.setHasAlpha(true);
this.bitmap.compress(Bitmap.CompressFormat.PNG, 50, outstream);
byte[] byteArray = outstream.toByteArray();
os.write(byteArray);
outstream.close();
os.close();
System.gc();
return byteArray.length;
}
/**
*
* @overide
* Two img are equals whenever they have the same id and type
*/
public boolean equals(Object o) {
return ((Img)o).id==this.id && ((Img)o).type.equals(this.type);
}
/**
* @overide
*/
public String toString() {return this.type+"."+this.id;}
}
package com.yottacode.pictogram.tools;
import android.content.Context;
import android.util.Log;
import com.yottacode.net.RestapiWrapper;
import com.yottacode.net.SSLDummyContext;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.action.ActionLog;
import com.yottacode.pictogram.dao.Device;
import com.yottacode.pictogram.dao.PCBDBHelper;
import com.yottacode.pictogram.dao.User;
import com.yottacode.pictogram.grammar.Vocabulary;
import com.yottacode.pictogram.net.iNetServiceDevice;
import com.yottacode.pictogram.action.Room;
import com.yottacode.pictogram.net.NetService;
import com.yottacode.pictogram.net.iImgDownloaderListener;
public final class PCBcontext {
private static Context context;
private static PCBDBHelper pcbdb;
private static Device device=null;
private static NetService service;
private static Room room;
private static RestapiWrapper wrapper;
private static Vocabulary vocabulary;
private static ActionLog actionLog;
private static boolean init=false;
/**
* Init method for passing params to the singleton
* @param c @TODO application or activity context?
*/
public static void init(Context c, iNetServiceDevice listener){
if (!init) {
init = true;
context = c;
device = new Device(c, null, 1);
SSLDummyContext.init(context.getResources().getBoolean(R.bool.ssl_connect));
wrapper = new RestapiWrapper(context.getResources().getString(R.string.server), null);
service = new NetService(context.getResources().getInteger(R.integer.netservice_timing),listener);
device.deleteDeprecatedImgs();
Log.i(PCBcontext.class.getCanonicalName(), "PCB context started. It's required" +
"set_user method call");
} else {
Log.e(PCBcontext.class.getClass().getCanonicalName(), "Init method was previously" +
"invoked! Please, check your code");
}
}
/**
* @param student
* @param listener
*/
public static void set_user(User student, String token, iImgDownloaderListener listener) {
if (!init) {
throw new java.lang.AssertionError("init must be called once previously ");
}
Log.i(PCBcontext.class.getCanonicalName(), "User set at student " + student.get_name_stu());
wrapper.setToken(token);
pcbdb = new PCBDBHelper(null, 1, student);
pcbdb.user_online(token!=null);
room = new Room();
actionLog = new ActionLog();
vocabulary = new Vocabulary(listener);
getNetService().notifyStatus();
}
/**
*
* @return true if a given user has been logged into the system (the login window was successfully filled)
*/
public static boolean is_user_logged() {
return (init && pcbdb!=null);
}
/**
* @return true if the user is logged offline and it has not been online previously. False in
* other case (user not logged, user logged online, user offline but it was previously online
* logged
*/
public static boolean is_user_offline() {
if (pcbdb == null) {
return false;
} else {
return !getPcbdb().isUser_online();
}
}
public static boolean is_user_online() {
return pcbdb != null && getPcbdb().isUser_online();
}
/**
* @return context
* @TODO complete documentation
*/
public static Context getContext() {
if (context == null) {
throw new java.lang.NullPointerException("Context is null. PCBcontext.init must be" +
"invoked previously");
}
return context;
}
/**
* @return device
* @TODO complete documentation
*/
public static Device getDevice() {
if (device == null) {
throw new java.lang.NullPointerException("Device is null. PCBcontext.init must be" +
"invoked previously");
}
return device;
}
/**
* @return PCBDB
* @TODO complete documentation
*/
public static PCBDBHelper getPcbdb() {
if (context == null) {
throw new java.lang.NullPointerException("PCBDB is null. PCBcontext.set_user must be" +
"invoked previously");
}
return pcbdb;
}
/**
* @return NetService
* @TODO complete documentation
*/
public static NetService getNetService(){
if (service == null) {
throw new java.lang.NullPointerException("NetService is null. PCBcontext.set_user" +
"must be invoked previously");
}
return service;
}
/**
* @return RestapiWrapper
* @TODO complete documentation
*/
public static RestapiWrapper getRestapiWrapper(){
if (wrapper == null) {
throw new java.lang.NullPointerException("RestapiWrapper is null. PCBcontext.init" +
"must be invoked previously");
}
return wrapper;
}
/**
* @return Room
* @TODO complete documentation
*/
public static Room getRoom(){
if (room == null) {
throw new java.lang.NullPointerException("Room is null. PCBcontext.set_user must" +
"be invoked previously");
}
return room;
}
/**
* @return Vocabulary
* @TODO complete documentation
*/
public static Vocabulary getVocabulary(){
if (vocabulary == null) {
throw new java.lang.NullPointerException("Vocabulary is null. PCBcontext.set_user" +
"must be invoked previously");
}
return vocabulary;
}
/**
* @return ActionLog
* @TODO complete documentation
*/
public static ActionLog getActionLog(){
if (actionLog == null) {
throw new java.lang.NullPointerException("ActionLog is null. PCBcontext.set_user" +
"must be invoked previously");
}
return actionLog;
}
}
package com.yottacode.pictogram.tts;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import android.annotation.TargetApi;
import android.os.Build;
import android.speech.tts.TextToSpeech;
import android.content.Context;
import android.speech.tts.Voice;
import android.util.Log;
import android.widget.ArrayAdapter;
/**
* PCB TTS service
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class TTSHelper {
TextToSpeech ttobj;
public boolean engine_ok;
public boolean voice_ok=true;
Voice voice;
public void createTTS(Context context, String engine) {
this.destroy();
/* this.ttobj = new TextToSpeech(
context,
new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status != TextToSpeech.ERROR) {
ttobj.setLanguage(Locale.getDefault());
}
}
}, engine);*/
this.engine_ok=true;
Log.e(context.getApplicationInfo().processName,"TTS engine "+engine+" loaded");
}
public TTSHelper(Context context) {
createTTS(context ,null);
}
public TTSHelper(Context context, String engine) {
try {
createTTS(context ,engine);
}catch (Exception e) {
createTTS(context ,null);
Log.e(context.getApplicationInfo().processName, "Engine "+engine+" error",e);
this.engine_ok=false;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void setVoice(Context context, String voice) {
try {
Set<Voice> voices = this.ttobj.getVoices();
if (voices!=null)
for (Voice avoice : voices) {
Log.e(context.getApplicationInfo().processName,"Voice name "+avoice.getName());
if (avoice.getName().compareTo(voice) == 0) {
this.ttobj.setVoice(avoice);
this.voice_ok=true;
Log.e(context.getApplicationInfo().processName,"Voice "+voice+" loaded");
break;
}
}
else
Log.e(context.getApplicationInfo().processName,"Voice "+voice+" is not available");
}catch(Exception e) {
Log.e(context.getApplicationInfo().processName,"Voice "+voice+" error",e);
this.voice_ok=false;
}
}
public boolean isVoice_ok() { return this.voice_ok;}
public boolean isEngine_ok() { return this.engine_ok;}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void speakText(String toSpeak){
this.ttobj.speak(toSpeak, TextToSpeech.QUEUE_FLUSH,null, toSpeak.hashCode()+"");
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void show(ArrayAdapter mArrayAdapter) {
String tts_info[] = new String[25];
int i=0;
List<TextToSpeech.EngineInfo> engines = this.ttobj.getEngines();
for (Iterator<TextToSpeech.EngineInfo> iter = engines.iterator(); iter.hasNext(); ) {
TextToSpeech.EngineInfo element = iter.next();
Log.i("TTSHelper","TTS: "+element.name + " label:"+element.label);
mArrayAdapter.add("TTS: "+element.name + " label:"+element.label);
}
if (this.ttobj.getVoice()!=null) {
Log.i("TTSHelper", "Default voice:" + this.ttobj.getDefaultVoice().getName());
mArrayAdapter.add("Current voice:" + this.ttobj.getVoice().getName());
Set<Voice> voices = this.ttobj.getVoices();
if (voices != null)
for (Voice voice : voices) {
Log.i("TTSHelper", "Voice: " + voice.getName() + " locale:" + voice.getLocale());
mArrayAdapter.add("Voice: " + voice.getName() + " locale:" + voice.getLocale());
}
}
}
public void destroy() {
if(ttobj !=null){
ttobj.stop();
ttobj.shutdown();
}
this.ttobj=null;
}
}
\ No newline at end of file
package com.yottacode.tools;
import android.graphics.Bitmap;
import android.graphics.Matrix;
/**
* Created by Fernando on 15/03/2016.
*/
public class BitmapTools {
Bitmap bitmap;
public BitmapTools(Bitmap bitmap) { this.bitmap=bitmap;}
public Bitmap get() {return this.bitmap;}
public BitmapTools resize(int w, int h) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int newWidth = w;
int newHeight = h;
// calculamos el escalado de la imagen destino
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// para poder manipular la imagen
// debemos crear una matriz
Matrix matrix = new Matrix();
// resize the Bitmap
matrix.postScale(scaleWidth, scaleHeight);
// volvemos a crear la imagen con los nuevos valores
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
width, height, matrix, true);
this.bitmap=resizedBitmap;
return this;
}
public BitmapTools rescale(float scale) {
int width = (int) (scale * (float) this.bitmap.getWidth());
int height = (int) (scale * (float) this.bitmap.getHeight());
return resize(width,height);
}
public BitmapTools paintSquare(int thickness, int color ) {
for (int i = 0; i < this.bitmap.getWidth(); i++)
for (int t = 1; t <= thickness; t++) {
this.bitmap.setPixel(i, 0 + t-1, color);
this.bitmap.setPixel(i, this.bitmap.getHeight() - t, color);
}
for (int i = 0; i < this.bitmap.getHeight(); i++)
for (int t = 1; t <= thickness; t++) {
this.bitmap.setPixel(0 + t-1, i, color);
this.bitmap.setPixel(this.bitmap.getWidth() - t, i, color);
}
return this;
}
}
package com.yottacode.tools;
import java.io.File;
/**
* Created by Fernando on 14/03/2016.
*/
public class FileTools {
public static boolean deleteDirectory(File path) {
if( path.exists() ) {
File[] files = path.listFiles();
if (files == null) {
return true;
}
for(int i=0; i<files.length; i++) {
if(files[i].isDirectory()) {
deleteDirectory(files[i]);
}
else {
files[i].delete();
}
}
}
return( path.delete() );
}
}
package com.yottacode.tools;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
/**
* Created by Fernando on 01/04/2016.
*/
public class GUITools {
public interface iOKListener {
public void ok();
}
public static void show_alert(Context context, int resource_msg, String additional_msg, final iOKListener oklistener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
String msg = context.getString(resource_msg);
if (additional_msg != null) msg += ": " + additional_msg;
Log.i(GUITools.class.getName(), "Alert:" + msg);
builder.setMessage(msg)
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (oklistener != null) oklistener.ok();
}
});
AlertDialog alert = builder.create();
alert.show();
}
public static void show_alert(Context context, int resource_msg) {
show_alert(context, resource_msg, null);
}
public static void show_alert(Context context, int resource_msg, String additional_msg) {
show_alert(context, resource_msg, additional_msg,null);
}
}
package com.yottacode.pictogram.tablet.gui;
import android.app.Activity;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.yottacode.pictogram.tablet.R;
import java.util.Vector;
/**
* Creates a View for each student on the list with a photo and his/her name.
* It uses list_single.xml for the layout creation.
*/
public class CustomList extends ArrayAdapter<String>{
private final Activity context;
private final String[] name_surname;
private final Vector<Bitmap> images;
/**
* @param context Context used for rendering the view
* @param name_surname List of students' names
* @param images List of students' photos
*/
public CustomList(Activity context,
String[] name_surname, Vector<Bitmap> images) {
super(context, R.layout.list_single, name_surname);
this.context = context;
this.name_surname = name_surname;
this.images = images;
}
/**
* @param position Student position in the name_surname/images arrays
* @param view @TODO not being used
* @param parent @TODO not being used
* @return The rendered student view
*/
@Override
public View getView(int position, View view, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View rowView= inflater.inflate(R.layout.list_single, null, true);
TextView txtTitle = (TextView) rowView.findViewById(R.id.loginStudentName);
ImageView imageView = (ImageView) rowView.findViewById(R.id.loginStudentPhoto);
txtTitle.setText(name_surname[position]);
//imageView.setImageResource(R.drawable.user);
imageView.setImageBitmap(images.elementAt(position));
return rowView;
}
}
package com.yottacode.pictogram.tablet.gui;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.yottacode.pictogram.tablet.R;
import com.yottacode.pictogram.net.ImgDownloader;
import com.yottacode.pictogram.net.iImgDownloaderListener;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.tools.GUITools;
import java.io.IOException;
import java.util.Vector;
/**
* LoginActivity show the login window to select the student and supervisor
* It uses device to read the token value
*/
public class LoginActivity extends FragmentActivity {
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
/**
* If there is Internet connection it calls the WS to recover the pairs student-supervisor
* @param savedInstanceState
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_login);
// Enable logout button
final Button logoutButton = (Button) findViewById(R.id.loginTopbarLogout);
logoutButton.setEnabled(true);
logoutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent serialActivity = new Intent(getBaseContext(), SerialActivity.class);
serialActivity.putExtra("resetPrevUser", true);
startActivity(serialActivity);
}
});
// Set supervisor information on topbar
final TextView supervisorFullNameView = (TextView) findViewById(R.id.loginTopbarSupervisorFullName);
final TextView supervisorUserNameView = (TextView) findViewById(R.id.loginTopbarSupervisorUserName);
final ImageView supervisorPhotoView = (ImageView) findViewById(R.id.loginTopbarSupervisorPhoto);
supervisorFullNameView.setText(
this.getIntent().getStringExtra("name") + " " +
this.getIntent().getStringExtra("surname"));
supervisorUserNameView.setText(this.getIntent().getStringExtra("email"));
final Vector<Img> imgs = new Vector<>(1);
imgs.add(new Img(
this.getIntent().getIntExtra("sup_id", -1),
this.getIntent().getStringExtra("pic"),
Img.SUPERVISOR
));
ImgDownloader downloader = new ImgDownloader(this, new iImgDownloaderListener() {
@Override
public void loadComplete() {
try {
supervisorPhotoView.setImageBitmap(
imgs.get(0).get_bitmap(PCBcontext.getContext())
);
supervisorPhotoView.invalidate();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void loadImg(Img image) {}
@Override
public void error(Exception e) {
GUITools.show_alert(PCBcontext.getContext(), R.string.serverError, e.getMessage());
Log.e(this.getClass().getCanonicalName(), "Server error:"+ e.getLocalizedMessage());
}
}, ImgDownloader.tsource.remote);
downloader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imgs);
}
@Override
protected void onResume() {
super.onResume();
}
}
package com.yottacode.pictogram.tablet.gui;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
import com.yottacode.pictogram.tablet.R;
import com.yottacode.pictogram.tablet.net.NetServiceTablet;
import com.yottacode.pictogram.tools.PCBcontext;
public class MainActivity extends Activity {
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// For deactivating the lock screen (just before setContentView)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
PCBcontext.init(this, new NetServiceTablet());
Intent serialActivity = new Intent(this, SerialActivity.class);
serialActivity.putExtra("resetPrevUser", false);
startActivity(serialActivity);
}
@Override
protected void onDestroy() {
super.onDestroy();
PCBcontext.getNetService().closeNotifyStatus();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
package com.yottacode.pictogram.tablet.gui;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import java.util.LinkedList;
public class PictoGridAdapter extends ArrayAdapter {
private LinkedList<Picto> pictoLinkedList;
private final String LOG_TAG = this.getClass().getSimpleName();
public PictoGridAdapter(LinkedList<Picto> pictoLinkedList){
super(PCBcontext.getContext(), PictoItemViewGenerator.LAYOUT, pictoLinkedList);
this.pictoLinkedList = pictoLinkedList;
}
@Override
public int getCount() {
return this.pictoLinkedList.size();
}
@Override
public Picto getItem(int position) {
return this.pictoLinkedList.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
public void deleteAll() {
this.pictoLinkedList.clear();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return PictoItemViewGenerator.getPictoView(
this.pictoLinkedList.get(position),
convertView,
parent
);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void ttsPicto(Picto p, TextToSpeech tts) {
if (p.is_enabled()) {
String input = p.get_translation();
Bundle params = new Bundle();
params.putString(TextToSpeech.Engine.KEY_PARAM_VOLUME, "1");
tts.speak(input, TextToSpeech.QUEUE_FLUSH, params, null);
}
}
}
package com.yottacode.pictogram.tablet.gui;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.yottacode.pictogram.tablet.R;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import java.io.IOException;
/**
* This class is used for generating PictoViews which will be inserted inside a picto grid
* or a picto tape.
*/
public class PictoItemViewGenerator {
public static final int LAYOUT = R.layout.picto_grid_item;
public static View getPictoView(Picto picto, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(LAYOUT, parent, false);
}
RelativeLayout layoutWrapper = (RelativeLayout) convertView.findViewById(R.id.picto_grid_item_layout_wrapper);
FrameLayout layout = (FrameLayout) convertView.findViewById(R.id.picto_grid_item_layout);
ImageView pictoImage = (ImageView) convertView.findViewById(R.id.picto_grid_item_image);
ImageView redCrossImage = (ImageView) convertView.findViewById(R.id.picto_grid_item_redcross);
layoutWrapper.setVisibility(View.GONE);
layoutWrapper.setBackground(null);
layoutWrapper.setAlpha(0.25f);
layout.setBackgroundColor(convertView.getResources()
.getColor(R.color.picto_default_background));
redCrossImage.setVisibility(View.GONE);
pictoImage.setScaleX(1.0f);
pictoImage.setScaleY(1.0f);
pictoImage.setVisibility(View.GONE);
if (picto == null) {
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) {
layoutWrapper.setVisibility(View.VISIBLE);
layoutWrapper.setBackground(convertView.getResources()
.getDrawable(R.drawable.picto_grid_item_border));
}
} else {
if (!picto.is_invisible() && !picto.is_disabled()) {
layoutWrapper.setAlpha(1.00f);
}
try {
pictoImage.setImageBitmap(picto.get_bitmap(PCBcontext.getContext()));
if (!picto.is_invisible() || PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) {
layoutWrapper.setVisibility(View.VISIBLE);
pictoImage.setVisibility(View.VISIBLE);
layoutWrapper.setBackground(convertView.getResources()
.getDrawable(R.drawable.picto_grid_item_border));
if (picto.is_magnify()) {
pictoImage.setScaleX(1.2f);
pictoImage.setScaleY(1.2f);
}
if (picto.is_disabled()) {
redCrossImage.setVisibility(View.VISIBLE);
}
if (picto.is_category()) {
layout.setBackgroundColor(picto.get_color());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
return convertView;
}
}
package com.yottacode.pictogram.tablet.gui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.yottacode.pictogram.tablet.R;
import com.yottacode.pictogram.action.PictosAction;
import com.yottacode.pictogram.action.TalkAction;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.grammar.Vocabulary;
import com.yottacode.pictogram.grammar.iLocalPicto;
import com.yottacode.pictogram.grammar.iVocabularyListener;
import com.yottacode.pictogram.net.PictoUploader;
import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
public class PictogramActivity extends Activity implements iVocabularyListener, TextToSpeech.OnInitListener {
// Main layout for this activity
RelativeLayout mainLayout;
// Adapter for de grid showing the categories grid (and main pictos)
PictoGridAdapter pictoMainGridAdapter;
// Grid showing the categories grid (and main pictos)
GridView pictoMainGridView;
// Adapter for the grid showing pictos from a category (initially hidden)
PictoGridAdapter pictoCategoryGridAdapter;
// Grid showing pictos from a category (initially hidden)
GridView pictoCategoryGridView;
// Adapter for the tape view (list of pictos to send to the server)
TapeAdapter tapeAdapter;
// Tape view (list of pictos to send to the server)
GridView tapeGridView;
// Current picto category, if not null the corresponding category grid will be shown
Picto currentCategory;
// Object used for reading text
TextToSpeech tts;
// Element used for loading new pictos (while changing categories)
Vocabulary vocabulary;
// TODO describe this variable
static final int SELECT_PICTURE = 1;
// For disabling volume button (See method at the end of the class)
private final List blockedKeys = new ArrayList(Arrays.asList(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP));
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
// Count for delete long press (hidden exit)
private int count_deletelong = 0;
// Animation for hidding the picto category view
Animation hidePictoMainViewAnimation;
// Animation for showing the picto category view
Animation showPictoMainViewAnimation;
// Button used for showing the picto category view
ImageButton showPictoCategoriesViewButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_pictogram);
this.mainLayout = (RelativeLayout) findViewById(R.id.pictogramLayout);
this.currentCategory = null;
this.count_deletelong = 0;
this.vocabulary = PCBcontext.getVocabulary();
this.vocabulary.listen(PCBcontext.getRoom(), this);
if (this.vocabulary != null && this.vocabulary.size() > 0) {
Log.d(LOG_TAG, "Vocabulario correcto de tam:" + vocabulary.size());
}
this.tapeAdapter = new TapeAdapter();
this.tapeGridView = (GridView) this.findViewById(R.id.tape_grid_view);
this.tapeGridView.setAdapter(this.tapeAdapter);
this.pictoMainGridAdapter = new PictoGridAdapter(new LinkedList<Picto>());
this.pictoMainGridView = (GridView) this.findViewById(R.id.picto_main_grid_view);
this.pictoMainGridView.setAdapter(this.pictoMainGridAdapter);
this.pictoCategoryGridAdapter = new PictoGridAdapter(new LinkedList<Picto>());
this.pictoCategoryGridView = (GridView) this.findViewById(R.id.picto_category_grid_view);
this.pictoCategoryGridView.setAdapter(this.pictoCategoryGridAdapter);
// @TODO take this value from user configuration (0: normal, 1: large)
int pictogramSize = 0;
if (pictogramSize == 0) {
this.pictoMainGridView.setNumColumns(10);
this.pictoCategoryGridView.setNumColumns(10);
} else if (pictogramSize == 1) {
this.pictoMainGridView.setNumColumns(4);
this.pictoCategoryGridView.setNumColumns(4);
}
// tts = new TextToSpeech(this, this, "IVONA Text-to-Speech HQ");
tts = new TextToSpeech(this, this);
tts.setOnUtteranceProgressListener(new OnTTSEndListener());
this.tapeGridView.setOnDragListener(new OnPictoDragListener());
this.pictoMainGridView.setOnDragListener(new OnPictoDragListener());
this.pictoCategoryGridView.setOnDragListener(new OnPictoDragListener());
this.pictoMainGridView.setOnItemClickListener(new OnPictoClickListener());
this.pictoCategoryGridView.setOnItemClickListener(new OnPictoClickListener());
this.pictoMainGridView.setOnItemLongClickListener(new OnPictoLongClickListener());
this.pictoCategoryGridView.setOnItemLongClickListener(new OnPictoLongClickListener());
final ImageButton deleteButton = (ImageButton) findViewById(R.id.button_delete);
final ImageButton ttsButton = (ImageButton) findViewById(R.id.button_tts);
ttsButton.setOnClickListener(new OnTTSButtonClickListener());
deleteButton.setOnClickListener(new OnDeleteButtonClickListener());
deleteButton.setOnLongClickListener(new OnDeleteButtonLongClickListener());
this.showPictoCategoriesViewButton = (ImageButton) this.findViewById(R.id.showPictoCategoriesViewButton);
this.showPictoCategoriesViewButton.setOnClickListener(new OnShowPictoCategoriesViewButtonClick());
this.generateAnimations();
if (this.currentCategory != null) {
this.hidePictoMainGridView();
} else {
this.showPictoMainGridView();
}
}
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.getDefault());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (tts != null){
tts.shutdown();
}
}
/**
* Creates the animations for moving the picto view
*/
private void generateAnimations() {
DisplayMetrics displayMetrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int animationDuration = 400;
int animationWidth = displayMetrics.widthPixels;
this.hidePictoMainViewAnimation = new TranslateAnimation(0, -animationWidth, 0, 0);
this.hidePictoMainViewAnimation.setDuration(animationDuration);
this.hidePictoMainViewAnimation.setFillAfter(true);
this.hidePictoMainViewAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
this.hidePictoMainViewAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
pictoMainGridView.setTranslationZ(-1000.0f);
pictoMainGridView.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {}
});
this.showPictoMainViewAnimation = new TranslateAnimation(-animationWidth, 0, 0, 0);
this.showPictoMainViewAnimation.setDuration(animationDuration);
this.showPictoMainViewAnimation.setFillAfter(true);
this.showPictoMainViewAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
this.showPictoMainViewAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
pictoMainGridView.setTranslationZ(1000.0f);
pictoMainGridView.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
});
}
/**
* Show the main category grid view and hide the pictoGrid, unloading all pictos
* if necesary.
*/
private void showPictoMainGridView() {
Log.d(LOG_TAG, "Showing pictoMainGridView");
this.pictoMainGridAdapter.clear();
this.pictoMainGridAdapter.addAll(this.sort(this.vocabulary.startSentence()));
this.pictoMainGridAdapter.notifyDataSetChanged();
this.pictoMainGridView.setEnabled(true);
this.pictoCategoryGridView.setEnabled(false);
if (this.pictoMainGridView.getAnimation() == this.hidePictoMainViewAnimation) {
this.pictoMainGridView.startAnimation(this.showPictoMainViewAnimation);
}
this.currentCategory = null;
}
/**
* Hides the main category grid view and show a concrete category (this.currentCategory)
*/
private void hidePictoMainGridView() {
Log.d(LOG_TAG, "Hiding pictoMainGridView");
this.pictoCategoryGridAdapter.clear();
this.pictoCategoryGridAdapter.addAll(this.sort(this.vocabulary.next(this.currentCategory)));
this.pictoCategoryGridAdapter.notifyDataSetChanged();
if (this.currentCategory.get_color() != -1) {
this.pictoCategoryGridView.setBackgroundColor(this.currentCategory.get_color());
}
this.pictoMainGridView.setEnabled(false);
this.pictoCategoryGridView.setEnabled(true);
if (this.pictoMainGridView.getAnimation() != this.hidePictoMainViewAnimation) {
this.pictoMainGridView.startAnimation(this.hidePictoMainViewAnimation);
}
}
/**
* Returns pictoCategoryGridAdapter or pictoMainGridAdapter depending on the current View
*/
private PictoGridAdapter getCurrentPictoGridAdapter() {
return (currentCategory == null) ? this.pictoMainGridAdapter : this.pictoCategoryGridAdapter;
}
/**
* Order a linked list of pictos with "blank spaces" between them
* @param list
* @return
*/
public LinkedList<Picto> sort(LinkedList<Picto> list){
if (list == null) {
list = new LinkedList<>();
}
LinkedList<Picto> ll = new LinkedList<>();
// This is to show the pictos ordered in the 2D Array that represents the panel
int rows = getResources().getInteger(R.integer.rows);
int cols = getResources().getInteger(R.integer.columns);
Picto[][] mp = new Picto[rows][cols];
for (Picto p : list) {
if (PCBcontext.getPcbdb().getCurrentUser().has_categories()) {
if (p.get_column() != -1 && p.get_row() != -1) {
mp[p.get_column()][p.get_row()] = p;
}
} else {
if (p.getFreeColumn() != -1 && p.getFreeRow() != -1) {
mp[p.getFreeColumn()][p.getFreeRow()] = p;
}
}
}
try {
/*
Picto blankPicto = new Picto(0,
"/symbolstx/color/png/arts_crafts/eraser.png", // TODO Definir imagen para picto en blanco
"Blank picto",
"{\"magnify\":false,\"highlight\":false,\"coord_y\":1,\"id_cat\":12303,\"status\":\"invisible\",\"coord_x\":4}");
*/
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
if(mp[i][j] != null) ll.add(mp[i][j]);
else ll.add(null); // Add the "blank picto" simulating an empty position
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return ll;
}
/**
* Background task that updates the ui into the main thread
*/
public void refresh() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (currentCategory != null) {
hidePictoMainGridView();
} else {
showPictoMainGridView();
}
}
});
}
/**
* @TODO check documentation
* De la interfaz iVocabularyListener
*/
@Override
public void change(action action, int picto_cat, int picto_id, JSONObject args) {
Log.d(LOG_TAG, "Vocabulary action listened: " + action);
if(args!=null) Log.d(LOG_TAG, "args: " + args.toString());
refresh();
}
/**
* Disable Back Button --> Kiosk mode
*/
@Override
public void onBackPressed() {
// Inflate the layout for the AlertDialog
View dialogEscape = View.inflate(this, R.layout.dialog_escape, null);
final CheckBox checkBox = (CheckBox) dialogEscape.findViewById(R.id.checkBox);
final EditText input = (EditText) dialogEscape.findViewById(R.id.editText);
// Build de AlertDialog
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getResources().getString(R.string.exitPictogram));
builder.setView(dialogEscape);
// Set up the buttons
builder.setPositiveButton(getResources().getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String m_Text = input.getText().toString();
String keyword = PCBcontext.getDevice().getKeyword();
if (m_Text.equalsIgnoreCase(keyword)) {
// Change the password
if(checkBox.isChecked()){
// Show a new dialog
View dialogChangeEscapeCode = View.inflate(PCBcontext.getContext(), R.layout.dialog_change_escape_code, null);
final EditText input1 = (EditText) dialogChangeEscapeCode.findViewById(R.id.editText1);
final EditText input2 = (EditText) dialogChangeEscapeCode.findViewById(R.id.editText2);
// Build the AlertDialog
AlertDialog.Builder builder = new AlertDialog.Builder(PCBcontext.getContext());
builder.setTitle(PCBcontext.getContext().getResources().getString(R.string.newEscapeCode));
builder.setView(dialogChangeEscapeCode);
// Set up the buttons
builder.setPositiveButton(getResources().getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Retrieve the text of the 2 password inputs
String m_Text1 = input1.getText().toString();
String m_Text2 = input2.getText().toString();
// If they are equal
if (m_Text1.equalsIgnoreCase(m_Text2)) {
// Change the keyword
PCBcontext.getDevice().setKeyword(m_Text1);
Toast.makeText(PCBcontext.getContext(), getResources().getString(R.string.codeModified), Toast.LENGTH_SHORT).show();
// And exit PictogramActivity
finish();
} else Toast.makeText(PCBcontext.getContext(), getResources().getString(R.string.codesNotEqual), Toast.LENGTH_SHORT).show();
}
});
builder.setNegativeButton(getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}else {
// Finish this Activity it the code is ok and is not checked the checkbox to change the code
finish();
/*
// Start the main activity
Intent mainActivity = new Intent(context, MainActivity.class);
startActivity(mainActivity);
*/
}
// Wrong code
} else Toast.makeText(PCBcontext.getContext(),getResources().getString(R.string.wrongCode), Toast.LENGTH_SHORT).show();
}
});
builder.setNegativeButton(getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}
/**
* Disable long power button press (system menu)
* @param hasFocus
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(!hasFocus) {
// Close every kind of system dialog
Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
sendBroadcast(closeDialog);
}
}
/**
* Disable volume button on key event
* @param event
*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (blockedKeys.contains(event.getKeyCode())) {
return true;
} else {
return super.dispatchKeyEvent(event);
}
}
/* *********************************************************************************************
* Event listener classes
* ********************************************************************************************/
/**
* Class used for dragging pictos to the tape
*/
private class OnPictoDragListener implements View.OnDragListener {
//Drawable normalShape = getResources().getDrawable(R.drawable.normal_shape);
//Drawable targetShape = getResources().getDrawable(R.drawable.target_shape);
@Override
public boolean onDrag(View v, DragEvent event) {
// Handles each of the expected events
switch (event.getAction()) {
//signal for the start of a drag and drop operation.
case DragEvent.ACTION_DRAG_STARTED:
// do nothing
Log.d("Drag:", "1 Drag started");
break;
//the drag point has entered the bounding box of the View
case DragEvent.ACTION_DRAG_ENTERED:
//v.setBackground(targetShape); //change the shape of the view
Log.d("Drag:", "2 Drag entered");
break;
//the user has moved the drag shadow outside the bounding box of the View
case DragEvent.ACTION_DRAG_EXITED:
//v.setBackground(normalShape); //change the shape of the view back to normal
Log.d("Drag:", "3 Drag exited");
break;
//drag shadow has been released,the drag point is within the bounding box of the View
case DragEvent.ACTION_DROP:
Log.d("Drag:", "4 Drop");
View view = (View) event.getLocalState();
ViewGroup viewgroup = (ViewGroup) view.getParent();
int position = Integer.parseInt((String) event.getClipDescription().getLabel());
// if the view is the tape_grid_view, we accept the drag item
// Destino tape_grid_view y origen panel_grid_view
if(v == findViewById(R.id.tape_grid_view) && viewgroup == findViewById(R.id.picto_category_grid_view)) {
Log.d("Drag:", "Posición: " + position);
Picto p = pictoCategoryGridAdapter.getItem(position);
if(!p.is_category()) {
currentCategory = null;
tapeAdapter.addItem(p);
tapeAdapter.notifyDataSetChanged();
showPictoMainGridView();
PCBcontext.getActionLog().log(new TalkAction(TalkAction.ADD, p));
}
}
// Si el destino es el panel y el origen la cinta de frase
else if(v == findViewById(R.id.picto_category_grid_view) && viewgroup == findViewById(R.id.tape_grid_view)){
Log.d("Drag:", "Posición: " + position);
Picto p = tapeAdapter.getItem(position);
tapeAdapter.deleteItem(position);
tapeAdapter.notifyDataSetChanged();
}
break;
//the drag and drop operation has concluded.
case DragEvent.ACTION_DRAG_ENDED:
Log.d("Drag:", "5 Drag ended");
// v.setBackground(normalShape); //go back to normal shape
default:
break;
}
return true;
}
}
/**
* Class used for picto clicking feedback
*/
private class OnPictoClickListener implements AdapterView.OnItemClickListener{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Picto p = getCurrentPictoGridAdapter().getItem(position);
if (p != null && !p.is_invisible() && p.is_enabled()) {
Log.d(LOG_TAG, "Clic en picto: " + p.toString());
//Log.d(LOG_TAG, "STATUS: " + p.get_status());
//QUITAR PARA QUE HABLE pictoCategoryGridAdapter.ttsPicto(p, tts);
// If is not the blank picto, it isn't invisible or disabled
if (p.get_id() != 0 &&
!p.get_status().equalsIgnoreCase("invisible") &&
!p.get_status().equalsIgnoreCase("disabled")) {
LinkedList<Picto> ll = sort(PCBcontext.getVocabulary().next(p));
//LinkedList<Picto> ll = vocabulary.next(p);
Log.d(LOG_TAG, "Lista de pictos recuperada: " + ll.toString());
int maxInTape = getResources().getInteger(R.integer.maxInTape);
// If the picto is a category
if (p.is_category()) {
currentCategory = p;
PCBcontext.getActionLog().log(new TalkAction(TalkAction.SELECT, p));
hidePictoMainGridView();
} else if (tapeAdapter.getCount() < maxInTape) {
currentCategory = null;
tapeAdapter.addItem(p);
tapeAdapter.notifyDataSetChanged();
PCBcontext.getActionLog().log(new TalkAction(TalkAction.ADD, p));
showPictoMainGridView();
}
}
}
}
}
/**
* Class used for long pressing on pictos (start drag)
*/
private class OnPictoLongClickListener implements AdapterView.OnItemLongClickListener {
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) {
// Si es supervisor al hacer longClick deshabilito ese pictograma o lo habilito
Picto p = getCurrentPictoGridAdapter().getItem(position);
if (p == null) {
// No tengo pictograma. Abro una nueva ventana de selección desde el Carrete del device si no es categoria
if (currentCategory != null || !PCBcontext.getPcbdb().getCurrentUser().has_categories()) {
Log.d(LOG_TAG, "No tengo pictograma. Abro carrete...");
int cols = getResources().getInteger(R.integer.columns);
addPicto(position % cols, (int) (position / cols));
}
else
Toast.makeText(PictogramActivity.this, PictogramActivity.this.getResources().getString(R.string.notNewCats), Toast.LENGTH_SHORT).show();
} else {
p.alter_status();
getCurrentPictoGridAdapter().notifyDataSetChanged();
}
} else {
ClipData.Item item = new ClipData.Item("" + position);
String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
ClipData data = new ClipData("" + position, mimeTypes, item);
Picto p = getCurrentPictoGridAdapter().getItem(position);
if (p != null && !p.is_invisible() && p.is_enabled()) {
// If is not the blank picto, it isn't invisible or disabled
if (p.get_id() != 0 &&
!p.get_status().equalsIgnoreCase("invisible") &&
!p.get_status().equalsIgnoreCase("disabled") &&
tapeAdapter.getCount() < 8) {
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(data, //data to be dragged
shadowBuilder, //drag shadow
view, //local data about the drag and drop operation
0 //no needed flags
);
//view.setVisibility(View.INVISIBLE);
}
}
}
return true;
}
}
/**
* Class used for reading pictos on TTS button click
*/
private class OnTTSButtonClickListener implements View.OnClickListener {
@Override
public void onClick(View arg0) {
LinkedList<Picto> lp = tapeAdapter.getAll();
PCBcontext.getActionLog().log(new PictosAction(lp));
tapeAdapter.ttsAllNew(tts);
}
}
/**
* Class for listening the TTS start/end/error events.
* It clears the tape and shows the category grid when the speech has finished or errored.
*/
private class OnTTSEndListener extends UtteranceProgressListener {
@Override
public void onStart(String utteranceId) {
Log.d(LOG_TAG, "TTS tape read start");
}
@Override
public void onDone(String utteranceId) {
Log.d(LOG_TAG, "TTS tape read end");
this.finishSpeech();
}
@Override
public void onError(String utteranceId) {
Log.d(LOG_TAG, "TTS tape read error");
this.finishSpeech();
}
private void finishSpeech() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tapeAdapter.deleteAll();
tapeAdapter.notifyDataSetChanged();
showPictoMainGridView();
}
});
}
}
/**
* Class used for canceling a list of pictos on the tape
*/
private class OnDeleteButtonClickListener implements View.OnClickListener {
@Override
public void onClick(View arg0) {
if (tapeAdapter.hasElements()) {
Picto p = tapeAdapter.getLastItem();
// Call to static log method
PCBcontext.getActionLog().log(new TalkAction(TalkAction.DELETE, p));
// Send websocket action
tapeAdapter.deleteLastView();
tapeAdapter.notifyDataSetChanged();
}
}
}
/**
* Class used for long click on delete button. When a press count reaches 3, the application
* exists.
*/
private class OnDeleteButtonLongClickListener implements View.OnLongClickListener {
@Override
public boolean onLongClick(View v) {
count_deletelong++;
if (count_deletelong >= 3)
PCBcontext.getNetService().restart_app();
return false;
}
}
/**
* Listener used for bringing back the picto categories grid when clicked.
*/
private class OnShowPictoCategoriesViewButtonClick implements View.OnClickListener {
@Override
public void onClick(View v) {
showPictoMainGridView();
}
}
/* *********************************************************************************************
* Methods for adding a new picto from pcb
* ********************************************************************************************/
/**
* add a local picto from pcb
* @param row
* @param col
*/
public void addPicto(int row, int col) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
if (PCBcontext.getPcbdb().getCurrentUser().has_categories()) {
this.getIntent().putExtra(Picto.JSON_ATTTRS.ROW, row);
this.getIntent().putExtra(Picto.JSON_ATTTRS.COLUMN, col);
} else {
this.getIntent().putExtra(Picto.JSON_ATTTRS.FREE_ROW, row);
this.getIntent().putExtra(Picto.JSON_ATTTRS.FREE_COLUMN, col);
}
startActivityForResult(intent, SELECT_PICTURE);
}
/**
* función para la edición de un texto asociado a una nueva imagen y guardar el nuevo picto
*/
public void chooseTextAndSavePicto(final String selectedImagePath, final int row, final int col, final int freeRow, final int freeColumn) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getResources().getString(R.string.enterImgLabel));
// Set up the input
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
// Set up the buttons
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int cat = PictogramActivity.this.currentCategory != null
? PictogramActivity.this.currentCategory.get_id()
: Picto.NO_CATEGORY;
PCBcontext.getVocabulary().saveLocalPicto(
selectedImagePath,
input.getText().toString(),
cat,
row,
col,
freeRow,
freeColumn,
new iLocalPicto() {
@Override
public void saved(Picto localPicto) {
PictogramActivity.this.refresh();
try {
new PictoUploader(localPicto).upload(PictogramActivity.this);
} catch (IOException e) {
Log.e(Vocabulary.class.getCanonicalName(), e.getMessage());
}
}
});
}
});
builder.show();
}
/**
* Función para la selección de una foto del carrete
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == SELECT_PICTURE) {
String selectedImagePath;
Uri selectedImageUri = data.getData();
selectedImagePath = getPath(selectedImageUri);
int row=this.getIntent().getIntExtra(Picto.JSON_ATTTRS.ROW, -1);
int col=this.getIntent().getIntExtra(Picto.JSON_ATTTRS.COLUMN, -1);
int freeRow = this.getIntent().getIntExtra(Picto.JSON_ATTTRS.FREE_ROW, -1);
int freeColumn = this.getIntent().getIntExtra(Picto.JSON_ATTTRS.FREE_COLUMN, -1);
Log.i(this.getClass().getCanonicalName(), "0 Picto x y " + " " + row + " " + col);
this.getIntent().removeExtra(Picto.JSON_ATTTRS.ROW);
this.getIntent().removeExtra(Picto.JSON_ATTTRS.COLUMN);
this.getIntent().removeExtra(Picto.JSON_ATTTRS.FREE_ROW);
this.getIntent().removeExtra(Picto.JSON_ATTTRS.FREE_COLUMN);
chooseTextAndSavePicto(selectedImagePath, row, col, freeRow, freeColumn);
}
}
}
/**
* Función para la selección de una foto del carrete
* @param uri
* @return
*/
static public String getPath(Uri uri) {
if( uri == null ) {
return null;
}
// this will only work for images selected from gallery
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = PCBcontext.getContext().getContentResolver().query(uri, projection, null, null, null);
if( cursor != null ){
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
return uri.getPath();
}
}
package com.yottacode.pictogram.tablet.gui;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import com.yottacode.pictogram.dao.UserLogin;
import com.yottacode.pictogram.tablet.R;
import com.yottacode.pictogram.tools.PCBcontext;
/**
* A login screen that offers login via email/password.
*/
public class SerialActivity extends Activity {
public static final String PREFS_NAME = "MyPrefsFile";
// String constant for logs
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
public static void resetDefaultUser() {
SharedPreferences settings = PCBcontext.getContext().getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("username", "");
editor.putString("password", "");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_serial);
Intent intent=getIntent();
final EditText mSerialViewMail = (EditText) findViewById(R.id.serialmail);
final EditText mSerialViewPass = (EditText) findViewById(R.id.serialpass);
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
String username = settings.getString("username", "");
String password = intent.getBooleanExtra("resetPrevUser", true) ? "" : settings.getString("password", "");
// Escribo el último valor indicado de username
mSerialViewMail.setText(username);
if (!username.equals("") && !password.equals("")) new UserLogin().login(username, password,SerialActivity.this, PictogramActivity.class, LoginActivity.class);;
Button mEntrarButton = (Button) findViewById(R.id.entrar_button);
mEntrarButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
String username = mSerialViewMail.getText().toString();
String password = mSerialViewPass.getText().toString();
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("username", username);
editor.putString("password", password);
editor.commit();
new UserLogin().login(username, password,SerialActivity.this, PictogramActivity.class, LoginActivity.class);
}
});
}
}
package com.yottacode.pictogram.tablet.gui;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
import com.yottacode.pictogram.tablet.R;
import java.util.Timer;
import java.util.TimerTask;
public class SplashScreenActivity extends Activity {
// Set the duration of the splash screen
private static final long SPLASH_SCREEN_DELAY = 3000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set landscape orientation
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// Hide title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_splash_screen);
TimerTask task = new TimerTask() {
@Override
public void run() {
// Start the next activity
Intent mainIntent = new Intent().setClass(
SplashScreenActivity.this, MainActivity.class);
startActivity(mainIntent);
// Intent pictogramActivity = new Intent().setClass(
// SplashScreenActivity.this, PictogramActivity.class);
//pictogramActivity.putExtra();
// seguir: hay que pasarle el estudiante y el supervisor, para cargar la colección, etc...
// startActivity(pictogramActivity);
// Close the activity so the user won't able to go back this
// activity pressing Back button
finish();
}
};
// Simulate a long loading process on application startup.
Timer timer = new Timer();
timer.schedule(task, SPLASH_SCREEN_DELAY);
}
}
package com.yottacode.pictogram.tablet.gui;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
import com.yottacode.net.RestapiWrapper;
import com.yottacode.net.iRestapiListener;
import com.yottacode.pictogram.tablet.R;
import com.yottacode.pictogram.dao.User;
import com.yottacode.pictogram.net.ImgDownloader;
import com.yottacode.pictogram.net.iImgDownloaderListener;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.tools.GUITools;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Vector;
/**
* StudentFragmentGrid implements the gridview with the students
* @author Miguel Ángel García Cumbreras
* @author Fernando
* @version 1.1
*/
public class StudentFragmentGrid extends Fragment{
final String TAG_ID = "id";
final String TAG_USERNAME = "username";
final String TAG_NAME = "name";
final String TAG_SURNAME = "surname";
final String TAG_GENDER = "gender";
final String TAG_PIC = "pic";
final String TAG_LANG = "lang";
final String TAG_ATTRIBUTES = "attributes";
final String TAG_SUPERVISION="supervision";
Vector<Integer> idStudents;
String nameStudents[];
Vector<Bitmap> imageStudents;
private Vector<JSONObject> downloaded_students;
private final String LOG_TAG = this.getClass().getSimpleName(); // Or .getCanonicalName()
GridView gridView;
boolean onlineStudentsOK=false;
private void showStudentsGrid(){
CustomList adapter = new CustomList(getActivity(), nameStudents, imageStudents);
gridView.setAdapter(adapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int pos, long arg3) {
set_user(pos);
}
});
}
private void set_user(int i) {
Boolean offline = getActivity().getIntent().getBooleanExtra("offline", false);
if (offline) {
User currentUser= null;
try {
currentUser = PCBcontext.getDevice().findUser(idStudents.get(i),getActivity().getIntent().getIntExtra("sup_id", 0));
} catch (JSONException e) {
e.printStackTrace();
Log.e(StudentFragmentGrid.this.getClass().getCanonicalName(), e.getMessage());
}
PCBcontext.set_user(currentUser, null, null);
Intent pictogramActivity = new Intent(getActivity(), PictogramActivity.class);
startActivity(pictogramActivity);
} else
new_user(i);
}
private void new_user(int i) {
JSONObject st = this.downloaded_students.get(i);
Intent intent = getActivity().getIntent();
try {
User new_user = new User(
st.getInt(TAG_ID),
st.getString(TAG_USERNAME),
null,
st.getString(TAG_NAME),
st.getString(TAG_SURNAME),
st.getString(TAG_PIC),
st.getString(TAG_GENDER),
st.getString(TAG_LANG),
st.getString(TAG_ATTRIBUTES),
intent.getIntExtra("sup_id", 0),
intent.getStringExtra("email"),
intent.getStringExtra("password"),
intent.getStringExtra("name"),
intent.getStringExtra("surname"),
intent.getStringExtra("pic"),
intent.getStringExtra("gender"),
intent.getStringExtra("lang"),
"");
PCBcontext.getDevice().insertUser(new_user);
Log.i(this.getClass().getCanonicalName(),"Loading vocabulary for "+new_user.get_name_stu());
final ProgressDialog progressDialog=ProgressDialog.show(getActivity(), getString(R.string.loadingGrammar),
getString(R.string.userLoadingTxt), false, false);
PCBcontext.set_user(new_user, intent.getStringExtra("token"), new iImgDownloaderListener() {
@Override
public void loadComplete() {
progressDialog.dismiss();
Intent pictogramActivity = new Intent(getActivity(), PictogramActivity.class);
startActivity(pictogramActivity);
}
@Override
public void loadImg(Img image) {
}
public void error(Exception e) {
progressDialog.dismiss();
GUITools.show_alert(StudentFragmentGrid.this.getActivity(), R.string.serverError, e.getMessage());
Log.e(this.getClass().getCanonicalName(), "Server error:"+ e.getLocalizedMessage());
}
});
} catch (JSONException e) {
e.printStackTrace();
Log.e(StudentFragmentGrid.this.getClass().getCanonicalName(), e.getMessage());
}
}
private void show_student_list_online() {
final Vector<Img> imgs = new Vector<>(downloaded_students.size());
this.nameStudents=new String[downloaded_students.size()];
this.idStudents=new Vector<>(downloaded_students.size());
this.imageStudents=new Vector<>(downloaded_students.size());
for (int i = 0; i < downloaded_students.size(); i++) {
JSONObject st;
try {
st = downloaded_students.get(i);
Integer st_id = st.getInt(TAG_ID);
String st_name = st.getString(TAG_NAME);
String st_pic = st.getString(TAG_PIC);
nameStudents[i]=st_name;
idStudents.add(st_id);
imgs.add(new Img(st_id,st_pic,Img.STUDENT)); //it's required to download student's images
} catch (JSONException e) {
e.printStackTrace();
Log.e(StudentFragmentGrid.this.getClass().getCanonicalName(), e.getMessage());
}
} //for
final ProgressDialog progressDialog= ProgressDialog.show(getActivity(), getString(R.string.imguserLoadingMsg),
getString(R.string.userLoadingTxt), false, false);
ImgDownloader downloader = new ImgDownloader(getActivity(), new iImgDownloaderListener() {
@Override
public void loadComplete() {
progressDialog.dismiss();
if (downloaded_students.size() > 1) {
for (int i = 0; i < imgs.size(); i++)
try {
imageStudents.add(imgs.get(i).get_bitmap(getActivity().getBaseContext()));
} catch (IOException e) {
e.printStackTrace();
Log.e(StudentFragmentGrid.this.getClass().getCanonicalName(),e.getMessage());
}
}
if (StudentFragmentGrid.super.getView()!=null)
showStudentsGrid();
else
onlineStudentsOK=true;
}
@Override
public void loadImg(Img image) {
}
public void error(Exception e) {
progressDialog.dismiss();
GUITools.show_alert(PCBcontext.getContext(), R.string.serverError, e.getMessage());
Log.e(this.getClass().getCanonicalName(), "Server error:"+ e.getLocalizedMessage());
}
}, ImgDownloader.tsource.remote);
downloader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imgs);
}
private void download_students(int sup_id ) {
String token = getActivity().getIntent().getExtras().getString("token");
RestapiWrapper wrapper = new RestapiWrapper(
getActivity().getApplicationContext().getResources().getString(R.string.server), token);
String operation = "sup/" + sup_id + "/students";
final ProgressDialog progressDialog= ProgressDialog.show(getActivity(), getString(R.string.userLoadingTxt),
getString(R.string.userLoadingTxt), false, false);
wrapper.ask(operation, new iRestapiListener() {
@Override
public void error(Exception e) {
Log.e(this.getClass().getName(), " Server restapi error: " + e.getLocalizedMessage());
progressDialog.dismiss();
GUITools.show_alert(getActivity(), R.string.loginErrorTxt, getString(R.string.serverError), new GUITools.iOKListener() {
@Override
public void ok() {
PCBcontext.getNetService().restart_app();
}
});
}
@Override
public void preExecute() {
}
@Override
public void result(JSONArray students) {
progressDialog.dismiss();
StudentFragmentGrid.this.downloaded_students=new Vector();
for (int i=0;i<students.length();i++) {
JSONObject student;
try {
student = students.getJSONObject(i);
if (student.getInt(TAG_SUPERVISION)>0)
StudentFragmentGrid.this.downloaded_students.add(student);
} catch (JSONException e) {
e.printStackTrace();
}
}
switch (students.length()) {
case 0:
GUITools.show_alert(getActivity(), R.string.loginErrorTxt,getString(R.string.noStudentsError), new GUITools.iOKListener() {
@Override
public void ok() {
PCBcontext.getNetService().restart_app();
}
});
break;
case 1:
new_user(0);
break;
default:
show_student_list_online();
}
}
@Override
public void result(JSONObject result) {
}
});
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
Intent intent=getActivity().getIntent();
Boolean offline = intent.getBooleanExtra("offline", false);
int sup_id=intent.getIntExtra("sup_id", 0);
if (offline) {
Vector<User> users;
try {
users = PCBcontext.getDevice().recoverStudents(sup_id);
Log.i(this.getClass().getCanonicalName(),"Recovering "+users.size()+" students list for "+ sup_id+" from local DB");
} catch (JSONException e) {
e.printStackTrace();
users=null;
}
int i=0;
this.nameStudents=new String[users.size()];
this.idStudents=new Vector<>(users.size());
this.imageStudents=new Vector<>(users.size());
for (User user: users) {
this.idStudents.add(user.get_id_stu());
this.nameStudents[i++]=user.get_name_stu();
try {
this.imageStudents.add(user.get_bitmap_stu(getActivity().getBaseContext()));
} catch (IOException e) {
e.printStackTrace();
Log.e(this.getClass().getName(), " Getting images error: " + e.getLocalizedMessage());
}
}
}
else
download_students(sup_id);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_student, container, false);
gridView = (GridView)view.findViewById(R.id.loginStudentGridView);
Boolean offline = getActivity().getIntent().getBooleanExtra("offline", false);
if (offline || onlineStudentsOK) showStudentsGrid();
return view;
}
}
package com.yottacode.pictogram.tablet.gui;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.yottacode.pictogram.dao.Picto;
import java.util.Iterator;
import java.util.LinkedList;
public class TapeAdapter extends BaseAdapter {
//private Context mContext;
private LinkedList<Picto> pictoLinkedList;
public TapeAdapter(){
//mContext = c;
pictoLinkedList = new LinkedList<Picto>(); // the list begins empty
}
@Override
public int getCount(){
return pictoLinkedList.size();
}
public Picto getItem(int position) {
// este método debería devolver el objeto que esta en esa posición del
// adapter.
return pictoLinkedList.get(position);
}
public long getItemId(int position) {
// este método debería devolver el id de fila del item que esta en esa
// posición del adapter. No es necesario en este caso más que devolver 0.
return 0;
}
// AÑADIR ITEM AL ADAPTADOR
public void addItem(Picto p){
pictoLinkedList.add(p);
}
// ELIMINAR ITEM DEL ADAPTADOR
public void deleteItem(int position){
pictoLinkedList.remove(position);
}
// ELIMINAR el último ITEM DEL ADAPTADOR
public void deleteLastView(){
// Controlar excepcion al intentar eliminar el último cuando no hay elementos
try{
pictoLinkedList.removeLast();
}catch(ArrayIndexOutOfBoundsException exception){
Log.e("Excepción", "ArrayIndexOutOfBounds: " + exception.getMessage());
}
}
// ELIMINAR TODOS LOS ITEMS DEL ADAPTADOR
public void deleteAll(){
pictoLinkedList.clear();
}
// DEVUELVE TODOS LOS ELEMENTOS
public LinkedList<Picto> getAll(){ return pictoLinkedList; }
// Devuelvo la cadena actual como un String
public String getAllAsString(){
String complete = "";
Iterator<Picto> iterator = pictoLinkedList.iterator();
while (iterator.hasNext()) {
Picto current = iterator.next();
complete += " " + current.get_translation();
}
return complete;
}
// DEVUELVE último elemento
public Picto getLastItem(){
return pictoLinkedList.getLast();
}
// Devuelve true o false si tiene o no elementos la lista de pictos
public boolean hasElements(){
return (pictoLinkedList.size() > 0);
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
return PictoItemViewGenerator.getPictoView(
this.pictoLinkedList.get(position),
convertView,
parent
);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void ttsAllNew(TextToSpeech tts) {
String input = getAllAsString();
Bundle params = new Bundle();
params.putString(TextToSpeech.Engine.KEY_PARAM_VOLUME, "1");
params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "TAPE_READ");
tts.speak(input, TextToSpeech.QUEUE_FLUSH, params, "TAPE_READ");
}
}
package com.yottacode.pictogram.tablet.net;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.app.admin.SystemUpdatePolicy;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.yottacode.pictogram.tablet.R;
import com.yottacode.pictogram.tablet.gui.PictogramActivity;
import com.yottacode.pictogram.tablet.gui.SerialActivity;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.pictogram.net.iNetServiceDevice;
/**
* Background notify service
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class NetServiceTablet implements iNetServiceDevice {
private static NotificationCompat.Builder builder;
public void build() {
this.builder = new NotificationCompat.Builder(PCBcontext.getContext());
Intent resultIntent = new Intent(PCBcontext.getContext(), PictogramActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(PCBcontext.getContext());
stackBuilder.addParentStack(PictogramActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendingIntent);
}
public void notifyStatus(boolean updated) {
int notifyID = 1;
String user="";
if (PCBcontext.getPcbdb()!=null) {
user=PCBcontext.getPcbdb().getCurrentUser().get_name_stu();
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor())
user += " (" + PCBcontext.getPcbdb().getCurrentUser().get_name_sup() + ")";
}
if (updated)
builder.setSmallIcon(R.drawable.application_online)
.setContentTitle(PCBcontext.getContext().getResources().getString(R.string.pictogram_online))
.setContentText(user);
else
builder.setSmallIcon(R.drawable.application_offline)
.setContentTitle(PCBcontext.getContext().getResources().getString(R.string.pictogram_offline))
.setContentText(user);
NotificationManager mNotificationManager =
(NotificationManager) PCBcontext.getContext().getSystemService(PCBcontext.getContext().NOTIFICATION_SERVICE);
mNotificationManager.notify(notifyID, builder.build());
}
public void closeNotifyStatus(){
int notifyID = 1;
NotificationManager mNotificationManager =
(NotificationManager) PCBcontext.getContext().getSystemService(PCBcontext.getContext().NOTIFICATION_SERVICE);
mNotificationManager.cancel(notifyID);
}
/**
* if the user becomes from offline to online and the login is failed, then the user relogin in
* the app. The most frequent reason is a password change while the user have been logged
* offline
*/
public void restart_app() {
StackTraceElement trace[] = Thread.currentThread().getStackTrace();
Log.i(this.getClass().getCanonicalName(),"App restarted from "+trace[0]+"->"+trace[1]+"..."+
trace[trace.length-2]+"->"+trace[trace.length-1]);
SerialActivity.resetDefaultUser();
Intent serialActivity = new Intent(PCBcontext.getContext(), SerialActivity.class);
serialActivity.putExtra("resetPrevUser", true);
PCBcontext.getContext().startActivity(serialActivity);
}
}
package com.yottacode.pictogram.watch.gui;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.util.Log;
import android.widget.TextView;
import com.yottacode.pictogram.dao.UserLogin;
import com.yottacode.pictogram.net.NetService;
import com.yottacode.pictogram.net.iNetServiceDevice;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.pictogram.watch.R;
import com.yottacode.pictogram.watch.net.NetServiceWatch;
public class MainActivity extends Activity {
private static NetServiceWatch service_watch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("MAIN_AC", "Is user log? "+PCBcontext.is_user_logged());
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
if (PCBcontext.is_user_logged()) {
TextView alumnView = (TextView) stub.findViewById(R.id.alumn);
TextView vocabView = (TextView) stub.findViewById(R.id.vocab);
TextView textView = (TextView) stub.findViewById(R.id.status);
alumnView.setText("Estudiante " + PCBcontext.getPcbdb().getCurrentUser().get_name_stu());
vocabView.setText("Tamaño vocabulario: " + PCBcontext.getVocabulary().size());
service_watch.set_notifyTextView(textView);
}
else {
service_watch= new NetServiceWatch(this.getClass());
PCBcontext.init(MainActivity.this,service_watch );
Log.d(this.getClass().getCanonicalName(), "PCBcontext iniciado.");
login();
}
}
});
}
private void login() {
final String username="faf0001";
final String password="faf0001";
new UserLogin().login(username,password,this, MainActivity.class,null);
}
}
package com.yottacode.watch;
import android.app.Activity;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.widget.TextView;
import com.yottacode.pictogram.watch.R;
public class MainActivity extends Activity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
mTextView = (TextView) stub.findViewById(R.id.text);
}
});
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment