diff --git a/.gitignore b/.gitignore index 116fa474..3b524b65 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ local.properties # User-specific configurations .idea/copyright/profiles_settings.xml .idea/libraries/ +.idea/inspectionProfiles/ .idea/.name .idea/compiler.xml .idea/encodings.xml diff --git a/app/build.gradle b/app/build.gradle index 70b2c1db..4b9275b5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,6 +42,7 @@ dependencies { compile 'com.thoughtbot:expandablerecyclerview:1.3' compile 'com.android.support:cardview-v7:25.3.1' compile 'com.google.code.gson:gson:2.8.1' + compile 'com.firebase:firebase-jobdispatcher:0.8.1' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.9.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 820ae8f9..d0bebe51 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,12 +5,13 @@ android:installLocation="internalOnly"> - + + - + + android:theme="@style/WulkanowyTheme.noActionBar"> @@ -29,13 +30,21 @@ + android:configChanges="orientation|screenSize" + android:label="@string/activity_dashboard_text" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/activity/dashboard/DashboardActivity.java b/app/src/main/java/io/github/wulkanowy/activity/dashboard/DashboardActivity.java index c0eb15a5..badcd423 100644 --- a/app/src/main/java/io/github/wulkanowy/activity/dashboard/DashboardActivity.java +++ b/app/src/main/java/io/github/wulkanowy/activity/dashboard/DashboardActivity.java @@ -16,8 +16,11 @@ import io.github.wulkanowy.activity.dashboard.lessonplan.LessonPlanFragment; public class DashboardActivity extends AppCompatActivity { private GradesFragment gradesFragment = new GradesFragment(); + private AttendanceFragment attendanceFragment = new AttendanceFragment(); + private BoardFragment boardFragment = new BoardFragment(); + private LessonPlanFragment lessonPlanFragment = new LessonPlanFragment(); private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener diff --git a/app/src/main/java/io/github/wulkanowy/activity/dashboard/grades/GradesFragment.java b/app/src/main/java/io/github/wulkanowy/activity/dashboard/grades/GradesFragment.java index a339b2b7..434d711a 100644 --- a/app/src/main/java/io/github/wulkanowy/activity/dashboard/grades/GradesFragment.java +++ b/app/src/main/java/io/github/wulkanowy/activity/dashboard/grades/GradesFragment.java @@ -10,23 +10,11 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import io.github.wulkanowy.R; -import io.github.wulkanowy.api.Cookies; -import io.github.wulkanowy.api.StudentAndParent; -import io.github.wulkanowy.api.grades.GradesList; import io.github.wulkanowy.api.grades.Subject; -import io.github.wulkanowy.api.grades.SubjectsList; -import io.github.wulkanowy.database.accounts.Account; -import io.github.wulkanowy.database.accounts.AccountsDatabase; -import io.github.wulkanowy.database.cookies.CookiesDatabase; import io.github.wulkanowy.database.grades.GradesDatabase; import io.github.wulkanowy.database.subjects.SubjectsDatabase; @@ -63,66 +51,30 @@ public class GradesFragment extends Fragment { public class MarksTask extends AsyncTask { - private Context mContext; - - private Map loginCookies = new HashMap<>(); + private Context context; MarksTask(Context context) { - mContext = context; + this.context = context; } @Override protected Void doInBackground(Void... params) { - long userId = mContext.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("isLogin", 0); - try { - Gson gson = new GsonBuilder().enableComplexMapKeySerialization() - .setPrettyPrinting().create(); - CookiesDatabase cookiesDatabase = new CookiesDatabase(mContext); - cookiesDatabase.open(); - loginCookies = gson.fromJson(cookiesDatabase.getCookies(), loginCookies.getClass()); - cookiesDatabase.close(); - } catch (Exception e) { - e.printStackTrace(); - } + SubjectsDatabase subjectsDatabase = new SubjectsDatabase(context); + GradesDatabase gradesDatabase = new GradesDatabase(context); - try { - Cookies cookies = new Cookies(); - cookies.setItems(loginCookies); + gradesDatabase.open(); - AccountsDatabase accountsDatabase = new AccountsDatabase(mContext); - accountsDatabase.open(); - Account account = accountsDatabase.getAccount(userId); - accountsDatabase.close(); - - StudentAndParent snp = new StudentAndParent(cookies, account.getSymbol()); - SubjectsList subjectsList = new SubjectsList(snp); - - SubjectsDatabase subjectsDatabase = new SubjectsDatabase(mContext); - subjectsDatabase.open(); - subjectsDatabase.put(subjectsList.getAll()); - subjectsDatabase.close(); - - - GradesList gradesList = new GradesList(snp); - GradesDatabase gradesDatabase = new GradesDatabase(mContext); - gradesDatabase.open(); - gradesDatabase.put(gradesList.getAll()); - - for (Subject subject : subjectsDatabase.getAllSubjectsNames()) { - List gradeItems = gradesDatabase.getSubjectGrades(userId, SubjectsDatabase.getSubjectId(subject.getName())); - if (gradeItems.size() > 0) { - subjectWithGradesList.add(new SubjectWithGrades(subject.getName(), gradeItems)); - } + for (Subject subject : subjectsDatabase.getAllSubjectsNames()) { + List gradeItems = gradesDatabase.getSubjectGrades(context.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("isLogin", 0), + SubjectsDatabase.getSubjectId(subject.getName())); + if (gradeItems.size() > 0) { + subjectWithGradesList.add(new SubjectWithGrades(subject.getName(), gradeItems)); } - - gradesDatabase.close(); - - - } catch (Exception e) { - e.printStackTrace(); } + gradesDatabase.close(); + return null; } diff --git a/app/src/main/java/io/github/wulkanowy/activity/main/LoginTask.java b/app/src/main/java/io/github/wulkanowy/activity/main/LoginTask.java index 3bec0d96..d04756f7 100644 --- a/app/src/main/java/io/github/wulkanowy/activity/main/LoginTask.java +++ b/app/src/main/java/io/github/wulkanowy/activity/main/LoginTask.java @@ -1,137 +1,86 @@ package io.github.wulkanowy.activity.main; -import android.app.Activity; import android.app.ProgressDialog; +import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.database.SQLException; import android.os.AsyncTask; import android.widget.Toast; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import java.io.IOException; import io.github.wulkanowy.R; import io.github.wulkanowy.activity.dashboard.DashboardActivity; -import io.github.wulkanowy.api.Cookies; -import io.github.wulkanowy.api.StudentAndParent; import io.github.wulkanowy.api.login.AccountPermissionException; import io.github.wulkanowy.api.login.BadCredentialsException; -import io.github.wulkanowy.api.login.Login; import io.github.wulkanowy.api.login.LoginErrorException; -import io.github.wulkanowy.api.user.BasicInformation; -import io.github.wulkanowy.api.user.PersonalData; -import io.github.wulkanowy.database.accounts.Account; -import io.github.wulkanowy.database.accounts.AccountsDatabase; -import io.github.wulkanowy.database.cookies.CookiesDatabase; import io.github.wulkanowy.security.CryptoException; -import io.github.wulkanowy.security.Safety; +import io.github.wulkanowy.services.jobs.GradesSync; +import io.github.wulkanowy.services.synchronisation.DataSynchronisation; +import io.github.wulkanowy.services.synchronisation.VulcanSynchronisation; +import io.github.wulkanowy.utilities.ConnectionUtilities; public class LoginTask extends AsyncTask { - private Activity activity; - - private boolean save; + private Context context; private ProgressDialog progress; - public LoginTask(Activity activity, boolean save) { - this.activity = activity; - this.save = save; - - this.progress = new ProgressDialog(activity); + public LoginTask(Context context) { + this.context = context; + this.progress = new ProgressDialog(context); } @Override protected void onPreExecute() { super.onPreExecute(); - progress.setTitle(activity.getText(R.string.login_text)); - progress.setMessage(activity.getText(R.string.please_wait_text)); + progress.setTitle(context.getText(R.string.login_text)); + progress.setMessage(context.getText(R.string.please_wait_text)); progress.setCancelable(false); progress.show(); } @Override protected Integer doInBackground(String... credentials) { - Cookies cookies = new Cookies(); - Login login = new Login(cookies); - try { - login.login(credentials[0], credentials[1], credentials[2]); - } catch (BadCredentialsException e) { - return R.string.login_bad_credentials_text; - } catch (AccountPermissionException e) { - return R.string.login_bad_account_permission_text; - } catch (LoginErrorException e) { - return R.string.login_denied_text; - } - try { - Gson gson = new GsonBuilder().enableComplexMapKeySerialization() - .setPrettyPrinting().create(); - CookiesDatabase cookiesDatabase = new CookiesDatabase(activity); - cookiesDatabase.open(); - cookiesDatabase.put(gson.toJson(login.getCookies())); - cookiesDatabase.close(); - } catch (SQLException e) { - return R.string.login_cookies_save_failed_text; - } - - if (save) { + if (ConnectionUtilities.isOnline(context)) { + VulcanSynchronisation vulcanSynchronisation = new VulcanSynchronisation(); try { - StudentAndParent snp = new StudentAndParent(login.getCookiesObject(), - credentials[2]); - BasicInformation userInfo = new BasicInformation(snp); - PersonalData personalData = userInfo.getPersonalData(); - String firstAndLastName = personalData.getFirstAndLastName(); - - Safety safety = new Safety(activity); - - Account account = new Account() - .setName(firstAndLastName) - .setEmail(credentials[0]) - .setPassword(safety.encrypt(credentials[0], credentials[1])) - .setSymbol(credentials[2]); - - AccountsDatabase accountsDatabase = new AccountsDatabase(activity); - - accountsDatabase.open(); - long idUser = accountsDatabase.put(account); - accountsDatabase.close(); - - SharedPreferences sharedPreferences = activity.getSharedPreferences("LoginData", activity.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putLong("isLogin", idUser); - editor.apply(); - - } catch (SQLException e) { - return R.string.SQLite_ioError_text; - } catch (IOException | LoginErrorException e) { - return R.string.login_denied_text; + vulcanSynchronisation.loginNewUser(credentials[0], credentials[1], credentials[2], context); + } catch (BadCredentialsException e) { + return R.string.login_bad_credentials_text; + } catch (AccountPermissionException e) { + return R.string.login_bad_account_permission_text; } catch (CryptoException e) { return R.string.encrypt_failed_text; - } catch (UnsupportedOperationException e) { - return R.string.root_failed_text; + } catch (LoginErrorException | IOException e) { + return R.string.login_denied_text; } - } - //Map cookiesList = login.getJar(); - return R.string.login_accepted_text; + DataSynchronisation dataSynchronisation = new DataSynchronisation(context); + dataSynchronisation.syncGradesAndSubjects(vulcanSynchronisation); + + return R.string.login_accepted_text; + + } else { + return R.string.noInternet_text; + } } protected void onPostExecute(Integer messageID) { super.onPostExecute(messageID); + GradesSync gradesSync = new GradesSync(); + gradesSync.scheduledJob(context); + progress.dismiss(); - Toast.makeText(activity, activity.getString(messageID), Toast.LENGTH_LONG).show(); + Toast.makeText(context, context.getString(messageID), Toast.LENGTH_LONG).show(); if (messageID == R.string.login_accepted_text || messageID == R.string.root_failed_text || messageID == R.string.encrypt_failed_text) { - Intent intent = new Intent(activity, DashboardActivity.class); - activity.startActivity(intent); + Intent intent = new Intent(context, DashboardActivity.class); + context.startActivity(intent); } } } diff --git a/app/src/main/java/io/github/wulkanowy/activity/main/MainActivity.java b/app/src/main/java/io/github/wulkanowy/activity/main/MainActivity.java index 28b17d21..a7ee5f1b 100644 --- a/app/src/main/java/io/github/wulkanowy/activity/main/MainActivity.java +++ b/app/src/main/java/io/github/wulkanowy/activity/main/MainActivity.java @@ -20,6 +20,7 @@ import io.github.wulkanowy.R; public class MainActivity extends AppCompatActivity { private float mTouchPosition; + private float mReleasePosition; @Override @@ -71,7 +72,7 @@ public class MainActivity extends AppCompatActivity { } if (!email.isEmpty() && !password.isEmpty() && !symbol.isEmpty()) { - new LoginTask(this, true).execute(email, password, symbol); + new LoginTask(this).execute(email, password, symbol); } else { Toast.makeText(this, R.string.data_text, Toast.LENGTH_SHORT).show(); } diff --git a/app/src/main/java/io/github/wulkanowy/activity/started/LoadingTask.java b/app/src/main/java/io/github/wulkanowy/activity/started/LoadingTask.java index 64dc1cb3..363d747f 100644 --- a/app/src/main/java/io/github/wulkanowy/activity/started/LoadingTask.java +++ b/app/src/main/java/io/github/wulkanowy/activity/started/LoadingTask.java @@ -1,36 +1,26 @@ package io.github.wulkanowy.activity.started; -import android.app.Activity; +import android.content.Context; import android.content.Intent; -import android.database.SQLException; import android.os.AsyncTask; import android.widget.Toast; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketAddress; - import io.github.wulkanowy.R; -import io.github.wulkanowy.activity.main.LoginTask; +import io.github.wulkanowy.activity.dashboard.DashboardActivity; import io.github.wulkanowy.activity.main.MainActivity; -import io.github.wulkanowy.database.accounts.Account; -import io.github.wulkanowy.database.accounts.AccountsDatabase; -import io.github.wulkanowy.security.CryptoException; -import io.github.wulkanowy.security.Safety; +import io.github.wulkanowy.services.jobs.GradesSync; +import io.github.wulkanowy.utilities.ConnectionUtilities; -public class LoadingTask extends AsyncTask { +public class LoadingTask extends AsyncTask { - private final boolean SAVE_DATA = true; - private Activity activity; - private boolean isOnline; + private Context context; - LoadingTask(Activity main) { - activity = main; + LoadingTask(Context context) { + this.context = context; } @Override - protected Void doInBackground(Void... voids) { + protected Boolean doInBackground(Void... voids) { try { Thread.sleep(500); @@ -38,74 +28,27 @@ public class LoadingTask extends AsyncTask { e.printStackTrace(); } - isOnline = isOnline(); - - return null; + return ConnectionUtilities.isOnline(context); } - protected void onPostExecute(Void result) { + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); - if (isOnline) { - signIn(); + if (!result) { + Toast.makeText(context, R.string.noInternet_text, Toast.LENGTH_LONG).show(); + } + + if (context.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("isLogin", 0) == 0) { + Intent intent = new Intent(context, MainActivity.class); + context.startActivity(intent); } else { - Intent intent = new Intent(activity, MainActivity.class); - activity.startActivity(intent); + GradesSync gradesSync = new GradesSync(); + gradesSync.scheduledJob(context); - Toast.makeText(activity, R.string.noInternet_text, Toast.LENGTH_SHORT).show(); - } - } - - private boolean isOnline() { - try { - int timeoutMs = 1500; - Socket sock = new Socket(); - SocketAddress address = new InetSocketAddress("8.8.8.8", 53); - - sock.connect(address, timeoutMs); - sock.close(); - - return true; - } catch (IOException e) { - return false; - } - } - - private boolean signIn() { - - if (SAVE_DATA) { - AccountsDatabase accountsDatabase = new AccountsDatabase(activity); - accountsDatabase.open(); - - if (accountsDatabase.checkExist("accounts")) { - try { - Account account = accountsDatabase.getAccount(activity.getSharedPreferences("LoginData", activity.MODE_PRIVATE).getLong("isLogin", 0)); - accountsDatabase.close(); - - if (account != null) { - - Safety safety = new Safety(activity); - - new LoginTask(activity, false).execute( - account.getEmail(), - safety.decrypt(account.getEmail(), account.getPassword()), - account.getSymbol() - ); - - return true; - } - } catch (SQLException e) { - Toast.makeText(activity, R.string.SQLite_ioError_text, - Toast.LENGTH_LONG).show(); - } catch (CryptoException e) { - Toast.makeText(activity, R.string.decrypt_failed_text, Toast.LENGTH_LONG).show(); - } - } - accountsDatabase.close(); + Intent intent = new Intent(context, DashboardActivity.class); + context.startActivity(intent); } - Intent intent = new Intent(activity, MainActivity.class); - activity.startActivity(intent); - return false; } } diff --git a/app/src/main/java/io/github/wulkanowy/database/DatabaseAdapter.java b/app/src/main/java/io/github/wulkanowy/database/DatabaseAdapter.java index 65da3f29..82fa0fe2 100644 --- a/app/src/main/java/io/github/wulkanowy/database/DatabaseAdapter.java +++ b/app/src/main/java/io/github/wulkanowy/database/DatabaseAdapter.java @@ -10,7 +10,7 @@ public class DatabaseAdapter { private final String DATABASE_NAME = "accountdatabase.db"; - private final int DATABASE_VERSION = 5; + private final int DATABASE_VERSION = 6; public static SQLiteDatabase database; @@ -44,7 +44,7 @@ public class DatabaseAdapter { Log.d(DatabaseHelper.DEBUG_TAG, "Close database"); } - public boolean checkExist(String tableName, String dbfield, String fieldValue) { + protected boolean checkExist(String tableName, String dbfield, String fieldValue) { Cursor cursor; @@ -73,17 +73,16 @@ public class DatabaseAdapter { return false; } - public boolean checkExist(String tableName) { + protected boolean checkExist(String tableName) { return checkExist(tableName, null, null); } - public void deleteAndCreate(String tableName) { + protected void deleteAndCreate(String tableName) { database.execSQL(databaseHelper.DROP_TABLE + tableName); database.execSQL(databaseHelper.SUBJECT_TABLE); database.execSQL(databaseHelper.ACCOUNT_TABLE); database.execSQL(databaseHelper.GRADE_TABLE); - database.execSQL(databaseHelper.COOKIES_TABLE); Log.d(DatabaseHelper.DEBUG_TAG, "Recreate table " + tableName); diff --git a/app/src/main/java/io/github/wulkanowy/database/DatabaseHelper.java b/app/src/main/java/io/github/wulkanowy/database/DatabaseHelper.java index 7a2ae7af..3011eff6 100644 --- a/app/src/main/java/io/github/wulkanowy/database/DatabaseHelper.java +++ b/app/src/main/java/io/github/wulkanowy/database/DatabaseHelper.java @@ -40,10 +40,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { "semester INTEGER, " + "isNew INTEGER );"; - public final String COOKIES_TABLE = "CREATE TABLE IF NOT EXISTS cookies( " + - "id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "cookies TEXT );"; - public final String DROP_TABLE = "DROP TABLE IF EXISTS "; public DatabaseHelper(Context context, String name, CursorFactory factory, int version) { @@ -55,7 +51,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { db.execSQL(ACCOUNT_TABLE); db.execSQL(SUBJECT_TABLE); db.execSQL(GRADE_TABLE); - db.execSQL(COOKIES_TABLE); Log.d(DEBUG_TAG, "Create database"); } @@ -64,7 +59,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { db.execSQL(DROP_TABLE + "accounts"); db.execSQL(DROP_TABLE + "subjects"); db.execSQL(DROP_TABLE + "grades"); - db.execSQL(DROP_TABLE + "cookies"); onCreate(db); Log.d(DEBUG_TAG, "Database upgrade from ver." + oldVersion + " to ver." + newVersion); } diff --git a/app/src/main/java/io/github/wulkanowy/database/accounts/AccountsDatabase.java b/app/src/main/java/io/github/wulkanowy/database/accounts/AccountsDatabase.java index fd40521e..b367bdba 100644 --- a/app/src/main/java/io/github/wulkanowy/database/accounts/AccountsDatabase.java +++ b/app/src/main/java/io/github/wulkanowy/database/accounts/AccountsDatabase.java @@ -13,10 +13,15 @@ import io.github.wulkanowy.database.DatabaseHelper; public class AccountsDatabase extends DatabaseAdapter { private String name = "name"; + private String email = "email"; + private String password = "password"; + private String symbol = "symbol"; + private String idText = "id"; + private String accounts = "accounts"; public AccountsDatabase(Context context) { diff --git a/app/src/main/java/io/github/wulkanowy/database/cookies/CookiesDatabase.java b/app/src/main/java/io/github/wulkanowy/database/cookies/CookiesDatabase.java deleted file mode 100644 index 6cd40b06..00000000 --- a/app/src/main/java/io/github/wulkanowy/database/cookies/CookiesDatabase.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.database.cookies; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.SQLException; -import android.util.Log; - -import io.github.wulkanowy.database.DatabaseAdapter; -import io.github.wulkanowy.database.DatabaseHelper; - -public class CookiesDatabase extends DatabaseAdapter { - - private String cookies = "cookies"; - - public CookiesDatabase(Context context) { - super(context); - } - - public long put(String serializableCookiesMap) { - - ContentValues newCookie = new ContentValues(); - newCookie.put(cookies, serializableCookiesMap); - - if (!database.isReadOnly()) { - if (!checkExist(cookies)) { - long newId = database.insertOrThrow(cookies, null, newCookie); - Log.d(DatabaseHelper.DEBUG_TAG, "Put cookies into database"); - return newId; - } else { - deleteAndCreate(cookies); - long newId = database.insertOrThrow(cookies, null, newCookie); - Log.d(DatabaseHelper.DEBUG_TAG, "Put cookies into database"); - return newId; - } - } else { - Log.e(DatabaseHelper.DEBUG_TAG, "Attempt to write on read-only database"); - throw new SQLException("Attempt to write on read-only database"); - } - } - - public String getCookies() { - - String exec = "SELECT " + cookies + " FROM " + cookies; - - Cursor cursor = database.rawQuery(exec, null); - - cursor.moveToFirst(); - - String cookie = cursor.getString(0); - - cursor.close(); - - Log.d(DatabaseHelper.DEBUG_TAG, "Extract cookies from database"); - - return cookie; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/database/subjects/SubjectsDatabase.java b/app/src/main/java/io/github/wulkanowy/database/subjects/SubjectsDatabase.java index 4b8d99ae..093b9999 100644 --- a/app/src/main/java/io/github/wulkanowy/database/subjects/SubjectsDatabase.java +++ b/app/src/main/java/io/github/wulkanowy/database/subjects/SubjectsDatabase.java @@ -18,11 +18,17 @@ import io.github.wulkanowy.database.DatabaseHelper; public class SubjectsDatabase extends DatabaseAdapter { private static String idText = "id"; + private static String name = "name"; + private static String predictedRating1 = "predictedRating1"; + private static String finalRating1 = "finalRating1"; + private static String predictedRating2 = "predictedRating2"; + private static String finalRating2 = "finalRating2"; + private static String subjects = "subjects"; public SubjectsDatabase(Context context) { diff --git a/app/src/main/java/io/github/wulkanowy/security/Scrambler.java b/app/src/main/java/io/github/wulkanowy/security/Scrambler.java index 33df50b4..1bf344c7 100644 --- a/app/src/main/java/io/github/wulkanowy/security/Scrambler.java +++ b/app/src/main/java/io/github/wulkanowy/security/Scrambler.java @@ -31,8 +31,11 @@ import javax.security.auth.x500.X500Principal; public class Scrambler { private KeyStore keyStore; + private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; + public final static String DEBUG_TAG = "KeyStoreSecurity"; + public Context context; public Scrambler(Context context) { diff --git a/app/src/main/java/io/github/wulkanowy/services/jobs/GradesSync.java b/app/src/main/java/io/github/wulkanowy/services/jobs/GradesSync.java new file mode 100644 index 00000000..29a0835b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/jobs/GradesSync.java @@ -0,0 +1,53 @@ +package io.github.wulkanowy.services.jobs; + +import com.firebase.jobdispatcher.Constraint; +import com.firebase.jobdispatcher.FirebaseJobDispatcher; +import com.firebase.jobdispatcher.Job; +import com.firebase.jobdispatcher.Lifetime; +import com.firebase.jobdispatcher.RetryStrategy; +import com.firebase.jobdispatcher.Trigger; + +import java.io.IOException; + +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.LoginErrorException; +import io.github.wulkanowy.security.CryptoException; +import io.github.wulkanowy.services.synchronisation.DataSynchronisation; +import io.github.wulkanowy.services.synchronisation.VulcanSynchronisation; + +public class GradesSync extends VulcanSync { + + public static final String UNIQUE_TAG = "GradesSync34512"; + + public static final int DEFAULT_INTERVAL_START = 60 * 50; + + public static final int DEFAULT_INTERVAL_END = DEFAULT_INTERVAL_START + (60 * 10); + + @Override + protected Job createJob(FirebaseJobDispatcher dispatcher) { + return dispatcher.newJobBuilder() + .setLifetime(Lifetime.FOREVER) + .setService(GradeJob.class) + .setTag(UNIQUE_TAG) + .setRecurring(true) + .setTrigger(Trigger.executionWindow(DEFAULT_INTERVAL_START, DEFAULT_INTERVAL_END)) + .setConstraints(Constraint.ON_ANY_NETWORK) + .setReplaceCurrent(true) + .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) + .build(); + } + + public static class GradeJob extends VulcanJob { + + @Override + public void workToBePerformed() throws CryptoException, BadCredentialsException, + LoginErrorException, AccountPermissionException, IOException { + + VulcanSynchronisation vulcanSynchronisation = new VulcanSynchronisation(); + DataSynchronisation dataSynchronisation = new DataSynchronisation(getApplicationContext()); + vulcanSynchronisation.loginCurrentUser(getApplicationContext()); + dataSynchronisation.syncGrades(vulcanSynchronisation); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/jobs/SubjectsSync.java b/app/src/main/java/io/github/wulkanowy/services/jobs/SubjectsSync.java new file mode 100644 index 00000000..f6882fc8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/jobs/SubjectsSync.java @@ -0,0 +1,54 @@ +package io.github.wulkanowy.services.jobs; + +import com.firebase.jobdispatcher.Constraint; +import com.firebase.jobdispatcher.FirebaseJobDispatcher; +import com.firebase.jobdispatcher.Job; +import com.firebase.jobdispatcher.Lifetime; +import com.firebase.jobdispatcher.RetryStrategy; +import com.firebase.jobdispatcher.Trigger; + +import java.io.IOException; + +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.LoginErrorException; +import io.github.wulkanowy.security.CryptoException; +import io.github.wulkanowy.services.synchronisation.DataSynchronisation; +import io.github.wulkanowy.services.synchronisation.VulcanSynchronisation; + +public class SubjectsSync extends VulcanSync { + + public static final String UNIQUE_TAG = "SubjectsSync34512"; + + public static final int DEFAULT_INTERVAL_START = 0; + + public static final int DEFAULT_INTERVAL_END = DEFAULT_INTERVAL_START + 10; + + @Override + protected Job createJob(FirebaseJobDispatcher dispatcher) { + return dispatcher.newJobBuilder() + .setLifetime(Lifetime.UNTIL_NEXT_BOOT) + .setService(SubjectJob.class) + .setTag(UNIQUE_TAG) + .setRecurring(false) + .setTrigger(Trigger.executionWindow(DEFAULT_INTERVAL_START, DEFAULT_INTERVAL_END)) + .setConstraints(Constraint.ON_ANY_NETWORK) + .setReplaceCurrent(true) + .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) + .build(); + } + + private class SubjectJob extends VulcanJob { + + @Override + public void workToBePerformed() throws CryptoException, BadCredentialsException, + LoginErrorException, AccountPermissionException, IOException { + + VulcanSynchronisation vulcanSynchronisation = new VulcanSynchronisation(); + DataSynchronisation dataSynchronisation = new DataSynchronisation(getApplicationContext()); + vulcanSynchronisation.loginCurrentUser(getApplicationContext()); + dataSynchronisation.syncSubjects(vulcanSynchronisation); + + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/jobs/VulcanJob.java b/app/src/main/java/io/github/wulkanowy/services/jobs/VulcanJob.java new file mode 100644 index 00000000..9c787e57 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/jobs/VulcanJob.java @@ -0,0 +1,51 @@ +package io.github.wulkanowy.services.jobs; + +import android.os.AsyncTask; +import android.util.Log; + +import com.firebase.jobdispatcher.JobParameters; +import com.firebase.jobdispatcher.JobService; + +import java.io.IOException; + +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.LoginErrorException; +import io.github.wulkanowy.security.CryptoException; + +public abstract class VulcanJob extends JobService { + + private SyncTask syncTask = new SyncTask(); + + @Override + public boolean onStartJob(JobParameters params) { + Log.d(VulcanSync.DEBUG_TAG, "Start job"); + syncTask.execute(params); + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + Log.d(VulcanSync.DEBUG_TAG, "Stop job"); + syncTask.cancel(true); + return true; + } + + public abstract void workToBePerformed() throws CryptoException, BadCredentialsException, + LoginErrorException, AccountPermissionException, IOException; + + private class SyncTask extends AsyncTask { + + @Override + protected Void doInBackground(JobParameters... params) { + try { + workToBePerformed(); + } catch (Exception e) { + Log.e(VulcanSync.DEBUG_TAG, "User logging in the background failed", e); + } finally { + jobFinished(params[0], false); + } + return null; + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/jobs/VulcanSync.java b/app/src/main/java/io/github/wulkanowy/services/jobs/VulcanSync.java new file mode 100644 index 00000000..8ff1d485 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/jobs/VulcanSync.java @@ -0,0 +1,21 @@ +package io.github.wulkanowy.services.jobs; + + +import android.content.Context; + +import com.firebase.jobdispatcher.FirebaseJobDispatcher; +import com.firebase.jobdispatcher.GooglePlayDriver; +import com.firebase.jobdispatcher.Job; + +public abstract class VulcanSync { + + public static final String DEBUG_TAG = "SynchronizationService"; + + public void scheduledJob(Context context) { + FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context)); + dispatcher.mustSchedule(createJob(dispatcher)); + } + + protected abstract Job createJob(FirebaseJobDispatcher dispatcher); + +} diff --git a/app/src/main/java/io/github/wulkanowy/services/synchronisation/DataSynchronisation.java b/app/src/main/java/io/github/wulkanowy/services/synchronisation/DataSynchronisation.java new file mode 100644 index 00000000..d77607f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/synchronisation/DataSynchronisation.java @@ -0,0 +1,40 @@ +package io.github.wulkanowy.services.synchronisation; + +import android.content.Context; +import android.util.Log; + +import io.github.wulkanowy.services.jobs.VulcanSync; + +public class DataSynchronisation { + + private Context context; + + public DataSynchronisation(Context context) { + this.context = context; + } + + public void syncGrades(VulcanSynchronisation vulcanSynchronisation) { + GradesSynchronisation gradesSynchronisation = new GradesSynchronisation(); + + try { + gradesSynchronisation.sync(vulcanSynchronisation, context); + } catch (Exception e) { + Log.e(VulcanSync.DEBUG_TAG, "Synchronisation of grades failed", e); + } + } + + public void syncSubjects(VulcanSynchronisation vulcanSynchronisation) { + SubjectsSynchronisation subjectsSynchronisation = new SubjectsSynchronisation(); + + try { + subjectsSynchronisation.sync(vulcanSynchronisation, context); + } catch (Exception e) { + Log.e(VulcanSync.DEBUG_TAG, "Synchronisation of subjects failed", e); + } + } + + public void syncGradesAndSubjects(VulcanSynchronisation vulcanSynchronisation) { + syncSubjects(vulcanSynchronisation); + syncGrades(vulcanSynchronisation); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/synchronisation/GradesSynchronisation.java b/app/src/main/java/io/github/wulkanowy/services/synchronisation/GradesSynchronisation.java new file mode 100644 index 00000000..91507b9f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/synchronisation/GradesSynchronisation.java @@ -0,0 +1,23 @@ +package io.github.wulkanowy.services.synchronisation; + +import android.content.Context; + +import java.io.IOException; +import java.text.ParseException; + +import io.github.wulkanowy.api.grades.GradesList; +import io.github.wulkanowy.api.login.LoginErrorException; +import io.github.wulkanowy.database.grades.GradesDatabase; + +public class GradesSynchronisation { + + public void sync(VulcanSynchronisation vulcanSynchronisation, Context context) throws IOException, ParseException, LoginErrorException { + + GradesList gradesList = new GradesList(vulcanSynchronisation.getStudentAndParent()); + + GradesDatabase gradesDatabase = new GradesDatabase(context); + gradesDatabase.open(); + gradesDatabase.put(gradesList.getAll()); + gradesDatabase.close(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/synchronisation/SubjectsSynchronisation.java b/app/src/main/java/io/github/wulkanowy/services/synchronisation/SubjectsSynchronisation.java new file mode 100644 index 00000000..a3d33a0c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/synchronisation/SubjectsSynchronisation.java @@ -0,0 +1,23 @@ +package io.github.wulkanowy.services.synchronisation; + +import android.content.Context; + +import java.io.IOException; +import java.text.ParseException; + +import io.github.wulkanowy.api.grades.SubjectsList; +import io.github.wulkanowy.api.login.LoginErrorException; +import io.github.wulkanowy.database.subjects.SubjectsDatabase; + +public class SubjectsSynchronisation { + + public void sync(VulcanSynchronisation vulcanSynchronisation, Context context) throws IOException, ParseException, LoginErrorException { + + SubjectsList subjectsList = new SubjectsList(vulcanSynchronisation.getStudentAndParent()); + + SubjectsDatabase subjectsDatabase = new SubjectsDatabase(context); + subjectsDatabase.open(); + subjectsDatabase.put(subjectsList.getAll()); + subjectsDatabase.close(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/synchronisation/VulcanSynchronisation.java b/app/src/main/java/io/github/wulkanowy/services/synchronisation/VulcanSynchronisation.java new file mode 100644 index 00000000..e7114dfc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/synchronisation/VulcanSynchronisation.java @@ -0,0 +1,106 @@ +package io.github.wulkanowy.services.synchronisation; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import java.io.IOException; +import java.util.Map; + +import io.github.wulkanowy.api.Cookies; +import io.github.wulkanowy.api.StudentAndParent; +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.Login; +import io.github.wulkanowy.api.login.LoginErrorException; +import io.github.wulkanowy.api.user.BasicInformation; +import io.github.wulkanowy.api.user.PersonalData; +import io.github.wulkanowy.database.accounts.Account; +import io.github.wulkanowy.database.accounts.AccountsDatabase; +import io.github.wulkanowy.security.CryptoException; +import io.github.wulkanowy.security.Safety; +import io.github.wulkanowy.services.jobs.VulcanSync; + +public class VulcanSynchronisation { + + private StudentAndParent studentAndParent; + + public void loginCurrentUser(Context context) throws CryptoException, BadCredentialsException, LoginErrorException, AccountPermissionException, IOException { + + long userId = context.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("isLogin", 0); + + if (userId != 0) { + AccountsDatabase accountsDatabase = new AccountsDatabase(context); + accountsDatabase.open(); + Account account = accountsDatabase.getAccount(userId); + accountsDatabase.close(); + Safety safety = new Safety(context); + + Login login = loginUser( + account.getEmail(), + safety.decrypt(account.getEmail(), account.getPassword()), + account.getSymbol()); + + getAndSetStudentAndParentFromApi(account.getSymbol(), login.getCookies()); + } else { + Log.wtf(VulcanSync.DEBUG_TAG, "loginCurrentUser - USERID IS EMPTY"); + } + } + + public void loginNewUser(String email, String password, String symbol, Context context) throws BadCredentialsException, LoginErrorException, AccountPermissionException, IOException, CryptoException { + + Login login = loginUser(email, password, symbol); + + Safety safety = new Safety(context); + AccountsDatabase accountsDatabase = new AccountsDatabase(context); + BasicInformation basicInformation = new BasicInformation(getAndSetStudentAndParentFromApi(symbol, login.getCookies())); + PersonalData personalData = basicInformation.getPersonalData(); + + Account account = new Account() + .setName(personalData.getFirstAndLastName()) + .setEmail(email) + .setPassword(safety.encrypt(email, password)) + .setSymbol(symbol); + + accountsDatabase.open(); + long idNewUser = accountsDatabase.put(account); + accountsDatabase.close(); + + SharedPreferences sharedPreferences = context.getSharedPreferences("LoginData", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putLong("isLogin", idNewUser); + editor.apply(); + } + + public StudentAndParent getStudentAndParent() { + return studentAndParent; + } + + private void setStudentAndParent(StudentAndParent studentAndParent) { + this.studentAndParent = studentAndParent; + } + + private Login loginUser(String email, String password, String symbol) throws BadCredentialsException, LoginErrorException, AccountPermissionException { + + Cookies cookies = new Cookies(); + Login login = new Login(cookies); + login.login(email, password, symbol); + return login; + + } + + private StudentAndParent getAndSetStudentAndParentFromApi(String symbol, Map cookiesMap) throws IOException, LoginErrorException { + + if (studentAndParent == null) { + Cookies cookies = new Cookies(); + cookies.setItems(cookiesMap); + + StudentAndParent snp = new StudentAndParent(cookies, symbol); + + setStudentAndParent(snp); + return snp; + } else { + return studentAndParent; + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utilities/ConnectionUtilities.java b/app/src/main/java/io/github/wulkanowy/utilities/ConnectionUtilities.java new file mode 100644 index 00000000..aa8792e5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utilities/ConnectionUtilities.java @@ -0,0 +1,15 @@ +package io.github.wulkanowy.utilities; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +public abstract class ConnectionUtilities { + + public static boolean isOnline(Context context) { + ConnectivityManager connectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + return networkInfo != null && networkInfo.isConnectedOrConnecting(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utilities/DateHelper.java b/app/src/main/java/io/github/wulkanowy/utilities/DateHelper.java index 5677f721..bb759129 100644 --- a/app/src/main/java/io/github/wulkanowy/utilities/DateHelper.java +++ b/app/src/main/java/io/github/wulkanowy/utilities/DateHelper.java @@ -10,6 +10,7 @@ import java.util.TimeZone; abstract public class DateHelper { private static final long TICKS_AT_EPOCH = 621355968000000000L; + private static final long TICKS_PER_MILLISECOND = 10000; public static long getTicks(Date date) { diff --git a/app/src/main/java/io/github/wulkanowy/utilities/RootUtilities.java b/app/src/main/java/io/github/wulkanowy/utilities/RootUtilities.java index 596cd0e0..25840104 100644 --- a/app/src/main/java/io/github/wulkanowy/utilities/RootUtilities.java +++ b/app/src/main/java/io/github/wulkanowy/utilities/RootUtilities.java @@ -5,7 +5,7 @@ import android.os.Build; import java.io.File; -public class RootUtilities { +abstract public class RootUtilities { public static boolean isRooted() {