Commit 07acc222 by Arturo Montejo Ráez

Merge branch 'develop'

parents 0a44eb2b 90a2e0c6
Showing with 2832 additions and 516 deletions
......@@ -62,6 +62,13 @@ android/Pictogram/commonlibrary/build
android/Pictogram/tablet/build
android/Pictogram/watch/build
android/Pictogram/build
android/Pictogram/supervisor_tablet/supervisor_tablet.iml
android/Pictogram/tabletlibrary/tabletlibrary.iml
android/Pictogram/yotta_tablet/yotta_tablet.iml
android/Pictogram/watch/watch.iml
android/Pictogram/yotta_tablet.iml
android/Pictogram/tabletlibrary.iml
android/Pictogram/supervisor_tablet.iml
# OS generated files #
######################
......
......@@ -64,3 +64,20 @@ gen/
/Pictogram.iml
/.gitignore
!/tabletlibrary/tabletlibrary.iml
supervisor_tablet/src/main/res/drawable-xhdpi/
supervisor_tablet/src/main/res/drawable-xxhdpi/
supervisor_tablet/src/main/res/drawable-xxxhdpi/
tabletlibrary/libs/
tabletlibrary/src/main/res/anim/slide_in.xml
tabletlibrary/src/main/res/anim/slide_out.xml
tabletlibrary/src/main/res/drawable-hdpi/descarga.png
tabletlibrary/src/main/res/drawable-hdpi/flash.png
tabletlibrary/src/main/res/drawable-hdpi/session_talk.png
yotta_tablet/src/main/res/drawable-hdpi/
yotta_tablet/src/main/res/drawable-xhdpi/
yotta_tablet/src/main/res/drawable-xxhdpi/
yotta_tablet/src/main/res/drawable-xxxhdpi/
/watch/watch.iml
/commonlibrary/commonlibrary.iml
/tabletlibrary/tabletlibrary.iml
......@@ -10,7 +10,10 @@ android {
versionCode 1
versionName "1.0"
resValue "string", "db_name", "PCB.db"
resValue "integer", "db_version", "4"
resValue "string", "app_version", "0.1"
resValue "string", "core_vocabulary", "core_vocabulary"
resValue "string", "apk", "to_be_set_in_subproject"
}
buildTypes {
release {
......@@ -21,7 +24,7 @@ android {
resValue "bool", "ssl_connect", "true"
resValue "bool", "force_img_download", "false"
resValue "integer", "netservice_timing", "5"
resValue "integer", "netservice_force_restfull_synchro", "0"
resValue "integer", "netservice_force_restfull_synchro", "30"
}
debug {
resValue "string", "server", "https://dev.yottacode.com"
......@@ -29,7 +32,7 @@ android {
resValue "bool", "ssl_connect", "false"
resValue "bool", "force_img_download", "false"
resValue "integer", "netservice_timing", "5"
resValue "integer", "netservice_force_restfull_synchro", "0"
resValue "integer", "netservice_force_restfull_synchro", "30"
}
}
}
......
......@@ -18,8 +18,6 @@ import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.Hashtable;
import javax.net.ssl.HttpsURLConnection;
/**
* Wrapper for connecting with a server using its tokenized RESTful API.
......@@ -136,7 +134,7 @@ public class RestapiWrapper {
StringBuilder response=new StringBuilder("");
String line;
JSONObject JSONresponse;
BufferedReader br = new BufferedReader(new InputStreamReader(responseCode == HttpsURLConnection.HTTP_OK
BufferedReader br = new BufferedReader(new InputStreamReader(responseCode == HttpURLConnection.HTTP_OK
? urlConnection.getInputStream()
: urlConnection.getErrorStream()));
// Log.i(LOG_TAG, "starting to read server answer for"+urlConnection.getURL().toString());
......@@ -149,7 +147,7 @@ public class RestapiWrapper {
Log.i(LOG_TAG, "Raw server answer: " + response);
try {
JSONresponse = new JSONObject("{ "+SERVER_RESULT+": " + response + (responseCode == HttpsURLConnection.HTTP_OK
JSONresponse = new JSONObject("{ "+SERVER_RESULT+": " + response + (responseCode == HttpURLConnection.HTTP_OK
? "}"
: ", "+SERVER_ERROR+": " + responseCode +"}"));
} catch (JSONException e) {
......@@ -174,8 +172,7 @@ public class RestapiWrapper {
surl=surl.substring(0,surl.length()-1);
}
url = new URL(surl);
HttpURLConnection urlConnection = null;
urlConnection = (HttpsURLConnection) url.openConnection();
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(TIME_OUT);
urlConnection.setConnectTimeout(TIME_OUT);
urlConnection.setRequestMethod("GET");
......@@ -189,7 +186,7 @@ public class RestapiWrapper {
URL url = new URL(surl);
HttpURLConnection urlConnection = null;
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(TIME_OUT);
urlConnection.setConnectTimeout(TIME_OUT);
urlConnection.setRequestMethod(request_method);
......@@ -217,7 +214,6 @@ public class RestapiWrapper {
}
}
//Send request
DataOutputStream wr = new DataOutputStream (
urlConnection.getOutputStream ());
......@@ -254,7 +250,7 @@ public class RestapiWrapper {
protected HttpAsyncTaskParams doInBackground(HttpAsyncTaskParams... params) {
JSONObject jresult=null;
try {
Log.i(LOG_TAG, " Asking to the server for " + params[0].url+" params:"+params[0].url_params +" JSON?"+params[0].json_params);
Log.i(LOG_TAG, " Asking to the server for " + params[0].url+" method:"+params[0].request_method+" params:"+params[0].url_params +" JSON?"+params[0].json_params);
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);
......
......@@ -2,9 +2,8 @@ package com.yottacode.pictogram.action;
import android.util.Log;
import com.yottacode.pictogram.net.websockets.Room;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.pictogram.net.websockets.Room;
import org.json.JSONException;
import org.json.JSONObject;
......@@ -50,9 +49,7 @@ public abstract class PictoAction extends Action {
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";
......@@ -61,11 +58,8 @@ public abstract class PictoAction extends Action {
.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;
}
......
package com.yottacode.pictogram.dao;
import android.content.Context;
import com.yottacode.pictogram.R;
import com.yottacode.pictogram.tools.PCBcontext;
import java.io.InputStream;
/**
* Platform abstraction (Android)
* @see PCBDBHelper
* *
*
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class DeviceHelper {
public static float version= Float.valueOf(PCBcontext.getContext().getResources().getString(R.string.app_version)).floatValue();
public static String getDBName(Context context) {
return context.getResources().getString(R.string.db_name);
}
public static int getDBVersion(Context context) {
return context.getResources().getInteger(R.integer.db_version);
}
public static float getAppVersion() {
return version;
}
public static float setAppVersion(float version) {
return DeviceHelper.version=version;
}
public static InputStream getDBScriptStream(Context context) {
return context.getResources().openRawResource(R.raw.pcbdb_create);
}
public static boolean force_create(Context context) {
return context.getResources().getBoolean(R.bool.force_db_create);
}
}
......@@ -2,14 +2,12 @@ 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 com.yottacode.pictogram.R;
import com.yottacode.pictogram.action.Action;
import com.yottacode.pictogram.grammar.Vocabulary;
import com.yottacode.pictogram.tools.PCBcontext;
......@@ -17,37 +15,10 @@ import com.yottacode.pictogram.tools.PCBcontext;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Vector;
/**
* 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
......@@ -70,7 +41,7 @@ public class PCBDBHelper extends SQLiteOpenHelper {
* @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);
super(PCBcontext.getContext(), DeviceHelper.getDBName(PCBcontext.getContext()), factory, DeviceHelper.getDBVersion(PCBcontext.getContext()));
this.user_online=false;
this.setCurrentUser(user == null ? this.getCurrentUser() : user);
}
......@@ -105,14 +76,14 @@ public class PCBDBHelper extends SQLiteOpenHelper {
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));
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.getString(18));
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage() + " BAD FORMED JSON: " + cursor.getString(5));
System.exit(-1);
}
}
cursor.close();
db.close();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
return last_user;
}
......@@ -138,7 +109,7 @@ public class PCBDBHelper extends SQLiteOpenHelper {
ContentValues values=new ContentValues(1);
values.put("json_action", action.getDescription().toString());
db.insert("action", null, values);
db.close();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
}
/**
* Load saved actions
......@@ -159,7 +130,7 @@ public class PCBDBHelper extends SQLiteOpenHelper {
Log.e(LOG_TAG, "Recovering batch actions:"+e.getMessage());
}
cursor.close();
db.close();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
return actions;
}
......@@ -182,12 +153,12 @@ public class PCBDBHelper extends SQLiteOpenHelper {
Cursor cursor = db.query("collection_detail", null, "id_stu=?", new String[]{String.valueOf(id_stu)}, null, null, null, null);
Log.i(LOG_TAG, "Local recovering " + cursor.getCount() + " pictos for student " + id_stu + " from local DB");
cursor.moveToFirst();
while (cursor.moveToNext()) {
if (cursor.getCount()>0) do{
Picto picto = new Picto(cursor.getInt(1), cursor.getString(2), cursor.getString(3), cursor.getString(4));
vocabulary.loadPicto(picto);
}
vocabulary.addPicto(picto);
}while (cursor.moveToNext());
cursor.close();
db.close();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
return vocabulary;
}
......@@ -211,8 +182,9 @@ public class PCBDBHelper extends SQLiteOpenHelper {
ContentValues values=new ContentValues(5);
values.put("id_stu", id_stu);
db.beginTransaction();
for (Picto picto : vocabulary) {
Log.e(LOG_TAG,"inserting "+picto.get_id()+":"+picto.get_translation()+":"+picto.get_json_attrs());
//Log.e(LOG_TAG,"inserting "+picto.get_id()+":"+picto.get_translation()+":"+picto.get_json_attrs());
newsize++;
values.put("id_picto", picto.get_id());
values.put("url", picto.get_url());
......@@ -223,8 +195,9 @@ public class PCBDBHelper extends SQLiteOpenHelper {
int seconds2 = Calendar.getInstance().get(Calendar.SECOND);
Log.i(LOG_TAG, " Local student vocabulary updated, id:" + id_stu + ", cats: " + vocabulary.size() + " time:" + (seconds2 - seconds1) + " secs. Size: " + newsize + " read only?" + db.isReadOnly());
db.close();
db.setTransactionSuccessful();
db.endTransaction();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
}
/**
......@@ -233,7 +206,7 @@ public class PCBDBHelper extends SQLiteOpenHelper {
* @param picto added to the Student collection
* @see com.yottacode.pictogram.dao.Picto
*/
public void addPicto(Picto picto) {
public void savePicto(Picto picto) {
int id_stu = this.getCurrentUser().get_id_stu();
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values=new ContentValues(6);
......@@ -244,7 +217,7 @@ public class PCBDBHelper extends SQLiteOpenHelper {
values.put("attributes",picto.get_json_attrs());
db.insert("collection_detail", null, values);
Log.i(LOG_TAG,"Picto added:"+picto.get_translation());
db.close();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
}
/**
......@@ -254,13 +227,12 @@ public class PCBDBHelper extends SQLiteOpenHelper {
* @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();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
}
/**
......@@ -277,7 +249,7 @@ public class PCBDBHelper extends SQLiteOpenHelper {
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();
//db.close(); <--no es necesario cerrar la bbdd https://groups.google.com/forum/#!msg/android-developers/NwDRpHUXt0U/jIam4Q8-cqQJ
}
......
......@@ -19,11 +19,24 @@ import org.json.JSONObject;
* @author Fernando Martinez Santiago
* @version 1.0
*/
public class
Picto extends Img {
public class Picto extends Img {
private static final String LOG_TAG =Img.class.getName();
public static final int STUPICTO_NULL = -1;
public int get_stupicto_id() {
int stupicto_id;
try {
stupicto_id=this.attributes.getInt(JSON_ATTTRS.STUPICTO_ID);
} catch (JSONException e) {
e.printStackTrace();
stupicto_id=STUPICTO_NULL ;
}
return stupicto_id;
}
public final static class JSON_ATTTRS {
public static String STUPICTO_ID = "id";
public static String CATEGORY = "id_cat";
public static String COLUMN = "coord_x";
public static String ROW = "coord_y";
......@@ -57,8 +70,7 @@ Picto extends Img {
private JSONObject attributes;
private String translation;
private boolean is_mirror=false;
private boolean is_mirror=false;
private boolean highlight_background=false;
public boolean is_mirror() {return is_mirror;}
......@@ -73,34 +85,34 @@ Picto extends Img {
try {
this.attributes=new JSONObject(p.attributes.toString());
} catch (JSONException e) {
Log.e(this.getClass().getCanonicalName(),e.getMessage());
Log.e(LOG_TAG,e.getMessage());
}
translation=new String(p.get_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()
public Picto(int id, String url, String translation, int cat, int row, int column, int freeRow, int freeColumn, int stupicto_id) throws JSONException {
this(id, url, 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)
.put(JSON_ATTTRS.LEGEND,JSON_ATTTR_LEGEND_VALUES.NONE));
.put(JSON_ATTTRS.LEGEND,JSON_ATTTR_LEGEND_VALUES.NONE)
.put(JSON_ATTTRS.STUPICTO_ID,stupicto_id)
.put(JSON_ATTTRS.EXPRESSION,translation));
}
public Picto(int id, String url,String translation, String attributes) throws JSONException {
this(id, url, translation, new JSONObject(attributes));
this(id, url, new JSONObject(attributes).put(JSON_ATTTRS.EXPRESSION,translation));
}
public Picto(int id, String url,String translation, JSONObject attributes) {
super(id,url,Img.VOCABULARY);
this.translation=translation;
public Picto(int id, String url, JSONObject attributes) {
super(id,url,Img.VOCABULARY);;
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());
Log.e(LOG_TAG, e.toString());
}
}
......@@ -117,18 +129,6 @@ Picto extends Img {
*/
public String get_url() {return this.url;}
/**
*
* @param newTranslation
*/
public void set_translation(String newTranslation){
this.translation = newTranslation;
try {
this.attributes.put(JSON_ATTTRS.EXPRESSION,newTranslation);
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
*
......@@ -140,10 +140,10 @@ Picto extends Img {
translation=this.attributes.has(JSON_ATTTRS.EXPRESSION) &&
this.attributes.getString(JSON_ATTTRS.EXPRESSION)!=null && !this.attributes.getString(JSON_ATTTRS.EXPRESSION).equals("null")
? this.attributes.getString(JSON_ATTTRS.EXPRESSION)
: this.translation;
: "";
} catch (JSONException e) {
translation=this.translation;
translation="";
}
return translation;
}
......@@ -280,6 +280,7 @@ Picto extends Img {
legend=this.attributes.getString(JSON_ATTTRS.LEGEND);
} catch (JSONException e) {
legend=JSON_ATTTR_LEGEND_VALUES.NONE; // By default
Log.e(LOG_TAG," Error getting legend:"+e.getMessage());
}
return legend.equalsIgnoreCase("null") ? JSON_ATTTR_LEGEND_VALUES.NONE : legend;
}
......@@ -361,36 +362,14 @@ Picto extends Img {
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
: is_disabled() ? JSON_ATTTR_STATUS_VALUES.INVISIBLE
: 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();
}
*/
/**
* modify locally the status of the picto
* @param status the status that u press on the menu
* @return true if current status is enabled. False in other case.
*/
public boolean alter_status(String status) {
Log.i(this.getClass().getCanonicalName(),"Picto id. "+get_id()+" status enabled/disabled modified to "+is_enabled());
Log.i(LOG_TAG,"Picto id. "+get_id()+" status enabled/disabled modified to "+is_enabled());
try {
this.attributes.put(JSON_ATTTRS.STATUS, status);
set_local_status(true);
......@@ -400,7 +379,7 @@ Picto extends Img {
}
} catch (JSONException e) {
e.printStackTrace();
Log.e(this.getClass().getCanonicalName(),e.getMessage());
Log.e(LOG_TAG,e.getMessage());
}
return is_enabled();
}
......@@ -423,7 +402,7 @@ Picto extends Img {
PCBcontext.getPcbdb().modifyPicto(this.get_id(), this.get_json_attrs());
} catch (JSONException e) {
e.printStackTrace();
Log.e(this.getClass().getCanonicalName(), e.getMessage());
Log.e(LOG_TAG, e.getMessage());
}
else {
......@@ -431,4 +410,5 @@ Picto extends Img {
PCBcontext.getPcbdb().modifyPicto(this.get_id(), this.get_json_attrs());
}
}
}
......@@ -24,14 +24,19 @@ public class User {
return this.get_id_sup()!=User.NO_SUPERVISOR;
}
public String get_office() {
return office_sup;
}
public final static class JSON_STUDENT_ATTTRS{
public enum delivery {clean, one, many}
static String CATEGORIES = "categories";
static String INPUT_FEEDBACK = "input_feedback";
static String INPUT_SELECTION = "input_selection";
static String PICTOGRAM_SIZE ="size";
static String TTS_ENGINE = "tts engine";
static String TTS_VOICE = "tts voice";
static String DELETE_STRIP = "delete_strip_after_delivery";
static String DELIVERY = "delivery";
}
public final static class JSON_STUDENT_INPUT_FEEDBACK {
public static String VIBRATION="vibration";
......@@ -43,16 +48,16 @@ public class User {
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;
private String email_sup, pwd_sup, name_sup, surname_sup, gender_sup, lang_sup, tts_engine_sup, office_sup;
private boolean mirror_mode=false;
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);
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,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 {
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, String office_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;
......@@ -70,6 +75,7 @@ public class User {
this.gender_sup=gender_sup;
this.lang_sup=lang_sup;
this.tts_engine_sup=tts_engine_sup;
this.office_sup=office_sup;
}
......@@ -265,8 +271,27 @@ public class User {
*
* @return input feedback of the student configuration (default: "vibration")
*/
public boolean delete_tape_after_delivery() {
return input_feedback_on(JSON_STUDENT_ATTTRS.DELETE_STRIP);
public JSON_STUDENT_ATTTRS.delivery delivery() {
JSON_STUDENT_ATTTRS.delivery delivery;
try {
switch (this.attributes_stu.getInt(JSON_STUDENT_ATTTRS.DELIVERY)) {
case 0:
delivery = JSON_STUDENT_ATTTRS.delivery.clean;
break;
case 1:
delivery = JSON_STUDENT_ATTTRS.delivery.one;
break;
case 2:
delivery = JSON_STUDENT_ATTTRS.delivery.many;
break;
default:
delivery = JSON_STUDENT_ATTTRS.delivery.one;
break;
}
} catch (JSONException e) {
delivery=JSON_STUDENT_ATTTRS.delivery.one;
}
return delivery;
}
/**
......@@ -300,6 +325,9 @@ public class User {
return get_id_sup()!=User.NO_SUPERVISOR;
}
public boolean is_teacher() {
return get_office() == null ? false : get_office().length()>0;
}
/**
......
......@@ -32,6 +32,8 @@ public class UserLogin {
private static final String TAG_GENDER = "gender";
private static final String TAG_PIC = "pic";
private static final String TAG_LANG = "lang";
private static final String TAG_OFFICE = "office";
private static final String TAG_OFFICE_NAME = "name";
private static final String TAG_ATTRIBUTES = "attributes";
//private static final String TAG_TTSENGINE = "ttsEngine";
......@@ -75,8 +77,9 @@ public class UserLogin {
GUITools.show_alert(activity, R.string.serverError);
}
Intent loginActivity = new Intent(activity, LoginActivity);
try {
Intent loginActivity = new Intent(activity, LoginActivity);
Integer su_id = user.getInt(TAG_ID);
int su_id_int = su_id.intValue();
......@@ -90,11 +93,16 @@ public class UserLogin {
loginActivity.putExtra("pic", user.getString(TAG_PIC));
loginActivity.putExtra("gender", user.getString(TAG_GENDER));
loginActivity.putExtra("lang", user.getString(TAG_LANG));
activity.startActivity(loginActivity);
} catch (JSONException e) {
GUITools.show_alert(activity, R.string.serverError, e.getMessage());
}
try {
loginActivity.putExtra("office", user.getJSONObject(TAG_OFFICE).getString(TAG_OFFICE_NAME));
} catch (JSONException e) {
loginActivity.putExtra("office", "");
}
activity.startActivity(loginActivity);
}
@Override
public void error(RestapiWrapper.HTTPException e) {
......@@ -123,7 +131,7 @@ public class UserLogin {
Class PictogramActivity, Class LoginActivity) {
try {
Vector<User> localUsers = PCBcontext.getDevice().findUser(email, password);
Log.i(this.getClass().getCanonicalName(), "Offline login request for supervisor "+email);
Log.i(this.getClass().getCanonicalName(), "Offline login request for supervisor "+email+localUsers.elementAt(0).get_email_sup()+localUsers.elementAt(0).is_supervisor());
if (localUsers.size() == 1) {
PCBcontext.set_user(localUsers.elementAt(0), null, null);
......@@ -135,6 +143,7 @@ public class UserLogin {
loginActivity.putExtra("name", localUsers.elementAt(0).get_name_sup());
loginActivity.putExtra("surname", localUsers.elementAt(0).get_surname_sup());
loginActivity.putExtra("pic", localUsers.elementAt(0).get_url_img_sup());
loginActivity.putExtra("office", localUsers.elementAt(0).get_office());
loginActivity.putExtra("offline", true);
activity.startActivity(loginActivity);
}
......@@ -188,7 +197,7 @@ public class UserLogin {
st_gender,
st_lang,
st_attributes,
User.NO_SUPERVISOR, "", "", "", "", "", "M", "es-es", "")
User.NO_SUPERVISOR, "", "", "", "", "", "M", "es-es", "", "")
, result.getString(TAG_TOKEN)
, pictogramActivity, activity);
} catch (JSONException e) {
......
......@@ -36,7 +36,8 @@ import java.util.Vector;
public class Vocabulary implements Iterable<Picto> {
Hashtable<Integer,LinkedList<Picto>> pictos;
private static final String LOG_TAG = Vocabulary.class.getName();
Hashtable<Integer,LinkedList<Picto>> pictos;
static int DEFAULT_VOCABULARY_SIZE=200;
ImgDownloader.iImgDownloaderListener imgListener;
private ActionTalk actionTalk;
......@@ -89,9 +90,15 @@ public class Vocabulary implements Iterable<Picto> {
try{
String uri=args.getJSONObject("picto").getString("uri");
JSONObject attrs_picto = args.getJSONObject("attributes");
String text= attrs_picto.getString("expression");
attrs_picto.put(Picto.JSON_ATTTRS.STUPICTO_ID,args.getInt("id"));
Picto newPicto=new Picto(picto_id, uri, attrs_picto);
Picto prev_picto=find_picto(newPicto.get_category(), newPicto.get_row(),newPicto.get_column());
addPicto(new Picto(picto_id, uri, text, attrs_picto),ImgDownloader.tsource.remote);
if (prev_picto!=null) {
Log.i(LOG_TAG, "Pictogram "+prev_picto.get_translation()+":"+prev_picto.get_id()+" to be replaced by "+newPicto.get_translation()+":"+newPicto.get_id());
removePicto(prev_picto.get_category(), prev_picto.get_id());
}
if (find_picto_index(newPicto.get_category(),newPicto.get_id())==-1) addPicto(newPicto,ImgDownloader.tsource.remote);
} catch (JSONException e) {
Log.e(this.getClass().getCanonicalName(), e.getClass().getCanonicalName() + "--" + e);
......@@ -134,11 +141,11 @@ public class Vocabulary implements Iterable<Picto> {
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
if (picto.is_local())
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());
new PictoUploader(picto).upload();
} catch (IOException e) {
e.printStackTrace();
Log.e(this.getClass().getName(), " Picto json error from server: " + picto.toString());
......@@ -170,22 +177,19 @@ public class Vocabulary implements Iterable<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, attributes = null;
String expression;
JSONObject ojpicto=null;
JSONObject picto, attributes ;
JSONObject stupicto=null;
try {
for (int i=0; i < result.length(); i++) {
ojpicto=result.getJSONObject(i);
picto = ojpicto.getJSONObject(jpicto);
attributes = ojpicto.getJSONObject(jattributes);
expression = attributes.getString(jexpression);
stupicto=result.getJSONObject(i);
picto = stupicto.getJSONObject(jpicto);
attributes = stupicto.getJSONObject(jattributes);
attributes.put(Picto.JSON_ATTTRS.STUPICTO_ID,stupicto.get(jid));
pictos[i] = new Picto(picto.getInt(jid),
picto.getString(juri),
expression,
attributes);
}
synchronizeImgs(pictos);
......@@ -196,7 +200,7 @@ public class Vocabulary implements Iterable<Picto> {
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());
Log.e(this.getClass().getName(), " Picto JSON error from server: " + stupicto.toString());
this.error(new RestapiWrapper.HTTPException("JSON Error:"+e.getMessage(),-1));
}
}
......@@ -209,7 +213,7 @@ public class Vocabulary implements Iterable<Picto> {
@Override
public void error(RestapiWrapper.HTTPException e) {
Log.e(this.getClass().getName(), " Server RESTAPI error: " + e.getLocalizedMessage());
Vocabulary.this.imgListener.error(e);
if (Vocabulary.this.imgListener!=null) Vocabulary.this.imgListener.error(e);
}
});
......@@ -263,9 +267,9 @@ public class Vocabulary implements Iterable<Picto> {
ImgDownloader downloader = new ImgDownloader(PCBcontext.getContext(), imgListener,source);
downloader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imgs);
loadPicto(pic);
addPicto(pic);
PCBcontext.getPcbdb().addPicto(pic);
PCBcontext.getPcbdb().savePicto(pic);
}
public void setImgDownloaderListener(ImgDownloader.iImgDownloaderListener listener) {
......@@ -288,6 +292,15 @@ public class Vocabulary implements Iterable<Picto> {
return index;
}
private Picto find_picto(int pic_cat, int row,int column) {
LinkedList<Picto> pictos_cat=this.pictos.get(pic_cat);
Picto picto=null;
for (int i=0; i<pictos_cat.size() && picto==null; i++)
if (pictos_cat.get(i).get_column()==column && pictos_cat.get(i).get_row()==row) picto=pictos_cat.get(i);
return picto;
}
public Picto get_picto(int pic_cat, int pic_id) {
Picto picto=null;
LinkedList<Picto> pictos_cat=this.pictos.get(pic_cat);
......@@ -334,8 +347,7 @@ public class Vocabulary implements Iterable<Picto> {
* @param picto
* @seealso com.yottacode.pictogram.dao.PCBDBHelper.getStudentVocabulary
*/
public void loadPicto(Picto picto) {
public void addPicto(Picto picto) {
LinkedList<Picto> pictos_cat;
if (this.pictos.containsKey(picto.get_category()))
pictos_cat = pictos.get(picto.get_category());
......@@ -387,15 +399,24 @@ public class Vocabulary implements Iterable<Picto> {
/*
* 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];
public void 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) {
Picto prev_picto=find_picto(cat, coord_x,coord_y); //¿estamos reemplazanddo un picto que ya existe?
if (prev_picto!=null) { //El picto ya existe
removePicto(prev_picto.get_category(),prev_picto.get_id()); //borramos el picto local actual
Log.i(LOG_TAG,"Picto "+exp+" position is filled with "+prev_picto.get_translation()+"(id "+prev_picto.get_id()+"). Previous local picto "+prev_picto.get_id()+" is deleted.");
}
int id=PCBcontext.getDevice().getNextLocalPictoID();
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 ImgDownloader.iImgDownloaderListener() {
final Picto picto = new Picto(id, url, exp, cat, coord_x, coord_y, free_category_coord_x, free_category_coord_y, prev_picto!=null ? prev_picto.get_stupicto_id() : Picto.STUPICTO_NULL);
addPicto(picto, ImgDownloader.tsource.local, new ImgDownloader.iImgDownloaderListener() {
@Override
public void loadComplete() {
listener.saved(picto[0]);
listener.saved(picto);
PCBcontext.getActionLog().log(new VocabularyAction(VocabularyAction.ADD, picto));
}
@Override
public void loadImg(Img image) {
......@@ -407,12 +428,10 @@ public class Vocabulary implements Iterable<Picto> {
}
});
} 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];
}
/**
......
......@@ -39,15 +39,16 @@ public class StudentTalk implements Emitter.Listener {
Log.i(this.getClass().getName(), "raw Received message " +msg.toString());
int id=msg.getInt("id");
String username=msg.getString("username");
String name=msg.getString("name");
String surname=msg.getString("surname");
String gender=msg.getString("gender");
String pic=msg.getString("pic");
String lang=msg.getString("lang");
String attributes=msg.getString("attributes");
User user=PCBcontext.getPcbdb().getCurrentUser();
User updatedUser=new User(id, username, user.get_pwd_stu(), username, surname, pic, gender, lang, attributes,
User updatedUser=new User(id, username, user.get_pwd_stu(), name, surname, pic, gender, lang, attributes,
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());
user.get_lang_sup(), user.get_tts_engine_sup(), user.get_office());
Log.i(this.getClass().getName(), "Attributes" +attributes+" listeners:"+listeners.length);
for (iStudentListener listener: listeners)
listener.change(updatedUser);
......
......@@ -45,6 +45,7 @@ public class VocabularyTalk implements Emitter.Listener {
Log.i(LOG_TAG, "raw Received message " +msg.toString());
String action = msg.getString(param_action).toLowerCase();
JSONObject stu_picto= msg.getJSONObject(param_attributes).getJSONObject(param_stu_picto);
int stupicto_id=stu_picto.getInt(param_picto_id);
JSONObject attrs_stu_picto = stu_picto.optJSONObject(param_attributes);
JSONObject picto_stupicto = stu_picto.optJSONObject(param_picto);
int picto_id = picto_stupicto.getInt(param_picto_id);
......
......@@ -8,6 +8,7 @@ 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.DeviceHelper;
import com.yottacode.pictogram.dao.PCBDBHelper;
import com.yottacode.pictogram.dao.User;
import com.yottacode.pictogram.grammar.Vocabulary;
......@@ -27,6 +28,7 @@ public final class PCBcontext {
private static ActionLog actionLog;
private static boolean init=false;
private static StudentTalk studentTalk;
private static Context activityContext;
/**
......@@ -37,7 +39,8 @@ public final class PCBcontext {
if (!init) {
init = true;
context = c;
device = new Device(c, null, 1);
device = new Device(c, null, DeviceHelper.getDBVersion(context));
activityContext=null;
SSLDummyContext.init(context.getResources().getBoolean(R.bool.ssl_connect));
service = new NetService(context.getResources().getInteger(R.integer.netservice_timing),listener);
wrapper = new RestapiWrapper(context.getResources().getString(R.string.server), null, service);
......@@ -62,9 +65,9 @@ public final class PCBcontext {
throw new java.lang.AssertionError("init must be called once previously ");
}
Log.i(PCBcontext.class.getCanonicalName(), "User set at student " + student.get_name_stu());
Log.i(PCBcontext.class.getCanonicalName(), "User set at student " + student.get_name_stu()+ (student.is_supervisor() ? " with supervisor "+student.get_name_sup():"."));
wrapper.setToken(token);
pcbdb = new PCBDBHelper(null, 1, student);
pcbdb = new PCBDBHelper(null, 2, student);
pcbdb.user_online(token!=null);
room = new Room();
actionLog = new ActionLog();
......@@ -225,4 +228,12 @@ public final class PCBcontext {
}
return actionLog;
}
public static void setActivityContext(Context context) {
PCBcontext.activityContext = context;
}
public static Context getActivityContext() {
return activityContext;
}
}
package com.yottacode.tools;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Point;
import android.util.Log;
import android.view.Display;
/**
* Created by Fernando on 01/04/2016.
......@@ -35,4 +38,10 @@ public class GUITools {
public static void show_alert(Context context, int resource_msg, String additional_msg) {
show_alert(context, resource_msg, additional_msg,null);
}
public static Point getScreenSize(Activity context){
Display display = context.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size;
}
}
PRAGMA foreign_keys = ON
;--
DROP TABLE IF EXISTS params
;--
CREATE TABLE params (
key TEXT(12) CHECK (key in ('serial','deviceID', 'last__stu_id', 'last__sup_id','token', 'keyword')) UNIQUE,
value TEXT(40) NOT NULL
......@@ -10,6 +13,9 @@ value TEXT(40) NOT NULL
insert into params values("keyword", "1234")
;--
DROP TABLE IF EXISTS picto
;--
CREATE TABLE picto (
id INTEGER PRIMARY KEY,
url VARCHAR(250) NOT NULL,
......@@ -17,6 +23,9 @@ translation VARCHAR(60) NOT NULL
)
;--
DROP TABLE IF EXISTS student
;--
CREATE TABLE student (
id INTEGER PRIMARY KEY,
nickname TEXT(40) NOT NULL,
......@@ -30,6 +39,8 @@ attributes TEXT(1024) NULL
)
;--
DROP TABLE IF EXISTS supervisor
;--
CREATE TABLE supervisor (
id INTEGER PRIMARY KEY,
email TEXT(140) NOT NULL,
......@@ -39,11 +50,15 @@ surname TEXT(60) NOT NULL,
url_img VARCHAR(250) NOT NULL,
gender TEXT(1) NOT NULL CHECK (gender IN ('M','F')),
lang TEXT(5) NOT NULL CHECK (lang in ('en-gb','en-us','es-es')),
tts_engine TEXT(100) NULL
tts_engine TEXT(100) NULL,
office TEXT(140) NULL
)
;--
insert into supervisor values(-1,"","","","","","M","es-es",NULL)
insert into supervisor values(-1,"","","","","","M","es-es",NULL,NULL)
;--
DROP TABLE IF EXISTS users
;--
CREATE TABLE users (
......@@ -53,32 +68,50 @@ constraint ck_users UNIQUE(id_stu,id_sup)
)
;--
DROP TABLE IF EXISTS collection
;--
CREATE TABLE collection (
id_stu INTEGER NOT NULL REFERENCES student ON DELETE CASCADE,
id_picto INTEGER NOT NULL REFERENCES picto ON DELETE CASCADE,
attributes VARCHAR(1024),
constraint ck_collection UNIQUE(id_stu,id_picto)
constraint ck_collection UNIQUE(id_stu,id_picto,attributes)
)
;--
DROP INDEX IF EXISTS idx_col_stu
;--
CREATE INDEX idx_col_stu ON collection(id_stu)
;--
DROP INDEX IF EXISTS idx_col_pic
;--
CREATE INDEX idx_col_pic ON collection(id_picto)
;--
DROP TABLE IF EXISTS action
;--
CREATE TABLE action (
json_action TEXT(512)
)
;--
DROP TABLE IF EXISTS deprecated_images
;--
CREATE TABLE deprecated_images (
id INTEGER NOT NULL PRIMARY KEY,
type TEXT(5) NOT NULL CHECK (type in ('stu','sup','pic'))
)
;--
DROP VIEW IF EXISTS users_detail
;--
CREATE VIEW users_detail AS
SELECT
id_stu,
......@@ -98,19 +131,26 @@ CREATE VIEW users_detail AS
b.url_img url_img_sup,
b.gender gender_sup,
b.lang lang_sup,
b.tts_engine tts_engine_sup
b.tts_engine tts_engine_sup,
b.office office_sup
FROM
student a, supervisor b, users
WHERE
a.id=users.id_stu AND b.id=users.id_sup
;--
DROP VIEW IF EXISTS collection_detail
;--
CREATE VIEW collection_detail AS
SELECT id_stu, id_picto, b.url, translation,attributes
FROM collection a, picto b
WHERE a.id_picto=b.id
;--
DROP TRIGGER IF EXISTS trg_delete_user_stu
;--
--If a student is deleted and there is no longer any user with such a student, then it should be deleted
CREATE TRIGGER trg_delete_user_stu
AFTER DELETE ON users
......@@ -121,6 +161,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_delete_user_sup
;--
--If a supervisor is deleted and there is no longer any user with such a supervisor, then it should be deleted
CREATE TRIGGER trg_delete_user_sup
AFTER DELETE ON users
......@@ -131,6 +174,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_delete_stu
;--
--If a student is deleted, then the img should be deleted
CREATE TRIGGER trg_delete_stu
AFTER DELETE ON student
......@@ -140,6 +186,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_insert_stu
;--
--If a student is inserted, then the img should be preserved
CREATE TRIGGER trg_insert_stu
AFTER INSERT ON student
......@@ -149,6 +198,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_delete_sup
;--
--If a supervisor is deleted, then the img should be deleted
CREATE TRIGGER trg_delete_sup
AFTER DELETE ON supervisor
......@@ -158,6 +210,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_insert_sup
;--
--If a supervisor is inserted, then the img should be preserved
CREATE TRIGGER trg_insert_sup
AFTER INSERT ON supervisor
......@@ -167,6 +222,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_delete_pic
;--
--If a picto is deleted, then the img should be deleted
CREATE TRIGGER trg_delete_pic
AFTER DELETE ON picto
......@@ -176,6 +234,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_insert_pic
;--
--If a picto is inserted, then the img should be preserved
CREATE TRIGGER trg_insert_pic
AFTER INSERT ON picto
......@@ -185,6 +246,10 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_delete_users_detail
;--
CREATE TRIGGER trg_delete_users_detail
INSTEAD OF DELETE ON users_detail
FOR EACH ROW
......@@ -193,17 +258,24 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_insert_users_detail
;--
CREATE TRIGGER trg_insert_users_detail
INSTEAD OF INSERT ON users_detail
FOR EACH ROW
WHEN NEW.pwd_stu IS NOT NULL
BEGIN
INSERT OR REPLACE INTO student VALUES (NEW.id_stu, NEW.nickname_stu, NEW.pwd_stu, NEW.name_stu, NEW.surname_stu, NEW.url_img_stu, NEW.gender_stu, NEW.lang_stu, NEW.attributes_stu);
INSERT OR REPLACE INTO supervisor VALUES (NEW.id_sup, NEW.email_sup, NEW.pwd_sup, NEW.name_sup, NEW.surname_sup, NEW.url_img_sup, NEW.gender_sup, NEW.lang_sup, NEW.tts_engine_sup);
INSERT OR REPLACE INTO supervisor VALUES (NEW.id_sup, NEW.email_sup, NEW.pwd_sup, NEW.name_sup, NEW.surname_sup, NEW.url_img_sup, NEW.gender_sup, NEW.lang_sup, NEW.tts_engine_sup, NEW.office_sup);
INSERT OR REPLACE INTO users VALUES (NEW.id_stu,NEW.id_sup);
END
;--
DROP TRIGGER IF EXISTS trg_delete_trg_insert_users_detail_as_sup
;--
CREATE TRIGGER trg_insert_users_detail_as_sup
INSTEAD OF INSERT ON users_detail
FOR EACH ROW
......@@ -211,21 +283,38 @@ WHEN NEW.pwd_stu IS NULL
BEGIN
INSERT OR REPLACE INTO student(id,nickname, pwd, name, surname, url_img, gender, lang, attributes)
VALUES (NEW.id_stu, NEW.nickname_stu, (SELECT pwd FROM student WHERE id=NEW.id_stu), NEW.name_stu, NEW.surname_stu, NEW.url_img_stu, NEW.gender_stu, NEW.lang_stu, NEW.attributes_stu);
INSERT OR REPLACE INTO supervisor VALUES (NEW.id_sup, NEW.email_sup, NEW.pwd_sup, NEW.name_sup, NEW.surname_sup, NEW.url_img_sup, NEW.gender_sup, NEW.lang_sup, NEW.tts_engine_sup);
INSERT OR REPLACE INTO supervisor VALUES (NEW.id_sup, NEW.email_sup, NEW.pwd_sup, NEW.name_sup, NEW.surname_sup, NEW.url_img_sup, NEW.gender_sup, NEW.lang_sup, NEW.tts_engine_sup, NEW.office_sup);
INSERT OR REPLACE INTO users VALUES (NEW.id_stu,NEW.id_sup);
END
;--
DROP TRIGGER IF EXISTS trg_update_users_detail
;--
CREATE TRIGGER trg_update_users_detail
INSTEAD OF UPDATE ON users_detail
FOR EACH ROW
BEGIN
UPDATE student SET nickname=NEW.nickname_stu, pwd=NEW.pwd_stu,name=NEW.name_stu, surname=NEW.surname_stu, url_img=NEW.url_img_stu, gender=NEW.gender_stu, lang=NEW.lang_stu, attributes=NEW.attributes_stu WHERE id=NEW.id_stu;
UPDATE supervisor SET email=NEW.email_sup, pwd=NEW.pwd_sup, name=NEW.name_sup, surname=NEW.surname_sup, url_img=NEW.url_img_sup, gender=NEW.gender_sup, lang=NEW.lang_sup, tts_engine=NEW.tts_engine_sup WHERE id=NEW.id_sup;
UPDATE supervisor SET email=NEW.email_sup, pwd=NEW.pwd_sup, name=NEW.name_sup, surname=NEW.surname_sup, url_img=NEW.url_img_sup, gender=NEW.gender_sup, lang=NEW.lang_sup, tts_engine=NEW.tts_engine_sup, office=NEW.office_sup WHERE id=NEW.id_sup;
END
;--
CREATE TRIGGER trg_update_supervisor
BEFORE UPDATE ON supervisor
FOR EACH ROW
WHEN NEW.id=-1
BEGIN
SELECT RAISE( ABORT,
'Null supervisor -1 is not updatable'
);
END
;--
DROP TRIGGER IF EXISTS trg_delete_collection
;--
--If a collection is deleted and there is no longer any collection with such a picto, then the picto should be deleted
CREATE TRIGGER trg_delete_collection
AFTER DELETE ON collection
......@@ -236,6 +325,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_delete_collection_detail
;--
CREATE TRIGGER trg_delete_collection_detail
INSTEAD OF DELETE ON collection_detail
FOR EACH ROW
......@@ -244,6 +336,9 @@ BEGIN
END
;--
DROP TRIGGER IF EXISTS trg_insert_collection_detail
;--
CREATE TRIGGER trg_insert_collection_detail
INSTEAD OF INSERT ON collection_detail
FOR EACH ROW
......
......@@ -58,6 +58,7 @@
<string name="notNewCats">Including new categories is not allowed</string>
<string name="upload_error">It cannot be uploaded. It will be tried again later</string>
<string name="upload_ok">It was correctly uploaded</string>
<string name="upload_duplicated">Pictogram is part of the collecion. Please, select a different one</string>
<!--online/offline status-->
<string name="pictogram_offline">Pictogram offline</string>
<string name="pictogram_online">Pictogram online</string>
......@@ -66,8 +67,16 @@
<string name="mirror_mode_off">Mirror mode off</string>
<string name="mirror_mode_on">Mirror mode on</string>
<string name="new_version_title">New version available</string>
<string name="new_version_detail">Please, download and install the new version available at</string>
<!--default tts engine and voice-->
<string name="default_tts_engine">com.google.android.tts</string>
<string name="default_tts_voice_male">en-gb-x-rjs#male_1-local</string>
<string name="default_tts_voice_female">en-gb-x-fis#female_1-local</string>
<string name="crop_TextRequired">Please, introduce a message for the pictogram</string>
<string name="crop">Crop</string>
<string name="titleCropper">New pictogram</string>
<string name="uploadingImage">Uploading image</string>
</resources>
......@@ -58,6 +58,7 @@
<string name="notNewCats">No puede añadir nuevas categorias</string>
<string name="upload_error">Pictograma local no pudo subirse al servidor. Se intentará más tarde</string>
<string name="upload_ok">Pictograma local se subió correctamente al servidor</string>
<string name="upload_duplicated">Pictograma ya presente en la colección. Utilice otra imagen</string>
<!--online/offline status-->
<string name="pictogram_offline">Pictogram offline</string>
<string name="pictogram_online">Pictogram online</string>
......@@ -66,6 +67,8 @@
<string name="mirror_mode_off">Modo espejo desactivado</string>
<string name="mirror_mode_on">Modo espejo activado</string>
<string name="new_version_title">Nueva versión disponible</string>
<string name="new_version_detail">Por favor descargue e instale la nueva versión disponible en</string>
<!--default tts engine and voice-->
<string name="default_tts_engine">com.google.android.tts</string>
<string name="default_tts_voice_male">es-es-x-ana#male_1-local</string>
......
......@@ -61,6 +61,7 @@
<string name="notNewCats">No puede añadir nuevas categorias</string>
<string name="upload_error">No pudo subirse al servidor. Se intentará más adelante</string>
<string name="upload_ok">Se subió correctamente al servidor</string>
<string name="upload_duplicated">Pictograma ya presente en la colección. Utilice otra imagen</string>
<string name="title_activity_img_label">img_label</string>
......@@ -82,6 +83,10 @@
<string name="mirror_mode_off">Modo espejo desactivado</string>
<string name="mirror_mode_on">Modo espejo activado</string>
<!--new app version alertbox-->
<string name="new_version_title">Nueva versión disponible</string>
<string name="new_version_detail">Por favor descargue e instale la nueva versión disponible en</string>
<!--default tts engine and voice-->
<string name="default_tts_engine">com.google.android.tts</string>
<string name="default_tts_voice_male">en-gb-x-rjs#male_1-local</string>
......
/build
/supervisor_tablet.iml
supervisor_tablet.iml
......@@ -19,6 +19,7 @@ android {
versionCode 1
versionName "1.0"
resValue "string","SerialClass","com.yottacode.pictogram.supervisor_tablet.gui.Supervisor_SerialActivity"
resValue "string","apk","pictograms.apk"
// signingConfig signingConfigs.config
}
productFlavors {
......@@ -34,6 +35,9 @@ android {
resValue "string", "server", "https://pre.yottacode.com"
resValue "bool", "ssl_connect", "true"
}
LocalFlavor {
resValue "string", "server", "http://192.168.1.35:1337"
resValue "bool", "ssl_connect", "false" }
}
}
......
/build
/tabletlibrary.iml
\ No newline at end of file
.iml
......@@ -14,7 +14,9 @@ import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.yottacode.pictogram.dao.User;
import com.yottacode.pictogram.tabletlibrary.R;
import com.yottacode.pictogram.tools.PCBcontext;
......@@ -109,7 +111,7 @@ public class PictoAnimation {
public void onAnimationUpdate(ValueAnimator animation) {
int i=(int)animation.getAnimatedValue();
v.setTranslationY(i);
if (i<=255) activity.ttsButton.setImageAlpha(255-i);
if (PCBcontext.getPcbdb().getCurrentUser().delivery()== User.JSON_STUDENT_ATTTRS.delivery.one && i<=255) activity.ttsButton.setImageAlpha(255-i);
}
......@@ -121,12 +123,14 @@ public class PictoAnimation {
public void animateImageView(final PictogramActivity activity, View view, final int position) {
public void animateOnDeleteView(final PictogramActivity activity, View view, final int position) {
if (activity.deleting) return;
View borderlayout=((RelativeLayout)view).getChildAt(0);
View borderlayout=((RelativeLayout)view).getChildAt(0);
final ImageView v=((ImageView)((FrameLayout)borderlayout).getChildAt(0));
final TextView t1=((TextView)((FrameLayout)borderlayout).getChildAt(1));
final TextView t2=((TextView)((FrameLayout)borderlayout).getChildAt(2));
final int orange = ContextCompat.getColor(PCBcontext.getContext(),R.color.red);
......@@ -136,14 +140,19 @@ public class PictoAnimation {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float mul = (Float) animation.getAnimatedValue();
int alphaOrange = PictoAnimation.adjustAlpha(orange, mul);
v.setColorFilter(alphaOrange, PorterDuff.Mode.SRC_ATOP);
v.setBackgroundColor(alphaOrange);
v.setPadding(0,0,0,0);
if (mul == 0.0) {
v.setColorFilter(0);
if (t2.getVisibility()==View.VISIBLE) {
t2.setAlpha(1-mul);
mul=1;
}
int alphaOrange = PictoAnimation.adjustAlpha(orange, mul);
v.setColorFilter(alphaOrange, PorterDuff.Mode.SRC_ATOP);
v.setBackgroundColor(alphaOrange);
v.setPadding(0,0,0,0);
if (mul == 0.0) {
v.setColorFilter(0);
}
if (t1.getVisibility()==View.VISIBLE) t1.setAlpha(1-mul);
}
});
......@@ -151,6 +160,7 @@ public class PictoAnimation {
@Override
public void onAnimationStart(Animator animation) {
if (t2.getVisibility()==View.VISIBLE) v.setBackgroundColor(0);
activity.deleting=true;
}
......@@ -158,8 +168,12 @@ public class PictoAnimation {
public void onAnimationEnd(Animator animation) {
activity.tapeAdapter.deleteItem(position);
activity.tapeAdapter.notifyDataSetChanged();
if (t1.getVisibility()==View.VISIBLE) t1.setAlpha(1);
if (t2.getVisibility()==View.VISIBLE) t2.setAlpha(1);
v.setColorFilter(0);
v.setBackgroundColor(ContextCompat.getColor(PCBcontext.getContext(),R.color.picto_default_background));
v.setBackgroundColor(ContextCompat.getColor(PCBcontext.getContext(), R.color.picto_default_background));
activity.deleting=false;
if (activity.tapeAdapter.getCount()==0 && activity.tape_delivered) {
activity.showOnlyTape(false);
......
......@@ -9,6 +9,7 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.dao.User;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.pictogram.tts.TTSHelper;
......@@ -66,7 +67,7 @@ public class TapeAdapter extends BaseAdapter {
// ELIMINAR TODOS LOS ITEMS DEL ADAPTADOR
public void endPlay() {
if (PCBcontext.getPcbdb().getCurrentUser().delete_tape_after_delivery())
if (PCBcontext.getPcbdb().getCurrentUser().delivery()==User.JSON_STUDENT_ATTTRS.delivery.clean)
clear();
play = false;
}
......
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.tabletlibrary.R;
import com.yottacode.pictogram.tools.PCBcontext;
import java.io.ByteArrayOutputStream;
/**
* Created by German on 06/02/2017.
*/
public class EditPictoActivity extends Activity {
// Private Constants ///////////////////////////////////////////////////////////////////////////
public static final int EDIT_PICTO_REQUEST = 2288;
public static final String PATH = "pathImage";
public static final String IMAGE_PICTO = "imagePicto";
private static final String LOG_PATH = EditPictoActivity.class.getName();
// Activity Methods ////////////////////////////////////////////////////////////////////////////
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.edit_picto_layout);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
LinearLayout ll = (LinearLayout)findViewById(R.id.image_layout);
Log.i("DETALLES", "Tam menu imagen: "+ ll.getLayoutParams().width);
ll.getLayoutParams().width = (int) (metrics.widthPixels * 0.75 ) - (int) getResources().getDimension(R.dimen.activity_vertical_margin) *2;
Log.i("DETALLES", "Tam menu imagen: "+ ll.getLayoutParams().width);
ll.requestLayout();
FrameLayout fl = (FrameLayout)findViewById(R.id.legend_menu);
Log.i("DETALLES", "Tam menu leyenda: "+ fl.getLayoutParams().width);
fl.getLayoutParams().width = (int) (metrics.widthPixels * 0.25) - (int) getResources().getDimension(R.dimen.activity_vertical_margin);
Log.i("DETALLES", "Tam menu leyenda: "+ fl.getLayoutParams().width);
fl.requestLayout();
// Initialize Views.
final CropImageView cropImageView = (CropImageView) findViewById(R.id.CropImageView);
final EditText legend = (EditText) findViewById(R.id.edtLegend);
final Button okButton = (Button) findViewById(R.id.okButton);
final Button cancelButton = (Button) findViewById(R.id.cancelButton);
String transcription=getIntent().getExtras().getString(Picto.JSON_ATTTRS.EXPRESSION);
cropImageView.setFixedAspectRatio(true);
cropImageView.setGuidelines(2);
cropImageView.setAspectRatio(1,1);
if(transcription != null && transcription.length()>0)
legend.setText(transcription);
legend.setHorizontallyScrolling(false);
legend.setMaxLines(1);
legend.setSingleLine(true);
//Obtener imagen del intent
byte[] byteArray = getIntent().getByteArrayExtra(EditPictoActivity.IMAGE_PICTO);
Bitmap imagePicto = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
cropImageView.setImageBitmap(imagePicto);
cropImageView.setMaxWidth(imagePicto.getWidth());
cropImageView.setMaxHeight(imagePicto.getHeight());
if (transcription!=null && transcription.length()>0)
legend.setText(transcription);
//Gestion de botones
okButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (legend.getText().toString().trim().length()>0) {
final Bitmap croppedImage = cropImageView.getCroppedImage();
//Escalar imagen
Bitmap finalImage = croppedImage.createScaledBitmap(croppedImage, 96, 96, true);
//Guardar imagen en galeria y obtener la ruta
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
finalImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String title = getResources().getString(R.string.app_name) + ' ' + legend.getText().toString();
String path = MediaStore.Images.Media.insertImage(PCBcontext.getContext().getContentResolver(), finalImage, title, null);
path = getRealPathFromURI(Uri.parse(path));
Log.i(LOG_PATH, " New image " + title + " saved at " + path);
Intent intent = getIntent(); //Mandar a pictogram activity el path y el texto de la imagen
intent.putExtra(PATH, path);
intent.putExtra(Picto.JSON_ATTTRS.EXPRESSION, legend.getText().toString());
cropImageView.setImageBitmap(finalImage);
setResult(RESULT_OK, intent);
finish(); //Termina la actividad de editar
}
else Toast.makeText(getBaseContext(),R.string.crop_TextRequired, Toast.LENGTH_LONG).show();
}
});
//Si cancela, volver a pictogram activity
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
setResult(RESULT_CANCELED,getIntent());
}
});
}
public String getRealPathFromURI(Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = { MediaStore.Images.Media.DATA };
cursor = this.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge;
/**
* Simple class to hold a pair of Edges.
*/
public class EdgePair {
// Member Variables ////////////////////////////////////////////////////////
public Edge primary;
public Edge secondary;
// Constructor /////////////////////////////////////////////////////////////
public EdgePair(Edge edge1, Edge edge2) {
primary = edge1;
secondary = edge2;
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.handle;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.Edge;
/**
* HandleHelper class to handle the center handle.
*/
class CenterHandleHelper extends HandleHelper {
// Constructor /////////////////////////////////////////////////////////////////////////////////
CenterHandleHelper() {
super(null, null);
}
// HandleHelper Methods ////////////////////////////////////////////////////////////////////////
@Override
void updateCropWindow(float x,
float y,
@NonNull RectF imageRect,
float snapRadius) {
float left = Edge.LEFT.getCoordinate();
float top = Edge.TOP.getCoordinate();
float right = Edge.RIGHT.getCoordinate();
float bottom = Edge.BOTTOM.getCoordinate();
final float currentCenterX = (left + right) / 2;
final float currentCenterY = (top + bottom) / 2;
final float offsetX = x - currentCenterX;
final float offsetY = y - currentCenterY;
// Adjust the crop window.
Edge.LEFT.offset(offsetX);
Edge.TOP.offset(offsetY);
Edge.RIGHT.offset(offsetX);
Edge.BOTTOM.offset(offsetY);
// Check if we have gone out of bounds on the sides, and fix.
if (Edge.LEFT.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.LEFT.snapToRect(imageRect);
Edge.RIGHT.offset(offset);
} else if (Edge.RIGHT.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.RIGHT.snapToRect(imageRect);
Edge.LEFT.offset(offset);
}
// Check if we have gone out of bounds on the top or bottom, and fix.
if (Edge.TOP.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.TOP.snapToRect(imageRect);
Edge.BOTTOM.offset(offset);
} else if (Edge.BOTTOM.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.BOTTOM.snapToRect(imageRect);
Edge.TOP.offset(offset);
}
}
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
@NonNull RectF imageRect,
float snapRadius) {
updateCropWindow(x, y, imageRect, snapRadius);
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.handle;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.Edge;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.EdgePair;
/**
* HandleHelper class to handle corner Handles (i.e. top-left, top-right, bottom-left, and
* bottom-right handles).
*/
class CornerHandleHelper extends HandleHelper {
// Constructor /////////////////////////////////////////////////////////////////////////////////
CornerHandleHelper(Edge horizontalEdge, Edge verticalEdge) {
super(horizontalEdge, verticalEdge);
}
// HandleHelper Methods ////////////////////////////////////////////////////////////////////////
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
@NonNull RectF imageRect,
float snapRadius) {
final EdgePair activeEdges = getActiveEdges(x, y, targetAspectRatio);
final Edge primaryEdge = activeEdges.primary;
final Edge secondaryEdge = activeEdges.secondary;
primaryEdge.adjustCoordinate(x, y, imageRect, snapRadius, targetAspectRatio);
secondaryEdge.adjustCoordinate(targetAspectRatio);
if (secondaryEdge.isOutsideMargin(imageRect, snapRadius)) {
secondaryEdge.snapToRect(imageRect);
primaryEdge.adjustCoordinate(targetAspectRatio);
}
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.handle;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.Edge;
/**
* Enum representing a pressable, draggable Handle on the crop window.
*/
public enum Handle {
TOP_LEFT(new CornerHandleHelper(Edge.TOP, Edge.LEFT)),
TOP_RIGHT(new CornerHandleHelper(Edge.TOP, Edge.RIGHT)),
BOTTOM_LEFT(new CornerHandleHelper(Edge.BOTTOM, Edge.LEFT)),
BOTTOM_RIGHT(new CornerHandleHelper(Edge.BOTTOM, Edge.RIGHT)),
LEFT(new VerticalHandleHelper(Edge.LEFT)),
TOP(new HorizontalHandleHelper(Edge.TOP)),
RIGHT(new VerticalHandleHelper(Edge.RIGHT)),
BOTTOM(new HorizontalHandleHelper(Edge.BOTTOM)),
CENTER(new CenterHandleHelper());
// Member Variables ////////////////////////////////////////////////////////////////////////////
private HandleHelper mHelper;
// Constructors ////////////////////////////////////////////////////////////////////////////////
Handle(HandleHelper helper) {
mHelper = helper;
}
// Public Methods //////////////////////////////////////////////////////////
public void updateCropWindow(float x,
float y,
@NonNull RectF imageRect,
float snapRadius) {
mHelper.updateCropWindow(x, y, imageRect, snapRadius);
}
public void updateCropWindow(float x,
float y,
float targetAspectRatio,
@NonNull RectF imageRect,
float snapRadius) {
mHelper.updateCropWindow(x, y, targetAspectRatio, imageRect, snapRadius);
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.handle;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.Edge;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.EdgePair;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.util.AspectRatioUtil;
/**
* Abstract helper class to handle operations on a crop window Handle.
*/
abstract class HandleHelper {
// Member Variables ////////////////////////////////////////////////////////
private static final float UNFIXED_ASPECT_RATIO_CONSTANT = 1;
private Edge mHorizontalEdge;
private Edge mVerticalEdge;
// Save the Pair object as a member variable to avoid having to instantiate
// a new Object every time getActiveEdges() is called.
private EdgePair mActiveEdges;
// Constructor /////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*
* @param horizontalEdge the horizontal edge associated with this handle; may be null
* @param verticalEdge the vertical edge associated with this handle; may be null
*/
HandleHelper(Edge horizontalEdge, Edge verticalEdge) {
mHorizontalEdge = horizontalEdge;
mVerticalEdge = verticalEdge;
mActiveEdges = new EdgePair(mHorizontalEdge, mVerticalEdge);
}
// Package-Private Methods /////////////////////////////////////////////////////////////////////
/**
* Updates the crop window by directly setting the Edge coordinates.
*
* @param x the new x-coordinate of this handle
* @param y the new y-coordinate of this handle
* @param imageRect the bounding rectangle of the image
* @param snapRadius the maximum distance (in pixels) at which the crop window should snap to
* the image
*/
void updateCropWindow(float x,
float y,
@NonNull RectF imageRect,
float snapRadius) {
final EdgePair activeEdges = getActiveEdges();
final Edge primaryEdge = activeEdges.primary;
final Edge secondaryEdge = activeEdges.secondary;
if (primaryEdge != null)
primaryEdge.adjustCoordinate(x, y, imageRect, snapRadius, UNFIXED_ASPECT_RATIO_CONSTANT);
if (secondaryEdge != null)
secondaryEdge.adjustCoordinate(x, y, imageRect, snapRadius, UNFIXED_ASPECT_RATIO_CONSTANT);
}
/**
* Updates the crop window by directly setting the Edge coordinates; this method maintains a
* given aspect ratio.
*
* @param x the new x-coordinate of this handle
* @param y the new y-coordinate of this handle
* @param targetAspectRatio the aspect ratio to maintain
* @param imageRect the bounding rectangle of the image
* @param snapRadius the maximum distance (in pixels) at which the crop window should
* snap to the image
*/
abstract void updateCropWindow(float x,
float y,
float targetAspectRatio,
@NonNull RectF imageRect,
float snapRadius);
/**
* Gets the Edges associated with this handle (i.e. the Edges that should be moved when this
* handle is dragged). This is used when we are not maintaining the aspect ratio.
*
* @return the active edge as a pair (the pair may contain null values for the
* <code>primary</code>, <code>secondary</code> or both fields)
*/
EdgePair getActiveEdges() {
return mActiveEdges;
}
/**
* Gets the Edges associated with this handle as an ordered Pair. The <code>primary</code> Edge
* in the pair is the determining side. This method is used when we need to maintain the aspect
* ratio.
*
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param targetAspectRatio the aspect ratio that we are maintaining
*
* @return the active edges as an ordered pair
*/
EdgePair getActiveEdges(float x, float y, float targetAspectRatio) {
// Calculate the aspect ratio if this handle were dragged to the given x-y coordinate.
final float potentialAspectRatio = getAspectRatio(x, y);
// If the touched point is wider than the aspect ratio, then x is the determining side. Else, y is the determining side.
if (potentialAspectRatio > targetAspectRatio) {
mActiveEdges.primary = mVerticalEdge;
mActiveEdges.secondary = mHorizontalEdge;
} else {
mActiveEdges.primary = mHorizontalEdge;
mActiveEdges.secondary = mVerticalEdge;
}
return mActiveEdges;
}
// Private Methods /////////////////////////////////////////////////////////////////////////////
/**
* Gets the aspect ratio of the resulting crop window if this handle were dragged to the given
* point.
*
* @param x the x-coordinate
* @param y the y-coordinate
*
* @return the aspect ratio
*/
private float getAspectRatio(float x, float y) {
// Replace the active edge coordinate with the given touch coordinate.
final float left = (mVerticalEdge == Edge.LEFT) ? x : Edge.LEFT.getCoordinate();
final float top = (mHorizontalEdge == Edge.TOP) ? y : Edge.TOP.getCoordinate();
final float right = (mVerticalEdge == Edge.RIGHT) ? x : Edge.RIGHT.getCoordinate();
final float bottom = (mHorizontalEdge == Edge.BOTTOM) ? y : Edge.BOTTOM.getCoordinate();
return AspectRatioUtil.calculateAspectRatio(left, top, right, bottom);
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.handle;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.Edge;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.util.AspectRatioUtil;
/**
* Handle helper class to handle horizontal handles (i.e. top and bottom handles).
*/
class HorizontalHandleHelper extends HandleHelper {
// Member Variables ////////////////////////////////////////////////////////////////////////////
private Edge mEdge;
// Constructor /////////////////////////////////////////////////////////////////////////////////
HorizontalHandleHelper(Edge edge) {
super(edge, null);
mEdge = edge;
}
// HandleHelper Methods ////////////////////////////////////////////////////////////////////////
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
@NonNull RectF imageRect,
float snapRadius) {
// Adjust this Edge accordingly.
mEdge.adjustCoordinate(x, y, imageRect, snapRadius, targetAspectRatio);
float left = Edge.LEFT.getCoordinate();
float right = Edge.RIGHT.getCoordinate();
// After this Edge is moved, our crop window is now out of proportion.
final float targetWidth = AspectRatioUtil.calculateWidth(Edge.getHeight(), targetAspectRatio);
// Adjust the crop window so that it maintains the given aspect ratio by
// moving the adjacent edges symmetrically in or out.
final float difference = targetWidth - Edge.getWidth();
final float halfDifference = difference / 2;
left -= halfDifference;
right += halfDifference;
Edge.LEFT.setCoordinate(left);
Edge.RIGHT.setCoordinate(right);
// Check if we have gone out of bounds on the sides, and fix.
if (Edge.LEFT.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.LEFT, imageRect, targetAspectRatio)) {
final float offset = Edge.LEFT.snapToRect(imageRect);
Edge.RIGHT.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
if (Edge.RIGHT.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.RIGHT, imageRect, targetAspectRatio)) {
final float offset = Edge.RIGHT.snapToRect(imageRect);
Edge.LEFT.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.handle;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.edge.Edge;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.util.AspectRatioUtil;
/**
* HandleHelper class to handle vertical handles (i.e. left and right handles).
*/
class VerticalHandleHelper extends HandleHelper {
// Member Variables ////////////////////////////////////////////////////////////////////////////
private Edge mEdge;
// Constructor /////////////////////////////////////////////////////////////////////////////////
VerticalHandleHelper(Edge edge) {
super(null, edge);
mEdge = edge;
}
// HandleHelper Methods ////////////////////////////////////////////////////////////////////////
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
@NonNull RectF imageRect,
float snapRadius) {
// Adjust this Edge accordingly.
mEdge.adjustCoordinate(x, y, imageRect, snapRadius, targetAspectRatio);
float top = Edge.TOP.getCoordinate();
float bottom = Edge.BOTTOM.getCoordinate();
// After this Edge is moved, our crop window is now out of proportion.
final float targetHeight = AspectRatioUtil.calculateHeight(Edge.getWidth(), targetAspectRatio);
// Adjust the crop window so that it maintains the given aspect ratio by
// moving the adjacent edges symmetrically in or out.
final float difference = targetHeight - Edge.getHeight();
final float halfDifference = difference / 2;
top -= halfDifference;
bottom += halfDifference;
Edge.TOP.setCoordinate(top);
Edge.BOTTOM.setCoordinate(bottom);
// Check if we have gone out of bounds on the top or bottom, and fix.
if (Edge.TOP.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.TOP, imageRect, targetAspectRatio)) {
final float offset = Edge.TOP.snapToRect(imageRect);
Edge.BOTTOM.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
if (Edge.BOTTOM.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.BOTTOM, imageRect, targetAspectRatio)) {
final float offset = Edge.BOTTOM.snapToRect(imageRect);
Edge.TOP.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.util;
import android.graphics.RectF;
import android.support.annotation.NonNull;
/**
* Utility class for handling calculations involving a fixed aspect ratio.
*/
public class AspectRatioUtil {
/**
* Calculates the aspect ratio given a rectangle.
*/
public static float calculateAspectRatio(float left, float top, float right, float bottom) {
final float width = right - left;
final float height = bottom - top;
return width / height;
}
/**
* Calculates the aspect ratio given a rectangle.
*/
public static float calculateAspectRatio(@NonNull RectF rect) {
return rect.width() / rect.height();
}
/**
* Calculates the x-coordinate of the left edge given the other sides of the rectangle and an
* aspect ratio.
*/
public static float calculateLeft(float top, float right, float bottom, float targetAspectRatio) {
final float height = bottom - top;
// targetAspectRatio = width / height
// width = targetAspectRatio * height
// right - left = targetAspectRatio * height
return right - (targetAspectRatio * height);
}
/**
* Calculates the y-coordinate of the top edge given the other sides of the rectangle and an
* aspect ratio.
*/
public static float calculateTop(float left, float right, float bottom, float targetAspectRatio) {
final float width = right - left;
// targetAspectRatio = width / height
// width = targetAspectRatio * height
// height = width / targetAspectRatio
// bottom - top = width / targetAspectRatio
return bottom - (width / targetAspectRatio);
}
/**
* Calculates the x-coordinate of the right edge given the other sides of the rectangle and an
* aspect ratio.
*/
public static float calculateRight(float left, float top, float bottom, float targetAspectRatio) {
final float height = bottom - top;
// targetAspectRatio = width / height
// width = targetAspectRatio * height
// right - left = targetAspectRatio * height
return (targetAspectRatio * height) + left;
}
/**
* Calculates the y-coordinate of the bottom edge given the other sides of the rectangle and an
* aspect ratio.
*/
public static float calculateBottom(float left, float top, float right, float targetAspectRatio) {
final float width = right - left;
// targetAspectRatio = width / height
// width = targetAspectRatio * height
// height = width / targetAspectRatio
// bottom - top = width / targetAspectRatio
return (width / targetAspectRatio) + top;
}
/**
* Calculates the width of a rectangle given the top and bottom edges and an aspect ratio.
*/
public static float calculateWidth(float height, float targetAspectRatio) {
return targetAspectRatio * height;
}
/**
* Calculates the height of a rectangle given the left and right edges and an aspect ratio.
*/
public static float calculateHeight(float width, float targetAspectRatio) {
return width / targetAspectRatio;
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.util;
import android.graphics.PointF;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.cropwindow.handle.Handle;
/**
* Utility class to perform basic operations with Handles.
*/
public class HandleUtil {
// Public Methods //////////////////////////////////////////////////////////////////////////////
/**
* Determines which, if any, of the handles are pressed given the touch coordinates, the
* bounding box, and the touch radius.
*
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param left the x-coordinate of the left bound
* @param top the y-coordinate of the top bound
* @param right the x-coordinate of the right bound
* @param bottom the y-coordinate of the bottom bound
* @param targetRadius the target radius in pixels
*
* @return the Handle that was pressed; null if no Handle was pressed
*/
public static Handle getPressedHandle(float x,
float y,
float left,
float top,
float right,
float bottom,
float targetRadius) {
// Find the closest corner handle to the touch point.
// If the touch point is in the target zone of this closest handle, then this is the pressed handle.
// Else, check if any of the edges are in the target zone of the touch point.
// Else, check if the touch point is within the crop window bounds; if so, then choose the center handle.
Handle closestHandle = null;
float closestDistance = Float.POSITIVE_INFINITY;
final float distanceToTopLeft = MathUtil.calculateDistance(x, y, left, top);
if (distanceToTopLeft < closestDistance) {
closestDistance = distanceToTopLeft;
closestHandle = Handle.TOP_LEFT;
}
final float distanceToTopRight = MathUtil.calculateDistance(x, y, right, top);
if (distanceToTopRight < closestDistance) {
closestDistance = distanceToTopRight;
closestHandle = Handle.TOP_RIGHT;
}
final float distanceToBottomLeft = MathUtil.calculateDistance(x, y, left, bottom);
if (distanceToBottomLeft < closestDistance) {
closestDistance = distanceToBottomLeft;
closestHandle = Handle.BOTTOM_LEFT;
}
final float distanceToBottomRight = MathUtil.calculateDistance(x, y, right, bottom);
if (distanceToBottomRight < closestDistance) {
closestDistance = distanceToBottomRight;
closestHandle = Handle.BOTTOM_RIGHT;
}
if (closestDistance <= targetRadius) {
return closestHandle;
}
// If we get to this point, none of the corner handles were in the touch target zone, so then we check the edges.
if (HandleUtil.isInHorizontalTargetZone(x, y, left, right, top, targetRadius)) {
return Handle.TOP;
} else if (HandleUtil.isInHorizontalTargetZone(x, y, left, right, bottom, targetRadius)) {
return Handle.BOTTOM;
} else if (HandleUtil.isInVerticalTargetZone(x, y, left, top, bottom, targetRadius)) {
return Handle.LEFT;
} else if (HandleUtil.isInVerticalTargetZone(x, y, right, top, bottom, targetRadius)) {
return Handle.RIGHT;
}
// If we get to this point, none of the corners or edges are in the touch target zone.
// Check to see if the touch point is within the bounds of the crop window. If so, choose the center handle.
if (isWithinBounds(x, y, left, top, right, bottom)) {
return Handle.CENTER;
}
return null;
}
/**
* Calculates the offset of the touch point from the precise location of the specified handle.
* <p/>
* The offset will be returned in the 'touchOffsetOutput' parameter; the x-offset will be the
* first value and the y-offset will be the second value.
*/
public static void getOffset(@NonNull Handle handle,
float x,
float y,
float left,
float top,
float right,
float bottom,
@NonNull PointF touchOffsetOutput) {
float touchOffsetX = 0;
float touchOffsetY = 0;
// Calculate the offset from the appropriate handle.
switch (handle) {
case TOP_LEFT:
touchOffsetX = left - x;
touchOffsetY = top - y;
break;
case TOP_RIGHT:
touchOffsetX = right - x;
touchOffsetY = top - y;
break;
case BOTTOM_LEFT:
touchOffsetX = left - x;
touchOffsetY = bottom - y;
break;
case BOTTOM_RIGHT:
touchOffsetX = right - x;
touchOffsetY = bottom - y;
break;
case LEFT:
touchOffsetX = left - x;
touchOffsetY = 0;
break;
case TOP:
touchOffsetX = 0;
touchOffsetY = top - y;
break;
case RIGHT:
touchOffsetX = right - x;
touchOffsetY = 0;
break;
case BOTTOM:
touchOffsetX = 0;
touchOffsetY = bottom - y;
break;
case CENTER:
final float centerX = (right + left) / 2;
final float centerY = (top + bottom) / 2;
touchOffsetX = centerX - x;
touchOffsetY = centerY - y;
break;
}
touchOffsetOutput.x = touchOffsetX;
touchOffsetOutput.y = touchOffsetY;
}
// Private Methods /////////////////////////////////////////////////////////////////////////////
/**
* Determines if the specified coordinate is in the target touch zone for a horizontal bar
* handle.
*
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param handleXStart the left x-coordinate of the horizontal bar handle
* @param handleXEnd the right x-coordinate of the horizontal bar handle
* @param handleY the y-coordinate of the horizontal bar handle
* @param targetRadius the target radius in pixels
*
* @return true if the touch point is in the target touch zone; false otherwise
*/
private static boolean isInHorizontalTargetZone(float x,
float y,
float handleXStart,
float handleXEnd,
float handleY,
float targetRadius) {
return (x > handleXStart && x < handleXEnd && Math.abs(y - handleY) <= targetRadius);
}
/**
* Determines if the specified coordinate is in the target touch zone for a vertical bar
* handle.
*
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param handleX the x-coordinate of the vertical bar handle
* @param handleYStart the top y-coordinate of the vertical bar handle
* @param handleYEnd the bottom y-coordinate of the vertical bar handle
* @param targetRadius the target radius in pixels
*
* @return true if the touch point is in the target touch zone; false otherwise
*/
private static boolean isInVerticalTargetZone(float x,
float y,
float handleX,
float handleYStart,
float handleYEnd,
float targetRadius) {
return (Math.abs(x - handleX) <= targetRadius && y > handleYStart && y < handleYEnd);
}
private static boolean isWithinBounds(float x, float y, float left, float top, float right, float bottom) {
return x >= left && x <= right && y >= top && y <= bottom;
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.util;
public class MathUtil {
/**
* Calculates the distance between two points (x1, y1) and (x2, y2).
*/
public static float calculateDistance(float x1, float y1, float x2, float y2) {
final float side1 = x2 - x1;
final float side2 = y2 - y1;
return (float) Math.sqrt(side1 * side1 + side2 * side2);
}
}
package com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.util;
import android.content.res.Resources;
import android.graphics.Paint;
import android.support.annotation.NonNull;
import com.yottacode.pictogram.tabletlibrary.R;
/**
* Utility class for handling all of the Paint used to draw the CropOverlayView.
*/
public class PaintUtil {
// Public Methods //////////////////////////////////////////////////////////
/**
* Creates the Paint object for drawing the crop window border.
*/
public static Paint newBorderPaint(@NonNull Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(resources.getDimension(R.dimen.border_thickness));
paint.setColor(resources.getColor(R.color.border));
return paint;
}
/**
* Creates the Paint object for drawing the crop window guidelines.
*/
public static Paint newGuidelinePaint(@NonNull Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(resources.getDimension(R.dimen.guideline_thickness));
paint.setColor(resources.getColor(R.color.guideline));
return paint;
}
/**
* Creates the Paint object for drawing the translucent overlay outside the crop window.
*
* @return the new Paint object
*/
public static Paint newSurroundingAreaOverlayPaint(@NonNull Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(resources.getColor(R.color.surrounding_area));
return paint;
}
/**
* Creates the Paint object for drawing the corners of the border
*/
public static Paint newCornerPaint(@NonNull Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(resources.getDimension(R.dimen.corner_thickness));
paint.setColor(resources.getColor(R.color.corner));
return paint;
}
}
......@@ -15,7 +15,6 @@ import android.widget.TextView;
import com.yottacode.pictogram.net.ImgDownloader;
import com.yottacode.pictogram.tabletlibrary.R;
import com.yottacode.pictogram.tabletlibrary.gui.login.SerialActivity;
import com.yottacode.pictogram.tools.Img;
import com.yottacode.pictogram.tools.PCBcontext;
import com.yottacode.tools.GUITools;
......@@ -70,14 +69,13 @@ public class LoginActivity extends FragmentActivity {
protected void onStart() {
super.onStart();
// 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"));
this.getIntent().getStringExtra("surname")+" ("+this.getIntent().getStringExtra("office")+")");
supervisorUserNameView.setText(this.getIntent().getStringExtra("email"));
final Vector<Img> imgs = new Vector<>(1);
......@@ -116,4 +114,9 @@ public class LoginActivity extends FragmentActivity {
Log.i(LOG_TAG,"Closing Login window");
PCBcontext.getNetService().closeNotifyStatus();
}
@Override
public void onResume() {
super.onResume();
PCBcontext.setActivityContext(this);
}
}
......@@ -10,7 +10,6 @@ import android.view.Window;
import android.view.WindowManager;
import com.yottacode.pictogram.tabletlibrary.R;
import com.yottacode.pictogram.tabletlibrary.gui.login.SerialActivity;
import com.yottacode.pictogram.tools.PCBcontext;
public class MainActivity extends Activity {
......
......@@ -106,10 +106,10 @@ public class StudentFragmentGrid extends Fragment{
intent.getStringExtra("pic"),
intent.getStringExtra("gender"),
intent.getStringExtra("lang"),
"");
"",
intent.getStringExtra("office"));
Log.i(this.getClass().getCanonicalName(),"Loading vocabulary for "+new_user.get_name_stu()+" office:"+intent.getStringExtra("office"));
PCBcontext.getDevice().insertUser(new_user);
Log.i(this.getClass().getCanonicalName(),"Loading vocabulary for "+new_user.get_name_stu());
progressDialog=ProgressDialog.show(getActivity(), getString(R.string.loadingGrammar),
getString(R.string.userLoadingTxt), false, false);
PCBcontext.set_user(new_user, intent.getStringExtra("token"), new ImgDownloader.iImgDownloaderListener() {
......
......@@ -169,7 +169,7 @@ public class SessionActivity extends FragmentActivity implements ListInstruction
final Button modBtn =((Button)findViewById(R.id.btn_model));
((ListView) findViewById(R.id.session_serverlog)).setAdapter(new ArrayAdapter<String>(getBaseContext(), R.layout.list_logsessions));
StudentFullNameView.setText(student.get_name_stu() + " " + student.get_surname_stu());
StudentUserNameView.setText(student.get_nickname_stu());
StudentUserNameView.setText(student.get_office());
try {
StudentPhotoView.setImageBitmap(student.get_bitmap_stu(this.getBaseContext()));
} catch (IOException e) {
......@@ -185,7 +185,7 @@ public class SessionActivity extends FragmentActivity implements ListInstruction
onoffBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
set_fragment(isChecked);
set_fragment(isChecked,onoffBtn);
}
});
onoffBtn.setEnabled(false);
......@@ -350,7 +350,7 @@ public class SessionActivity extends FragmentActivity implements ListInstruction
finish();
startActivity(new Intent(SessionActivity.this, PictogramActivity.class));
}
private void set_fragment(boolean isChecked) {
private void set_fragment(boolean isChecked, final ToggleButton onoffBtn) {
if (isChecked) {
addLogMsg(getString(R.string.session_log_startingsession));
......@@ -360,6 +360,7 @@ public class SessionActivity extends FragmentActivity implements ListInstruction
addLogMsg(getString(R.string.session_log_startsession));
SessionActivity.this.id_session = id_session;
SessionFragment session= set_fragment_sesion();
onoffBtn.setText("STOP");
new_try(session, id_first_try);
}
......@@ -375,6 +376,7 @@ public class SessionActivity extends FragmentActivity implements ListInstruction
});
} else {
addLogMsg(getString(R.string.session_log_closingsession));
onoffBtn.setText("REC");
SessionWrapper.closeSession(id_session, new SessionWrapper.iCloseSession() {
@Override
public void closed(String end) {
......@@ -400,7 +402,11 @@ public class SessionActivity extends FragmentActivity implements ListInstruction
}
}
@Override
public void onResume() {
super.onResume();
PCBcontext.setActivityContext(this);
}
@Override
public void onDestroy() {
......
package com.yottacode.pictogram.tabletlibrary.net;
import android.app.Activity;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import com.yottacode.pictogram.dao.User;
......@@ -20,24 +24,26 @@ import com.yottacode.pictogram.tools.PCBcontext;
public class NetServiceTablet implements NetService.iNetServiceDevice {
private static final String LOG_TAG = NetServiceTablet.class.getName();
private static NotificationCompat.Builder builder;
private PictogramActivity pictogramActivity;
int notifyID = 666;
public void build() {
this.builder = new NotificationCompat.Builder(PCBcontext.getContext());
/* Intent resultIntent = new Intent(PCBcontext.getContext(), PictogramActivity.class);
this.builder = new NotificationCompat.Builder(PCBcontext.getContext()).setAutoCancel(true).setOngoing(true);
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);*/
builder.setContentIntent(resultPendingIntent);
}
public void notifyStatus(boolean updated) {
int notifyID = 1;
String user="";
if (PCBcontext.getPcbdb()!=null) {
user=PCBcontext.getPcbdb().getCurrentUser().get_name_stu()+" "+PCBcontext.getPcbdb().getCurrentUser().get_surname_stu();
if (PCBcontext.getPcbdb().getCurrentUser().is_supervisor())
......@@ -57,41 +63,49 @@ public class NetServiceTablet implements NetService.iNetServiceDevice {
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(Intent serialActivity, boolean direct_login) {
Log.i(this.getClass().getCanonicalName(),"App restarting");
if (serialActivity==null) {
Class serialClass;
try {
serialClass=Class.forName(PCBcontext.getContext().getString(R.string.SerialClass));
} catch (ClassNotFoundException e) {
Log.e(this.getClass().getCanonicalName(),"S:"+PCBcontext.getContext().getString(R.string.SerialClass));
serialClass=SerialActivity.class;
}
serialActivity = new Intent(PCBcontext.getContext(), serialClass);
}
serialActivity.putExtra("resetPrevUser", !direct_login);
if (direct_login) {
serialActivity.putExtra("switch_usr",PCBcontext.getPcbdb().getCurrentUser().is_supervisor() ? PCBcontext.getPcbdb().getCurrentUser().get_email_sup() : PCBcontext.getPcbdb().getCurrentUser().get_nickname_stu());
serialActivity.putExtra("switch_pwd",PCBcontext.getPcbdb().getCurrentUser().is_supervisor() ? PCBcontext.getPcbdb().getCurrentUser().get_pwd_sup() : PCBcontext.getPcbdb().getCurrentUser().get_pwd_stu());
}
PCBcontext.getContext().startActivity(serialActivity);
public static void restart_PictogramTablet(Activity activity) {
Log.i(LOG_TAG,"App restarting");
Context context=activity.getBaseContext();
Class serialClass;
try {
serialClass=Class.forName(context.getString(R.string.SerialClass));
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG,"S:"+PCBcontext.getContext().getString(R.string.SerialClass));
serialClass=SerialActivity.class;
}
Intent serialActivity = new Intent(context, serialClass);
serialActivity.putExtra("resetPrevUser", false);
serialActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(serialActivity);
activity.finish();
}
public void restart_app(boolean direct_login) {restart_app(null, direct_login);}
public void restart_app(boolean direct_login) {
Log.i(LOG_TAG,"App restarting");
Context context=PCBcontext.getContext();
Class serialClass;
try {
serialClass=Class.forName(PCBcontext.getContext().getString(R.string.SerialClass));
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG,"S:"+PCBcontext.getContext().getString(R.string.SerialClass));
serialClass=SerialActivity.class;
}
Intent serialActivity = new Intent(context, serialClass);
serialActivity.putExtra("resetPrevUser", direct_login);
context.startActivity(serialActivity);
}
public void setPictogramActivity(PictogramActivity pictogramActivity) {this.pictogramActivity=pictogramActivity;}
public void updateUserConfig(User user) {
this.pictogramActivity.setConfig();
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/TotalLayout"
android:background="@android:color/white">
<TextView
android:paddingTop="@dimen/content_padding_half"
android:id="@+id/crop_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/titleCropper"
android:textSize="24sp"
android:textColor="@color/black"
android:textAlignment="center"
android:layout_gravity="center_horizontal"
android:background="@color/gray_blue" />
<LinearLayout
android:orientation="horizontal"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="@+id/crop_legend"
android:layout_gravity="center_horizontal">
<LinearLayout
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/image_layout"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_vertical_margin"
android:gravity="center">
<com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.CropImageView
android:id="@+id/CropImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:layout_gravity="center"
android:cropToPadding="false"
/>
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@android:color/darker_gray"
android:layout_marginRight="@dimen/activity_vertical_margin" />
<FrameLayout
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:id="@+id/legend_menu"
android:layout_gravity="right"
android:layout_marginRight="@dimen/activity_vertical_margin"
android:weightSum="1"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:gravity="center_vertical|center">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="40dp">
<TextView
android:textColor= "@color/gray"
android:textSize="20sp"
android:text="@string/legendText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textLegend"
android:layout_gravity="center" />
<EditText
android:textSize="20sp"
android:imeOptions="actionDone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:id="@+id/edtLegend"
android:textColor="@color/black"
android:layout_below="@+id/textLegend"
android:layout_gravity="center"
android:gravity="center"
android:textColorLink="?android:attr/colorAccent"
android:maxLines="1"
android:textCursorDrawable="@null"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="bottom|center_horizontal"
android:layout_marginBottom="40dp">
<Button
android:id="@+id/okButton"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:textSize="18sp"
android:textAlignment="center"
android:text="@string/accept"
android:gravity="center_horizontal"
android:layout_marginLeft="10dp"
android:capitalize="sentences" />
<Button
android:id="@+id/cancelButton"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:textSize="18sp"
android:textAlignment="center"
android:text="@string/cancel"
android:gravity="center_horizontal"
android:layout_marginRight="10dp"
android:capitalize="sentences"
android:layout_width="120dp" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</LinearLayout>
......@@ -19,6 +19,32 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:text="Legend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/legend_text"
android:textSize="11sp"
android:textColor="@android:color/black"
android:textAlignment="center"
android:layout_gravity="bottom"
android:visibility="gone"
android:background="@android:color/white" />
<TextView
android:text="Legend"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/legend_text_full"
android:textSize="18sp"
android:textColor="@android:color/black"
android:textAlignment="center"
android:maxLines="5"
android:gravity="center"
android:layout_gravity="center"
android:visibility="gone"
android:background="@android:color/white" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
......
......@@ -19,6 +19,29 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/legend_text_big"
android:textSize="13sp"
android:textColor="@android:color/black"
android:textAlignment="center"
android:layout_gravity="bottom"
android:visibility="gone"
android:background="@android:color/white" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/legend_text_big_full"
android:textSize="20sp"
android:textColor="@android:color/black"
android:textAlignment="center"
android:gravity="center"
android:layout_gravity="center"
android:visibility="gone"
android:background="@android:color/white" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
......
......@@ -12,7 +12,7 @@
<string name="session_loading">Downloading instructions</string>
<string name="session_empty">Student without any method</string>
<string name="session_notclosed">Last session was not closed. Please close from </string>
<string name="session_closed_ok">Session upload ok</string>
<string name="session_closed_ok">Session uploupload ok</string>
<string name="session_closed_fail">Session not uploaded. Please, try it from</string>
<string name="session_pause_error">Pause session failed</string>
<string name="session_noinet">No server conexion. Internet conexion is available?</string>
......@@ -34,5 +34,15 @@
<string name="session_eval_model">modeled</string>
<string name="session_eval_notevuated">not evaluated</string>
<string name="session_eval_discarded">discarded</string>
<string name="crop">Crop</string>
<string name="titleCropper">Edit Picto</string>
<string name="croppedImageDesc">Preview</string>
<string name="cancel">Cancel</string>
<string name="accept">Accept</string>
<string name="legendText">Legend</string>
<string name="dialogCamera">Take a Picture</string>
<string name="dialogGallery">Pick from Gallery</string>
<string name="dialogTitle">Choose Method</string>
<string name="dialogCancel">Cancel</string>
</resources>
......@@ -5,7 +5,7 @@
<!---session-->
<string name="session_method">Método</string>
<string name="session_insrtuction">Instrucción</string>
<string name="session_instruction">Instrucción</string>
<string name="session_header">Seleccion de método e instrucción</string>
<string name="session_error">Error de conexión</string>
<string name="session_noinstructions">Alumno sin ninguna instrucción asignada. Por favor, asigne al menos una instrucción al alumno desde Pictogram Web</string>
......@@ -15,7 +15,7 @@
<string name="session_closed_ok">Sesión grabada correctamente. Hora</string>
<string name="session_closed_fail">Sesión no cerrada. Por favor cierre la sesión en el panel de control de Pictogram Tablet</string>
<string name="session_pause_error">Error pausando la sesión</string>
<string name="session_noinet">No hay conexión con el servidor. Por favor, asegúrese que tiene conexión a Internet</string>
<string name="session_noinet">Conexión a Internet necesaria para grabar sesiones. Por favor, asegúrese que tiene conexión</string>
<string name="session_inetok">Conexión con el servidor restablecida</string>
<string name="session_log_startingsession">iniciando sesión</string>
<string name="session_log_closingsession">cerrando sesión</string>
......@@ -34,4 +34,13 @@
<string name="session_eval_model">modelado</string>
<string name="session_eval_notevuated">no evaluado</string>
<string name="session_eval_discarded">inválido</string>
<string name="crop">Recortar</string>
<string name="titleCropper">Nuevo Pictograma</string>
<string name="crop_TextRequired">Por favor, introduzca una leyenda para el pictograma</string>
<string name="legendText">Leyenda:</string>
<string name="dialogCamera">Hacer Foto</string>
<string name="dialogGallery">Seleccionar de Galería</string>
<string name="dialogTitle">Seleccionar Método</string>
<string name="dialogCancel">Cancelar</string>
<string name="uploadingImage">Subiendo imagen al servidor</string>
</resources>
<resources>
<declare-styleable name="CropImageView">
<attr name="guidelines">
<enum name="off" value="0"/>
<enum name="onTouch" value="1"/>
<enum name="on" value="2"/>
</attr>
<attr name="fixAspectRatio" format="boolean"/>
<attr name="aspectRatioX" format="integer"/>
<attr name="aspectRatioY" format="integer"/>
</declare-styleable>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white_translucent">#AAFFFFFF</color>
<color name="black_translucent">#b4000000</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="gray_blue">#FF78909C</color>
<color name="blue_light">#FF40C4FF</color>
<color name="gray">#FF727272</color>
<color name="border">@color/white_translucent</color>
<color name="guideline">@color/white_translucent</color>
<color name="corner">@android:color/white</color>
<color name="surrounding_area">@color/black_translucent</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="border_thickness">3dp</dimen>
<dimen name="corner_thickness">5dp</dimen>
<dimen name="guideline_thickness">1px</dimen>
<dimen name="target_radius">24dp</dimen>
<dimen name="snap_radius">3dp</dimen>
<dimen name="corner_length">20dp</dimen>
<dimen name="content_padding">16dp</dimen>
<dimen name="content_padding_half">8dp</dimen>
</resources>
......@@ -17,7 +17,7 @@
<string name="session_closed_ok">Sesión grabada correctamente</string>
<string name="session_closed_fail">Sesión no cerrada. Por favor intente cerrarla desde</string>
<string name="session_pause_error">Error pausando la sesión</string>
<string name="session_noinet">No hay conexión con el servidor. Por favor, asegúrese que tiene conexión a Internet</string>
<string name="session_noinet">Conexión a Internet necesaria para grabar sesiones. Por favor, asegúrese que tiene conexión</string>
<string name="session_inetok">Conexión con el servidor restablecida</string>
<string name="session_log_startingsession">iniciando sesión</string>
<string name="session_log_closingsession">cerrando sesión</string>
......@@ -37,4 +37,28 @@
<string name="session_eval_notevuated">no evaluado</string>
<string name="session_eval_discarded">inválido</string>
<!-- Dialog for add new picto -->
<string name="dialogTitle">Seleccionar método</string>
<string name="dialogCamera">Hacer Foto</string>
<string name="dialogGallery">Seleccionar de Galería</string>
<string name="dialogCancel">Cancelar</string>
<!-- Cropper -->
<string name="legendText">Leyenda:</string>
<string name="fixedAspectRatio">fixedAspectRatio =\u0020</string>
<string name="aspectRatioXHeader">aspectRatioX =\u0020</string>
<string name="aspectRatioYHeader">aspectRatioY =\u0020</string>
<string name="showGuidelines">showGuidelines =\u0020</string>
<string name="crop">Recortar</string>
<string name="titleCropper">Nuevo Pictograma</string>
<string name="crop_TextRequired">Por favor, introduzca una leyenda para el pictograma</string>
<string name="uploadingImage">Subiendo imagen al servidor</string>
<string-array name="showGuidelinesArray">
<item>Off</item>
<item>On Touch</item>
<item>On</item>
</string-array>
</resources>
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":tabletlibrary" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<module external.linked.project.id=":tabletlibrary" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":tabletlibrary" />
</configuration>
</facet>
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
......@@ -33,7 +27,6 @@
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
......
/build
/watch.iml
\ No newline at end of file
.iml
......@@ -25,6 +25,9 @@ android {
DefaultFlavor {
resValue "string", "server", "https://dev.yottacode.com"
}
LocalFlavor {
resValue "string", "server", "http://192.168.1.35:1337"
}
}
}
......
......@@ -46,7 +46,7 @@ public class NetServiceWatch implements NetService.iNetServiceDevice {
restart_app(new Intent(PCBcontext.getContext(), Activity), direct_login);
}
@Override
public void restart_app(Intent intent, boolean direct_login) {
intent.putExtra("resetPrevUser", !direct_login);
PCBcontext.getContext().startActivity(intent);
......
......@@ -19,11 +19,12 @@ android {
versionCode 1
versionName "1.0"
resValue "string","SerialClass","com.yottacode.pictogram.tabletlibrary.gui.login.SerialActivity"
resValue "string","apk","pictogram.apk"
// signingConfig signingConfigs.config
}
productFlavors {
CIFlavor {
resValue "string", "server", "https://ci.yottacode.com"
resValue "string", "server", "http://ci.yottacode.com"
resValue "bool", "ssl_connect", "false"
}
DevFlavor {
......@@ -35,7 +36,7 @@ android {
resValue "bool", "ssl_connect", "true"
}
LocalFlavor {
resValue "string", "server", "http://192.168.1.36:1337"
resValue "string", "server", "http://192.168.1.35:1337"
resValue "bool", "ssl_connect", "false"
}
}
......
......@@ -73,5 +73,12 @@
android:launchMode="singleInstance"
android:screenOrientation="landscape" />
<activity
android:name="com.yottacode.pictogram.tabletlibrary.gui.communicator.cropper.EditPictoActivity"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleInstance"
android:screenOrientation="landscape" />
</application>
</manifest>
......@@ -22,6 +22,7 @@ public class AppContext extends Application {
instance = this;
registerKioskModeScreenOffReceiver();
startKioskService(); // Service for restarting the app when another app go to foreground
}
private void registerKioskModeScreenOffReceiver() {
......
......@@ -59,11 +59,21 @@ mostradas más adelante hacen todo el trabajo, basta con ejecutar [./install.sh]
### Opción B (desarrollo en local)
0. Comprobar que VT-x (virtualización) está habilitada en la BIOS y tenemos IP estática.
1. Instalar [virtualbox][1] y [vagrant][2] (version >1.5 para este último).
2. Ejecutar `vagrant up` desde este directorio.
0. Comprobar que VT-x (virtualización) está habilitada en la BIOS.
1. Instalar [virtualbox][1] y [vagrant][2] (version >1.5 para este último).
2. Ejecutar `vagrant up` desde este directorio.
Si queremos entrar en la máquina virtual, para Sails y volverlo a lanzar en modo
```
Es importante asegurarse de (si no hemos generado claves SSL) editar `src/config/local.js`
y quitar lo relativo a certificados SSL (toda la entrada de la clave `ssl`).
Si hacemos esto, también tendremos que apuntar Angular al backend correcto (no usar https, sino http) editando `src/assets/scripts/config.js`.
```
> **Importante**: el script utilizado creará un entorno para el servidor desde cero, por lo que > **pueden perderse los datos** almacenados por usuarios.
## Ejecución
Si queremos entrar en la máquina virtual, parar Sails y volverlo a lanzar en modo
consola:
```
......@@ -73,29 +83,6 @@ sails$ vagrant ssh
[vagrant@localhost src]$ sails console
```
Descargar upload.zip y symbolstx.zip desde el servidor de Yottacode,
descomprimir y crear enlaces simbólicos (en /vagrant/):
```
scp ec2-user@dev.yottacode.com:~/upload.tgz .
scp ec2-user@dev.yottacode.com:~/symbolstx.tgz .
tar zxvf upload.tgz
tar zxvf symbolstx.tgz
ln -s symbolstx_96x82 symbolstx
rm upload.tgz
rm symbolstx.tgz
```
Es importante asegurarse de (si no hemos generado claves SSL) editar `src/config/local.js`
y quitar lo relativo a certificados SSL (toda la entrada de la clave `ssl`).
Si hacemos esto, también tendremos que apuntar Angular al backend correcto (no usar https, sino http)
editando `src/assets/scripts/config.js`.
> **Importante**: el script utilizado creará un entorno para el servidor desde cero, por lo que
> **pueden perderse los datos** almacenados por usuarios.
## Ejecución
Una vez lanzado sails con el servidor comienza con la compilación de la aplicación web mediante
tareas de Grunt, para esta configuración existe, por un lado, un fichero [Gruntfile.js][11] que
se encarga de cargar las tareas establecidas y ejecutar la especificada (`default` si no se
......@@ -135,4 +122,4 @@ siguientes comandos**:
[18]: /softuno/pictogram/blob/develop/sails/src/tasks/register/buildProd.js
[19]: /softuno/pictogram/blob/develop/sails/bootstrap.sh
[20]: https://localhost:1337/app
[21]: /softuno/pictogram/tree/develop/sails/src/config/ssl
\ No newline at end of file
[21]: /softuno/pictogram/tree/develop/sails/src/config/ssl
......@@ -15,5 +15,6 @@ Vagrant.configure(2) do |config|
end
config.vm.provision "shell", path: "install.sh"
config.vm.provision "shell", path: "pictoload.sh"
config.vm.provision "shell", path: "bootstrap.sh", run: "always"
end
#!/bin/bash
# keymap and system encoding
echo "-- Configuring encoding and locale"
sudo localectl set-keymap es
export LANG=en_US.UTF-8
echo "-- Installing build-essentials and g++"
sudo apt-get update
sudo apt-get install build-essential g++
# installing ansible
echo "-- Installing ansible"
if [ -e "/usr/bin/apt-get" ]; then
......
#!/bin/bash
cd /vagrant/
echo "--Descargando coleccion de pictogramas"
wget http://dev.yottacode.com/symbolstx.tgz.gpg
wget http://dev.yottacode.com/upload.tgz.gpg
echo "--Desencriptando coleccion"
echo r\"YjtnB+a4$.M*nJ | gpg --batch --no-tty --yes --passphrase-fd 0 symbolstx.tgz.gpg
echo r\"YjtnB+a4$.M*nJ | gpg --batch --no-tty --yes --passphrase-fd 0 upload.tgz.gpg
echo "--Descomprimiendo coleccion"
tar zxvf /vagrant/symbolstx.tgz
tar zxvf /vagrant/upload.tgz
echo "--Renombrado y borrado de archivos"
rm upload.tgz
rm upload.tgz.gpg
rm symbolstx.tgz
rm symbolstx.tgz.gpg
mv symbolstx_96x82 symbolstx
......@@ -49,3 +49,9 @@ UNLOCK TABLES;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2017-01-17 9:48:07
--
-- Creacion y volcado de datos para la tabla `pictocattree`
--
source /vagrant/roles/database/files/pictocat_tree_populate.sql
source /vagrant/roles/database/files/catexp.sql
DROP TABLE IF EXISTS `pictocattree`;
CREATE TABLE pictocattree(
id_cat INT,
id_ancestor INT,
CONSTRAINT pk_primary_key PRIMARY KEY (id_cat, id_ancestor),
INDEX (id_ancestor)
);
DELIMITER $$
DROP PROCEDURE IF EXISTS picto_cat_tree $$
CREATE PROCEDURE picto_cat_tree()
BEGIN
DECLARE done, populated INT DEFAULT FALSE;
DECLARE _id, _id_supercat, x, _id_cat, _id_ancestor INT;
DECLARE pictocat CURSOR FOR SELECT id,id_supercat FROM pictodb.pictocat;
DECLARE pictocattree CURSOR FOR SELECT id_cat, id_ancestor FROM pictodb.pictocattree where id_cat = x;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN pictocat;
DELETE FROM pictocattree;
read_loop: LOOP
FETCH pictocat INTO _id, _id_supercat;
IF done THEN
LEAVE read_loop;
END IF;
INSERT INTO pictocattree (id_cat,id_ancestor) VALUES (_id, _id_supercat);
IF _id_supercat != 0 THEN
SET populated = 0;
populate_loop: LOOP
SET x = _id_supercat;
OPEN pictocattree;
FETCH pictocattree INTO _id_cat, _id_ancestor;
INSERT INTO pictocattree (id_cat, id_ancestor) VALUES (_id, _id_ancestor);
IF _id_ancestor = 0 THEN
LEAVE populate_loop;
END IF;
END LOOP;
CLOSE pictocattree;
END IF;
END LOOP;
CLOSE pictocat;
END $$
DELIMITER ;
CALL picto_cat_tree();
......@@ -67,7 +67,7 @@ SET foreign_key_checks=0;
--
-- Volcado de datos para la tabla `picto_core`
--
source pictodb-data-core.sql
source pictodb-data-coreexp.sql
source /vagrant/roles/database/files/pictodb-data-core.sql
source /vagrant/roles/database/files/pictodb-data-coreexp.sql
SET foreign_key_checks=1;
......@@ -366,6 +366,7 @@ CREATE TABLE IF NOT EXISTS `supervisor` (
`gender` char(1) COLLATE utf8_unicode_ci NOT NULL,
`pic` varchar(255) COLLATE utf8_unicode_ci DEFAULT 'defaultAvatar.jpg',
`address` varchar(180) COLLATE utf8_unicode_ci DEFAULT NULL,
`postal_code` char(10) COLLATE utf8_unicode_ci NOT NULL,
`country` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL,
`email` varchar(80) COLLATE utf8_unicode_ci NOT NULL,
`phone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
......
......@@ -5,34 +5,35 @@ DELIMITER ;;
-- Integrity rule 1: It is not possible to enrol a higer number of student than office.max_enrolments
-- NOTE this has been disabled as accounts are now controlled by licenses
DROP TRIGGER IF EXISTS TRG_NEW_STUDENT_MAXENROLMENTS;
CREATE TRIGGER TRG_NEW_STUDENT_MAXENROLMENTS
BEFORE INSERT ON student
FOR EACH ROW
thisTrigger: BEGIN
DECLARE max_enr,curr_enr INT;
IF ((@TRIGGER_CHECKS = FALSE)
OR (@TRIGGER_BEFORE_INSERT_CHECKS = FALSE))
AND (USER() = 'root@localhost')
THEN
LEAVE thisTrigger;
END IF;
IF new.id_off IS NOT NULL THEN
SELECT
max_students, current_students INTO max_enr, curr_enr
FROM
office
WHERE
office.id=new.id_off;
IF curr_enr>=max_enr THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Maximum number of enrolments reached', MYSQL_ERRNO = 1001;
END IF;
END IF;
END;;
-- CREATE TRIGGER TRG_NEW_STUDENT_MAXENROLMENTS
-- BEFORE INSERT ON student
-- FOR EACH ROW
-- thisTrigger: BEGIN
-- DECLARE max_enr,curr_enr INT;
--
-- IF ((@TRIGGER_CHECKS = FALSE)
-- OR (@TRIGGER_BEFORE_INSERT_CHECKS = FALSE))
-- AND (USER() = 'root@localhost')
-- THEN
-- LEAVE thisTrigger;
-- END IF;
--
-- IF new.id_off IS NOT NULL THEN
-- SELECT
-- max_students, current_students INTO max_enr, curr_enr
-- FROM
-- office
-- WHERE
-- office.id=new.id_off;
--
-- IF curr_enr>=max_enr THEN
-- SIGNAL SQLSTATE '45000'
-- SET MESSAGE_TEXT = 'Maximum number of enrolments reached', MYSQL_ERRNO = 1001;
-- END IF;
-- END IF;
-- END;;
-- Integrity rule 2: office.current_enrolments updating (adding core)
DROP TRIGGER IF EXISTS TRG_NEW_STUDENT_UPDATE_ENROLMENTS;
......@@ -45,17 +46,11 @@ thisTrigger: BEGIN
AND (USER() = 'root@localhost')
THEN
LEAVE thisTrigger;
END IF;
UPDATE
office
SET
current_students=current_students+1
WHERE
office.id=new.id_off;
END IF;
-- Load core collection for student
INSERT INTO stu_picto(id_stu,id_pic,attributes)
SELECT new.id,id_pic, concat('{"id_cat":', if (id_cat_pic is null, 'null', id_cat_pic),
SELECT new.id,id_pic, concat('{"id_cat":', if (id_cat_pic is null, 'null', id_cat_pic),
',"coord_x":',coord_x,
',"coord_y":',coord_y,
',"status":"invisible"',
......@@ -80,50 +75,12 @@ thisTrigger: BEGIN
IF NOT (old.id_off<=>new.id_off) THEN
IF (old.id_off IS NOT NULL) THEN
UPDATE
office
SET
current_students=current_students-1
WHERE
office.id=old.id_off;
DELETE
stu_sup
FROM
stu_sup INNER JOIN supervisor ON (stu_sup.id_sup=supervisor.id)
WHERE
id_stu=new.id AND old.id_off=supervisor.id_off;
END IF;
IF (new.id_off IS NOT NULL) THEN
UPDATE
office
SET
current_students=current_students+1
WHERE
office.id=new.id_off;
END IF;
END IF;
END;;
-- Integrity rule 4: office.current_enrolments when deleting student. Note that is not required to delete stu_sup because stu_sup.id_stu is a foreign key of student
DROP TRIGGER IF EXISTS TRG_DELETE_STUDENT_ENROLMENTS;
CREATE TRIGGER TRG_DELETE_STUDENT_ENROLMENTS
AFTER DELETE ON student
FOR EACH ROW
thisTrigger: BEGIN
IF ((@TRIGGER_CHECKS = FALSE)
OR (@TRIGGER_AFTER_DELETE_CHECKS = FALSE))
AND (USER() = 'root@localhost')
THEN
LEAVE thisTrigger;
END IF;
IF old.id_off IS NULL THEN
UPDATE
office
SET
current_students=current_students-1
WHERE
office.id=old.id_off;
END IF;
DELETE
stu_sup
FROM
stu_sup INNER JOIN supervisor ON (stu_sup.id_sup=supervisor.id)
WHERE
id_stu=new.id AND old.id_off=supervisor.id_off;
END IF;
END IF;
END;;
DELIMITER ;
......@@ -24,6 +24,14 @@
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/symbolstix.sql"
- name: Imports symbolstix categories
mysql_db:
login_user: "{{ database_user }}"
login_password: "{{ database_user_passwd }}"
name: "{{ database_name }}"
state: import
target: "{{ server_path }}/{{ database_files_relative_path }}/pictocat.sql"
- name: Imports application essential data
mysql_db:
login_user: "{{ database_user }}"
......
# Changes
Changes to be performed manually in servers to upgrade
## AngularJS
(already done in dev)
- angular-re-captcha has been replaced by angular-recaptcha, so bower has to be run
- reinstall ui-bootstrap
- replace angular-file-upload by ng-file-upload
`bower install`
## Database
(already done in dev)
- load pictocat_tree_populate.sql
`source /vagrant/roles/database/files/pictocat_tree_populate.sql;`
- reload trigers-enrolments-integrity-constraints.sql
- alter table supervisor to add postal_code:
`alter table supervisor add column `postal_code` char(10) COLLATE utf8_unicode_ci NOT NULL;`
- alter table office to add postal_code:
`alter table office add column `postal_code` char(10) COLLATE utf8_unicode_ci NOT NULL;`
- remove max_students and current_students columns from offices:
`alter table office drop column max_students;`
`alter table office drop column current_students;`
- copy postal_code value from office to its supervisors
`update supervisor as sup inner join office as off on off.id = sup.id_off set sup.postal_code = off.postal_code;`
- alter table office
`alter table office modify logo_url varchar(240) default null;`
......@@ -86,10 +86,14 @@ module.exports = {
if (!params.id_sup)
return res.badRequest("No supervisor defined");
if (!params.lang)
return res.badRequest("No lang defined");
MetaMethod.create({
"name": params.name,
"description": "",
"supervisor": params.id_sup
"supervisor": params.id_sup,
"lang": params.lang
})
.then(function(createdMethod){
return res.ok(createdMethod);
......
......@@ -132,16 +132,29 @@ module.exports = {
},
// FERNANDO: avoid method name duplicates
//
// Saves a method as a template
//
/*
* Saves a method as a template
* @param {request} req
* {
* id_stu: studentId,
* id_sup: supervisorID
* lang: student lang code
* }
* @param {response} res
* {
* id: methodId,
* supervisor: supervisorId
* lang: {string} language code
* description: {string}
* }
*/
save: function(req, res){
var params = req.allParams();
if (!params.id_met) return res.json(500, {error: "No method defined"});
if (!params.id_sup) return res.json(500, {error: "No supervisor defined"});
if (!params.lang) return res.json(500, {error: "No lang defined"});
Method.findOne(params.id_met).populate('instructions').exec(function(err, method) {
if (err){
......@@ -152,7 +165,8 @@ module.exports = {
MetaMethod.create({
"name": method.name,
"description": method.description,
"supervisor": params.id_sup
"supervisor": params.id_sup,
"lang": params.lang
}).exec(function(err, createdMethod){
if (err){
sails.log.debug("Create Method error: " + err);
......
......@@ -102,7 +102,8 @@ module.exports = {
*/
getAll: function (req, res) {
Office.find().populate('admin').then(function (offices) {
res.ok(offices);
// return all offices filtering out personal ones
res.ok(offices.filter(o => o.name !== 'no_office'));
})
.catch(function () {
res.serverError();
......@@ -135,14 +136,29 @@ module.exports = {
.findOne({ id: req.params.id })
.populate('supervisors')
.then(function (office) {
if (office) {
res.ok(office.supervisors);
} else {
res.notFound();
}
if (!office)
return res.notFound();
var sups = [];
async.eachSeries(office.supervisors,
function(sup, next) {
StuSup.find({id_sup: sup.id})
.populate('student')
.then((stusups) => {
var sup_obj = sup.toObject();
sup_obj.students = stusups.map((stusup) => {return stusup.student;});
sups.push(sup_obj);
next();
})
.catch((err) => next(err));
},
function(err) {
if (err)
return res.serverError("Unable to get students");
return res.ok(sups);
});
})
.catch(function () {
res.badRequest();
return res.badRequest();
});
},
};
......@@ -119,6 +119,259 @@ module.exports = {
},
/**
* Return all pictos from a category and subcategoriesusing the language
* of the specified supervisor.
* @param {request} req {} (with supervisorId and categoryId as url parameters)
* @param {response} res
* [
* {
* expressions: [
* // There should be just one expression per picto
* // with the language being used by the supervisor
* {
* id: 1234,
* lang: 'es-es',
* text: 'nacimiento',
* picto: 23
* }
* ],
* source: 1,
* owner: null,
* id: 23,
* uri: 'picto/uri.jpg',
* category: 41
* }
* ]
*/
fromCatSubcat: function (req, res) {
var l = [];
var fs = require('fs');
// return empty for category 0 (that represents category pictos and all custom pictos)
// if (req.params.id_cat == 0)
// return res.ok(l);
Supervisor.findOne({ id: req.params.id }).then(function (supervisor) {
if (supervisor) {
PictoCatTree
.find({select: ['id_cat'], id_ancestor: req.params.id_cat })
.then(function (categories) {
var filtered = [Number(req.params.id_cat)]; // Category itself
for(var i =0; i<categories.length;i++){ //All subcategories
filtered.push(categories[i].id_cat);
}
Picto.find({ category: filtered })
.paginate({ page: req.params.page, limit: req.params.limit})
.populate('expressions', { lang: supervisor.lang })
.then(function (pictos) {
async.eachSeries(pictos, function(picto, next_cb) {
// check picto has expressions associated in student language
if (picto.expressions.length == 0 || picto.expressions[0].text.length == 0)
return next_cb();
// check picto image is available
picto.imageFileExists((found) => {
if (found) {
l.push(picto);
next_cb();
}
else
next_cb();
});
},
function (err) { // loop has end
if (err) throw err;
sails.log.debug(pictos.length + " pictos sent for category " + req.params.id_cat + " in language " + supervisor.lang);
return res.ok(l);
}); // end async.eachSeries
})
.catch(() => res.badRequest());
return categories;
})
.catch(function (err) {
throw err;
});
} else {
return res.badRequest();
}
})
.catch(() => res.serverError());
},
/**
* Return own or public pictos using a search string from category
* and subcategories using the language of the specified supervisor.
* @param {request} req {} (with supervisorId and categoryId as url parameters)
* @param {response} res
* [
* {
* expressions: [
* // There should be just one expression per picto
* // with the language being used by the supervisor
* {
* id: 1234,
* lang: 'es-es',
* text: 'nacimiento',
* picto: 23
* }
* ],
* source: 1,
* owner: null,
* id: 23,
* uri: 'picto/uri.jpg',
* category: 41
* }
* ]
*/
fromSearch: function (req, res) {
Supervisor.findOne({ id: req.params.id }).then(function (supervisor) {
if (supervisor) {
if (req.params.id_cat == 0){ //Search in all categories
getPictos(0, supervisor.lang, supervisor.id);
}else{//Get selected category and subcategories
PictoCatTree
.find({select: ['id_cat'], id_ancestor: req.params.id_cat })
.then(function (categories) {
var filtered = [Number(req.params.id_cat)]; //Category itself
for(var i =0; i<categories.length;i++){ //All subcategories
filtered.push(categories[i].id_cat);
}
getPictos(filtered, supervisor.lang, supervisor.id);
})
.catch(function (err) {
throw err;
});
}
} else {
return res.badRequest();
}
})
.catch(() => res.serverError());
//
// Get pictos within specified categories
//
function getPictos(categories, lang, sup_id){
var l = [];
var fs = require('fs');
var sql = "";
var params = [];
if(typeof categories == "object" ){ //Is an array
sql="SELECT `picto_exp`.`id`, `picto_exp`.`id_pic`, `picto_exp`.`lang`, `picto_exp`.`text`, `picto`.`id`, `picto`.`uri`, `picto`.`id_src`, `picto`.`id_owner`, `picto`.`id_cat` FROM `picto_exp` INNER JOIN `picto` ON `picto_exp`.`id_pic` = `picto`.`id` WHERE `picto_exp`.`lang`=? AND `picto_exp`.`text` LIKE ? AND (`picto`.`id_owner` IS NULL OR `picto`.`id_owner`=?) AND `picto`.`id_src`=? AND `picto`.`id_cat` IN (?);";
params = [lang,'%'+req.params.text+'%', sup_id, req.params.source, categories];
}else{
sql= "SELECT `picto_exp`.`id`, `picto_exp`.`id_pic`, `picto_exp`.`lang`, `picto_exp`.`text`, `picto`.`id`, `picto`.`uri`, `picto`.`id_src`, `picto`.`id_owner`, `picto`.`id_cat` FROM `picto_exp` INNER JOIN `picto` ON `picto_exp`.`id_pic` = `picto`.`id` WHERE `picto_exp`.`lang`=? AND `picto_exp`.`text` LIKE ? AND (`picto`.`id_owner` IS NULL OR `picto`.`id_owner`=?) AND `picto`.`id_src`=?;";
params = [lang,'%'+req.params.text+'%', sup_id, req.params.source];
}
// PictoExp.find({lang: lang, text: { like: '%'+req.params.text+'%'}})
// .populate('picto', { where: { or: [{owner: null}, {owner: sup_id}],
// category: categories, source: req.params.source }})
// .then(function(expressions){
PictoExp.query(sql,params, function(err, rawResult){
if(err){
console.log(err);
return
}
async.eachSeries(rawResult, function(row, next_cb) {
var picto = new Picto._model({ id: row.id,
uri: row.uri, category: row.id_cat,
source: row.id_src, owner: row.id_owner});
// check picto image is available
picto.imageFileExists((found) => {
if (found) {
var pictoReturn = {expressions: [{id:row.id,
lang:row.lang, text: row.text,
picto:row.id_pic}], id: picto.id,
uri: picto.toJSON().uri, category: picto.id_cat,
source: picto.id_src, owner: picto.id_owner};
l.push(pictoReturn);
next_cb();
}
else
next_cb();
});
},
function (err) { // loop has end
if (err) throw err;
sails.log.debug(rawResult.length + " pictos sent for search " + req.params.text + " in language " + lang);
return res.ok(l);
}); // end async.eachSeries
});
//.catch(() => res.badRequest());
}
},
/**
* Return all pictos from a SymbolStx using the language of the specified supervisor.
* @param {request} req {} (with supervisorId as url parameters)
* @param {response} res
* [
* {
* expressions: [
* // There should be just one expression per picto
* // with the language being used by the supervisor
* {
* id: 1234,
* lang: 'es-es',
* text: 'nacimiento',
* picto: 23
* }
* ],
* source: 1,
* owner: null,
* id: 23,
* uri: 'picto/uri.jpg',
* category: 41
* }
* ]
*/
fromSymbolStx: function (req, res) {
var l = [];
var fs = require('fs');
var cat = req.params.id_cat;
Supervisor.findOne({ id: req.params.id }).then(function (supervisor) {
if (supervisor) {
Picto.find({ source: 1})
.paginate({page: req.params.page, limit:req.params.limit})
.populate('expressions', { lang: supervisor.lang })
.then(function (pictos) {
async.eachSeries(pictos, function(picto, next_cb) {
// check picto has expressions associated in student language
if (picto.expressions.length == 0 || picto.expressions[0].text.length == 0)
return next_cb();
// check picto image is available
picto.imageFileExists((found) => {
if (found) {
l.push(picto);
next_cb();
}
else
next_cb();
});
},
function (err) { // loop has end
if (err) throw err;
sails.log.debug(pictos.length + " pictos sent from SymbolStx in language " + supervisor.lang);
return res.ok(l);
}); // end async.eachSeries
})
.catch(() => res.badRequest());
} else {
return res.badRequest();
}
})
.catch(() => res.serverError());
},
/**
* Add a tag to an existing picto. This tag is associated to the supervisor.
* @param {request} req
* {
......
......@@ -11,7 +11,9 @@ module.exports = {
* @param {response} res {}
*/
ping: function (req, res) {
res.ok();
res.ok({
version: sails.config.pictogram.version
});
},
/**
......@@ -21,6 +23,8 @@ module.exports = {
* @param {response} res {}
*/
ping_session: function (req, res) {
res.ok();
res.ok({
version: sails.config.pictogram.version
});
}
};
......@@ -145,6 +145,8 @@ module.exports = {
student.supervision = 1; // requester is tutor of the studend
else if (stu_sup && req.token.office && student.office == req.token.office.id)
student.supervision = 2; // requester is supervisor of student
else if (req.token.isStudent && req.token.id == student.id)
student.supervision = 3 // requester is the student himself
if (student.supervision == -1) // should not hace access!!!
return res.forbidden("Access to this student should not be granted to you");
......@@ -177,17 +179,8 @@ module.exports = {
});
})
.error(function(err) {
if (err.message.search("Maximum number of enrolments reached") > 0) {
// This is a MySQL error triggered by TRG_NEW_STUDENT_MAXENROLMENTS trigger
// (see triggers-enroments-integrity-constraints.sql)
// As the format is not that of a normal error, we just get message
sails.log.debug(err.message);
return res.serverError(err.message);
}
else {
sails.log.debug(err.message);
return res.serverError(err.message);
}
sails.log.debug(err.message);
return res.serverError(err.message);
});
});
},
......@@ -836,22 +829,52 @@ module.exports = {
/**
* Add an existing picto to the student's collection
* @param {request} req (with studentId and pictoId as url parameter)
* @param {request} req (with id_stu and id_picto as url parameters)
* {
* id_stu,
* id_picto,
* attributes: { @see StuPicto.getValidAttributes() }
* }
* @param {response} res
* {
* id: stuPictoId (association betweet student and picto)
* student: studentId (speficied as url parameter)
* picto: { @see Picto model}
* attributes: { @see StuPicto.getValidAttributes() }
*
* @param {response} res {
* id: <stu_picto ID>,
* student: <student ID>,
* attributes: {
* id_cat: categoryId or null,
* coord_x: 1 .. 5 or null,
* coord_y: 1 .. 10 or null,
* free_category_coord_x: 0 .. 4 or null,
* free_category_coord_y: 0 .. 9 or null,
* status: '[invisible]/enabled/disabled',
* highlight: true/[false],
* legend: true/[false],
* legend_size: '[small]/large',
* expression: 'custom expression',
* color: any valid HEX color or [null]
* },
* picto: {
* id: <pictoID>,
* source: <sourceID>,
* owner: <ownerID> or null,
* id: <pictoID>,
* uri: <URL to image>,
* category: <categoryID>
* }
* }
*/
add_picto: function (req, res) {
var params = req.allParams();
Student.findOne({ id: params.id_stu })
StuPicto.find({id_pic: params.id_picto, id_stu: params.id_stu})
.then((entries) => {
if (entries && entries.length > 0) {
var err = new Error("Picto already in student's vocabulary");
err.code = sails.config.pictogram.error_codes.DUPLICATED_PICTO;
throw err;
}
return Student.findOne({ id: params.id_stu });
})
.then((student) => {
if (!student) {
sails.log.error(`Student ${params.id_stu} not found`);
......@@ -890,7 +913,7 @@ module.exports = {
return res.ok(resp);
});
})
.catch(err => res.serverError("Error adding picto: " + err));
.catch(err => res.serverError(err));
},
/**
......@@ -914,9 +937,41 @@ module.exports = {
});
},
// update action
// update picto atributes for a studentPicto
//
/*
* Updates the attributes of a picto related to student, i.e. stu_picto records
* @param {request} req {
* 'id_stu': <student ID>,
* 'id_pic': <picto ID>,
* 'attributes': <object>
* }
*
* or
*
* @param {request} req {
* 'id_stuPicto': <stuPicto ID>,
* 'attributes': <object>
* }
*
* @param {response} res {
* id: <stu_picto ID>,
* student: <student ID>,
* attributes: {
* id_cat: categoryId or null,
* coord_x: 1 .. 5 or null,
* coord_y: 1 .. 10 or null,
* free_category_coord_x: 0 .. 4 or null,
* free_category_coord_y: 0 .. 9 or null,
* status: '[invisible]/enabled/disabled',
* highlight: true/[false],
* legend: true/[false],
* legend_size: '[small]/large',
* expression: 'custom expression',
* color: any valid HEX color or [null]
* }
* },
* }
*
*/
update_picto: function (req, res) {
var params = req.allParams();
......@@ -952,11 +1007,24 @@ module.exports = {
* 'prev_id_stu_pic': <ID of previous category entry in stu_picto>,
* 'new_id_pic': <ID of the picto to be the replacement>
* }
*
* @param {response} res {
* 'id_stu_pic': <id of the new category in stu_picto>,
* 'attributes': <attributes of the new category picto>,
* 'picto': <picto information used as new category>
* }
* id: <stu_picto ID>,
* student: <student ID>,
* attributes: {
* id_cat: categoryId or null,
* coord_x: 1 .. 5 or null,
* coord_y: 1 .. 10 or null,
* free_category_coord_x: 0 .. 4 or null,
* free_category_coord_y: 0 .. 9 or null,
* status: '[invisible]/enabled/disabled',
* highlight: true/[false],
* legend: true/[false],
* legend_size: '[small]/large',
* expression: 'custom expression',
* color: any valid HEX color or [null]
* }
* },
*
*/
update_category: function (req, res) {
......@@ -1106,28 +1174,20 @@ module.exports = {
},
//
// Unsubscribe to websockets events
// Unsubscribe from websockets events
//
unsubscribe: function (req, res) {
var action = req.param('action');
//var attributes = req.param('attributes');
var attributes = req.param('attributes');
attributes.ui = attributes.ui ? attributes.ui : 'PCB';
if (req.isSocket) {
var rooms = sails.sockets.socketRooms(req.socket);
console.log("Subscribed rooms in socket: " + JSON.stringify(rooms));
// Leave all rooms
for (var i = 0; i < rooms.length; i++) {
//sails.sockets.leave(req.socket, rooms[i]); MODIFICADO POR FERNANDO. SI NO, NO SE ACTUALIZA UPDATE_PEERS
sails.hooks.rooms.unsubscribeFromRoom(rooms[i], req.socket);
sails.log.debug("Unsusbscribe from room " + rooms[i]);
}
res.json({
msg: "Unsubscribed from all rooms"
});
sails.hooks.rooms.unsubscribeFromRoom(
sails.hooks.rooms.student(attributes.id_stu),
req.socket
);
}
res.ok({ msg: "Unsubscribed from student's rooms"});
},
//
......@@ -1162,7 +1222,7 @@ module.exports = {
* "attributes": {
* "id_stu": <id_stu>,
* "timestamp": <timestamp_string_in_ISO_format> (e.g.: "2016-07-13 17:50:00.224+0200"),
* "picto": {...}
* "pictos": {...}
* }
* }
* @param {response} res {<action_created>}
......
......@@ -71,10 +71,13 @@ module.exports = {
Supervisor.findOneByEmail(email).then(function (supervisor) {
if (!supervisor)
return res.notFound("Supervisor not found")
throw new Error("Supervisor not found")
if (!bcrypt.compareSync(password, supervisor.password))
throw res.unauthorized("Invalid email/password")
throw new Error("Invalid email/password");
if (supervisor.active == 0)
throw new Error("This account has not been activated");
return (supervisor);
......@@ -100,7 +103,7 @@ module.exports = {
}
if (!supervisor.isSupAdmin && !stuSup)
throw res.unauthorized("Supervisor without students");
throw new Error("Supervisor without students");
return res.ok({
user: supervisor,
......@@ -108,9 +111,8 @@ module.exports = {
token: sailsTokenAuth.issueToken(supervisor, sails.config.jwt.expiresInMinutes)
});
})
.catch(function (err) {
return res.serverError("Error when connecting to database");
return res.badRequest(err.message);
});
},
......@@ -119,7 +121,7 @@ module.exports = {
* Activate the user account specified
* @param {request} req
* {
* "token": "12398123aas78sf798as7d987234" // Encryted code with supervisor ID
* "token": "12398123aas78sf798as7d987234" // Encryted code with supervisor ID, siging role and id_off
* }
* @param {response} login view
* {
......@@ -132,21 +134,67 @@ module.exports = {
* }
*/
activate: function (req, res) {
if (!req.params.token)
return res.badRequest("Invalid activation URL");
sailsTokenAuth.verifyToken(req.params.token, function(err, token) {
if (err)
return res.badRequest("Invalid token");
Supervisor.findOne(token).then(function (supervisor) {
Supervisor.findOne(token.id_sup)
.then(function (supervisor) {
if (!supervisor)
throw new Error("Error when looking for user");
supervisor.active = true;
delete supervisor.password;
supervisor.save();
return res.view('accountActivated', {sup: supervisor, login_url: 'https://' + req.headers.host + '/app'});
// an email has to be sent to office administrators
if (token.role == 'tutor_office' || token.role === 'therapist_office') {
Office.findOne(token.id_off)
.populate('admin')
.then((off) => {
if (!off)
throw new Error("Office not found: ");
var message = sails.__({
phrase: token.role + '_request',
locale: supervisor.lang
}, {name: supervisor.name + " " + supervisor.surname, email: supervisor.email});
mailService.mailer()
.send({
to: off.admin.email,
text: message
})
.then(() => {})
.catch((err) => {throw err});
})
.catch((err) => {throw err;});
}
// welcome message is returned
return res.view('accountActivated', {
welcome_msg1: sails.__({
phrase: 'welcome_msg1',
locale: supervisor.lang
}, {name: supervisor.name}),
welcome_msg2: sails.__({
phrase: 'welcome_msg2',
locale: supervisor.lang
}),
login_url: 'https://' + req.headers.host + '/app',
login: sails.__('login')
});
})
.catch(function (err) {
return res.serverError("Error when activating account " + err);
return res.serverError(err.message ? err.message : 'Supervisor not found');
});
});
},
......@@ -212,61 +260,98 @@ module.exports = {
*/
create: function (req, res) {
var params = req.params.all();
var supervisor;
// Send email confirmation
function sendConfirmationMail(cb) {
sails.log.debug("Creating supervisor with params " + JSON.stringify(params));
console.log("mail------------\n" + JSON.stringify(supervisor));
if (params.name &&
params.surname &&
params.gender &&
params.password &&
params.email) {
Supervisor.create({
name: params.name,
surname: params.surname,
gender: params.gender,
password: params.password,
email: params.email,
pic: sails.config.pictogram.paths.defaultAvatarFileName,
address: params.address || null,
country: params.country || null,
phone: params.phone || null,
lang: params.lang || null,
ttsEngine: params.ttsEngine || null
}).then(function (supervisor) {
var token = sailsTokenAuth.issueToken({
id_sup: supervisor.id,
role: params.role,
id_off: params.id_off,
}, 60*24*7); // expires in 1 week
if (!supervisor)
res.serverError("Supervisor created but returned null");
var message = sails.__({
phrase: 'signin_mail',
locale: params.lang || 'es-es'
}) + 'https://' + req.headers.host + '/sup/activate/' + token; // expires in 1 week
sails.log.debug("SUPERVISOR: " + JSON.stringify(supervisor));
/* Send email confirmation */
var message = sails.__({
phrase: 'signin_mail',
locale: params.lang || 'es-es'
}) + 'https://' + req.headers.host + '/sup/activate/' + sailsTokenAuth.issueToken(supervisor.id, 60*24*7); // expires in 1 week
sails.log.debug("Sending activation email: \n" + message);
sails.log.debug("Sending activation email: \n" + message);
mailService.mailer()
.send({
to: params.email,
text: message
})
.then(() => {
res.ok({
user: supervisor,
token: sailsTokenAuth.issueToken(supervisor.id)
mailService.mailer()
.send({
to: params.email,
text: message
})
.then(() => {cb();})
.catch((err) => {cb(err);});
} // /sendConfirmationEmail()
sails.log.debug("Creating supervisor with params " + JSON.stringify(params));
if (!params.name || !params.surname || !params.gender || !params.password || !params.email )
return res.badRequest("Invalid params");
var supData = {
name: params.name,
surname: params.surname,
gender: params.gender,
password: params.password,
email: params.email,
pic: sails.config.pictogram.paths.defaultAvatarFileName,
address: params.address || '',
postalCode: params.postalCode || '',
country: params.country || '',
phone: params.phone || '',
lang: params.lang || 'es-es',
};
if (params.id_off)
supData.id_off = params.id_off;
Supervisor.create(supData)
.then(function (sup) {
if (!sup)
return res.serverError("Supervisor created but returned null");
supervisor = sup;
if (params.role === 'therapist_office' || params.role === 'tutor_office') {
sendConfirmationMail((err) => {
if (err) throw err;
return res.ok();
});
} else if (params.role === 'therapist_nooffice' || params.role === 'tutor_nooffice') {
Office.create(params.office)
.then((off) => {
// link supervisor with office
console.log("supervisor: \n" + JSON.stringify(sup));
sup.id_off = off.id;
delete sup.password;
sup.save();
console.log("supervisor: \n" + JSON.stringify(sup));
// set supervisor as admin in the office
off.admin = sup.id;
off.save();
supervisor = sup;
sendConfirmationMail((err) => {
if (err) throw err;
return res.ok();
});
})
.catch((err) => {
res.serverError("Mail could not be sent " + err);
});
}).catch(function (err) {
res.serverError("Supervisor could not be created: " + err);
});
} else {
res.badRequest("Invalid params");
}
.catch(err => {throw err});
} else
return res.badRequest("Invalid role");
}).catch(function (err) {
return res.serverError("Supervisor could not be created: " + err);
});
},
/*
......

4.89 KB | W: | H:

5.23 KB | W: | H:

sails/src/assets/app/img/logo_pictogram.png
sails/src/assets/app/img/logo_pictogram.png
sails/src/assets/app/img/logo_pictogram.png
sails/src/assets/app/img/logo_pictogram.png
  • 2-up
  • Swipe
  • Onion skin
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