1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 02:29:08 -05:00

Add services (#21)

This commit is contained in:
RicomenPL 2017-09-10 12:50:28 +02:00 committed by Mikołaj Pich
parent 7b087144e9
commit 9dfb0bc729
25 changed files with 499 additions and 304 deletions

1
.gitignore vendored
View File

@ -22,6 +22,7 @@ local.properties
# User-specific configurations # User-specific configurations
.idea/copyright/profiles_settings.xml .idea/copyright/profiles_settings.xml
.idea/libraries/ .idea/libraries/
.idea/inspectionProfiles/
.idea/.name .idea/.name
.idea/compiler.xml .idea/compiler.xml
.idea/encodings.xml .idea/encodings.xml

View File

@ -42,6 +42,7 @@ dependencies {
compile 'com.thoughtbot:expandablerecyclerview:1.3' compile 'com.thoughtbot:expandablerecyclerview:1.3'
compile 'com.android.support:cardview-v7:25.3.1' compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.google.code.gson:gson:2.8.1' compile 'com.google.code.gson:gson:2.8.1'
compile 'com.firebase:firebase-jobdispatcher:0.8.1'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.9.0' testCompile 'org.mockito:mockito-core:2.9.0'

View File

@ -5,12 +5,13 @@
android:installLocation="internalOnly"> android:installLocation="internalOnly">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-sdk tools:overrideLibrary="com.thoughtbot.expandablerecyclerview, com.thoughtbot.expandablecheckrecyclerview" /> <uses-sdk tools:overrideLibrary="com.thoughtbot.expandablerecyclerview" />
<application <application
android:allowBackup="true" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
@ -18,9 +19,9 @@
android:theme="@style/WulkanowyTheme"> android:theme="@style/WulkanowyTheme">
<activity <activity
android:name=".activity.started.StartedActivity" android:name=".activity.started.StartedActivity"
android:configChanges="orientation|screenSize"
android:noHistory="true" android:noHistory="true"
android:theme="@style/WulkanowyTheme.noActionBar" android:theme="@style/WulkanowyTheme.noActionBar">
android:configChanges="orientation|screenSize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -29,13 +30,21 @@
</activity> </activity>
<activity <activity
android:name=".activity.main.MainActivity" android:name=".activity.main.MainActivity"
android:label="@string/login_text"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/login_text"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name=".activity.dashboard.DashboardActivity" android:name=".activity.dashboard.DashboardActivity"
android:label="@string/activity_dashboard_text" android:configChanges="orientation|screenSize"
android:configChanges="orientation|screenSize"/> android:label="@string/activity_dashboard_text" />
<service
android:name=".services.jobs.GradesSync$GradeJob"
android:exported="false">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
</intent-filter>
</service>
</application> </application>
</manifest> </manifest>

View File

@ -16,8 +16,11 @@ import io.github.wulkanowy.activity.dashboard.lessonplan.LessonPlanFragment;
public class DashboardActivity extends AppCompatActivity { public class DashboardActivity extends AppCompatActivity {
private GradesFragment gradesFragment = new GradesFragment(); private GradesFragment gradesFragment = new GradesFragment();
private AttendanceFragment attendanceFragment = new AttendanceFragment(); private AttendanceFragment attendanceFragment = new AttendanceFragment();
private BoardFragment boardFragment = new BoardFragment(); private BoardFragment boardFragment = new BoardFragment();
private LessonPlanFragment lessonPlanFragment = new LessonPlanFragment(); private LessonPlanFragment lessonPlanFragment = new LessonPlanFragment();
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener

View File

@ -10,23 +10,11 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import io.github.wulkanowy.R; 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.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.grades.GradesDatabase;
import io.github.wulkanowy.database.subjects.SubjectsDatabase; import io.github.wulkanowy.database.subjects.SubjectsDatabase;
@ -63,54 +51,23 @@ public class GradesFragment extends Fragment {
public class MarksTask extends AsyncTask<Void, Void, Void> { public class MarksTask extends AsyncTask<Void, Void, Void> {
private Context mContext; private Context context;
private Map<String, String> loginCookies = new HashMap<>();
MarksTask(Context context) { MarksTask(Context context) {
mContext = context; this.context = context;
} }
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
long userId = mContext.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("isLogin", 0);
try { SubjectsDatabase subjectsDatabase = new SubjectsDatabase(context);
Gson gson = new GsonBuilder().enableComplexMapKeySerialization() GradesDatabase gradesDatabase = new GradesDatabase(context);
.setPrettyPrinting().create();
CookiesDatabase cookiesDatabase = new CookiesDatabase(mContext);
cookiesDatabase.open();
loginCookies = gson.fromJson(cookiesDatabase.getCookies(), loginCookies.getClass());
cookiesDatabase.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
Cookies cookies = new Cookies();
cookies.setItems(loginCookies);
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.open();
gradesDatabase.put(gradesList.getAll());
for (Subject subject : subjectsDatabase.getAllSubjectsNames()) { for (Subject subject : subjectsDatabase.getAllSubjectsNames()) {
List<GradeItem> gradeItems = gradesDatabase.getSubjectGrades(userId, SubjectsDatabase.getSubjectId(subject.getName())); List<GradeItem> gradeItems = gradesDatabase.getSubjectGrades(context.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("isLogin", 0),
SubjectsDatabase.getSubjectId(subject.getName()));
if (gradeItems.size() > 0) { if (gradeItems.size() > 0) {
subjectWithGradesList.add(new SubjectWithGrades(subject.getName(), gradeItems)); subjectWithGradesList.add(new SubjectWithGrades(subject.getName(), gradeItems));
} }
@ -118,11 +75,6 @@ public class GradesFragment extends Fragment {
gradesDatabase.close(); gradesDatabase.close();
} catch (Exception e) {
e.printStackTrace();
}
return null; return null;
} }

View File

@ -1,137 +1,86 @@
package io.github.wulkanowy.activity.main; package io.github.wulkanowy.activity.main;
import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.database.SQLException;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.widget.Toast; import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException; import java.io.IOException;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.activity.dashboard.DashboardActivity; 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.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException; 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.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.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<String, Integer, Integer> { public class LoginTask extends AsyncTask<String, Integer, Integer> {
private Activity activity; private Context context;
private boolean save;
private ProgressDialog progress; private ProgressDialog progress;
public LoginTask(Activity activity, boolean save) { public LoginTask(Context context) {
this.activity = activity; this.context = context;
this.save = save; this.progress = new ProgressDialog(context);
this.progress = new ProgressDialog(activity);
} }
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
super.onPreExecute(); super.onPreExecute();
progress.setTitle(activity.getText(R.string.login_text)); progress.setTitle(context.getText(R.string.login_text));
progress.setMessage(activity.getText(R.string.please_wait_text)); progress.setMessage(context.getText(R.string.please_wait_text));
progress.setCancelable(false); progress.setCancelable(false);
progress.show(); progress.show();
} }
@Override @Override
protected Integer doInBackground(String... credentials) { protected Integer doInBackground(String... credentials) {
Cookies cookies = new Cookies();
Login login = new Login(cookies);
if (ConnectionUtilities.isOnline(context)) {
VulcanSynchronisation vulcanSynchronisation = new VulcanSynchronisation();
try { try {
login.login(credentials[0], credentials[1], credentials[2]); vulcanSynchronisation.loginNewUser(credentials[0], credentials[1], credentials[2], context);
} catch (BadCredentialsException e) { } catch (BadCredentialsException e) {
return R.string.login_bad_credentials_text; return R.string.login_bad_credentials_text;
} catch (AccountPermissionException e) { } catch (AccountPermissionException e) {
return R.string.login_bad_account_permission_text; 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) {
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;
} catch (CryptoException e) { } catch (CryptoException e) {
return R.string.encrypt_failed_text; return R.string.encrypt_failed_text;
} catch (UnsupportedOperationException e) { } catch (LoginErrorException | IOException e) {
return R.string.root_failed_text; return R.string.login_denied_text;
} }
}
//Map<String, String> cookiesList = login.getJar(); DataSynchronisation dataSynchronisation = new DataSynchronisation(context);
dataSynchronisation.syncGradesAndSubjects(vulcanSynchronisation);
return R.string.login_accepted_text; return R.string.login_accepted_text;
} else {
return R.string.noInternet_text;
}
} }
protected void onPostExecute(Integer messageID) { protected void onPostExecute(Integer messageID) {
super.onPostExecute(messageID); super.onPostExecute(messageID);
GradesSync gradesSync = new GradesSync();
gradesSync.scheduledJob(context);
progress.dismiss(); 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 if (messageID == R.string.login_accepted_text || messageID == R.string.root_failed_text
|| messageID == R.string.encrypt_failed_text) { || messageID == R.string.encrypt_failed_text) {
Intent intent = new Intent(activity, DashboardActivity.class); Intent intent = new Intent(context, DashboardActivity.class);
activity.startActivity(intent); context.startActivity(intent);
} }
} }
} }

View File

@ -20,6 +20,7 @@ import io.github.wulkanowy.R;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private float mTouchPosition; private float mTouchPosition;
private float mReleasePosition; private float mReleasePosition;
@Override @Override
@ -71,7 +72,7 @@ public class MainActivity extends AppCompatActivity {
} }
if (!email.isEmpty() && !password.isEmpty() && !symbol.isEmpty()) { if (!email.isEmpty() && !password.isEmpty() && !symbol.isEmpty()) {
new LoginTask(this, true).execute(email, password, symbol); new LoginTask(this).execute(email, password, symbol);
} else { } else {
Toast.makeText(this, R.string.data_text, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.data_text, Toast.LENGTH_SHORT).show();
} }

View File

@ -1,36 +1,26 @@
package io.github.wulkanowy.activity.started; package io.github.wulkanowy.activity.started;
import android.app.Activity; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.SQLException;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.widget.Toast; 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.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.activity.main.MainActivity;
import io.github.wulkanowy.database.accounts.Account; import io.github.wulkanowy.services.jobs.GradesSync;
import io.github.wulkanowy.database.accounts.AccountsDatabase; import io.github.wulkanowy.utilities.ConnectionUtilities;
import io.github.wulkanowy.security.CryptoException;
import io.github.wulkanowy.security.Safety;
public class LoadingTask extends AsyncTask<Void, Void, Void> { public class LoadingTask extends AsyncTask<Void, Void, Boolean> {
private final boolean SAVE_DATA = true; private Context context;
private Activity activity;
private boolean isOnline;
LoadingTask(Activity main) { LoadingTask(Context context) {
activity = main; this.context = context;
} }
@Override @Override
protected Void doInBackground(Void... voids) { protected Boolean doInBackground(Void... voids) {
try { try {
Thread.sleep(500); Thread.sleep(500);
@ -38,74 +28,27 @@ public class LoadingTask extends AsyncTask<Void, Void, Void> {
e.printStackTrace(); e.printStackTrace();
} }
isOnline = isOnline(); return ConnectionUtilities.isOnline(context);
return null;
} }
protected void onPostExecute(Void result) { protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (isOnline) { if (!result) {
signIn(); 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 { } else {
Intent intent = new Intent(activity, MainActivity.class); GradesSync gradesSync = new GradesSync();
activity.startActivity(intent); gradesSync.scheduledJob(context);
Toast.makeText(activity, R.string.noInternet_text, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(context, DashboardActivity.class);
} context.startActivity(intent);
} }
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(activity, MainActivity.class);
activity.startActivity(intent);
return false;
} }
} }

View File

@ -10,7 +10,7 @@ public class DatabaseAdapter {
private final String DATABASE_NAME = "accountdatabase.db"; private final String DATABASE_NAME = "accountdatabase.db";
private final int DATABASE_VERSION = 5; private final int DATABASE_VERSION = 6;
public static SQLiteDatabase database; public static SQLiteDatabase database;
@ -44,7 +44,7 @@ public class DatabaseAdapter {
Log.d(DatabaseHelper.DEBUG_TAG, "Close database"); 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; Cursor cursor;
@ -73,17 +73,16 @@ public class DatabaseAdapter {
return false; return false;
} }
public boolean checkExist(String tableName) { protected boolean checkExist(String tableName) {
return checkExist(tableName, null, null); return checkExist(tableName, null, null);
} }
public void deleteAndCreate(String tableName) { protected void deleteAndCreate(String tableName) {
database.execSQL(databaseHelper.DROP_TABLE + tableName); database.execSQL(databaseHelper.DROP_TABLE + tableName);
database.execSQL(databaseHelper.SUBJECT_TABLE); database.execSQL(databaseHelper.SUBJECT_TABLE);
database.execSQL(databaseHelper.ACCOUNT_TABLE); database.execSQL(databaseHelper.ACCOUNT_TABLE);
database.execSQL(databaseHelper.GRADE_TABLE); database.execSQL(databaseHelper.GRADE_TABLE);
database.execSQL(databaseHelper.COOKIES_TABLE);
Log.d(DatabaseHelper.DEBUG_TAG, "Recreate table " + tableName); Log.d(DatabaseHelper.DEBUG_TAG, "Recreate table " + tableName);

View File

@ -40,10 +40,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
"semester INTEGER, " + "semester INTEGER, " +
"isNew 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 final String DROP_TABLE = "DROP TABLE IF EXISTS ";
public DatabaseHelper(Context context, String name, CursorFactory factory, int version) { 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(ACCOUNT_TABLE);
db.execSQL(SUBJECT_TABLE); db.execSQL(SUBJECT_TABLE);
db.execSQL(GRADE_TABLE); db.execSQL(GRADE_TABLE);
db.execSQL(COOKIES_TABLE);
Log.d(DEBUG_TAG, "Create database"); Log.d(DEBUG_TAG, "Create database");
} }
@ -64,7 +59,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
db.execSQL(DROP_TABLE + "accounts"); db.execSQL(DROP_TABLE + "accounts");
db.execSQL(DROP_TABLE + "subjects"); db.execSQL(DROP_TABLE + "subjects");
db.execSQL(DROP_TABLE + "grades"); db.execSQL(DROP_TABLE + "grades");
db.execSQL(DROP_TABLE + "cookies");
onCreate(db); onCreate(db);
Log.d(DEBUG_TAG, "Database upgrade from ver." + oldVersion + " to ver." + newVersion); Log.d(DEBUG_TAG, "Database upgrade from ver." + oldVersion + " to ver." + newVersion);
} }

View File

@ -13,10 +13,15 @@ import io.github.wulkanowy.database.DatabaseHelper;
public class AccountsDatabase extends DatabaseAdapter { public class AccountsDatabase extends DatabaseAdapter {
private String name = "name"; private String name = "name";
private String email = "email"; private String email = "email";
private String password = "password"; private String password = "password";
private String symbol = "symbol"; private String symbol = "symbol";
private String idText = "id"; private String idText = "id";
private String accounts = "accounts"; private String accounts = "accounts";
public AccountsDatabase(Context context) { public AccountsDatabase(Context context) {

View File

@ -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;
}
}

View File

@ -18,11 +18,17 @@ import io.github.wulkanowy.database.DatabaseHelper;
public class SubjectsDatabase extends DatabaseAdapter { public class SubjectsDatabase extends DatabaseAdapter {
private static String idText = "id"; private static String idText = "id";
private static String name = "name"; private static String name = "name";
private static String predictedRating1 = "predictedRating1"; private static String predictedRating1 = "predictedRating1";
private static String finalRating1 = "finalRating1"; private static String finalRating1 = "finalRating1";
private static String predictedRating2 = "predictedRating2"; private static String predictedRating2 = "predictedRating2";
private static String finalRating2 = "finalRating2"; private static String finalRating2 = "finalRating2";
private static String subjects = "subjects"; private static String subjects = "subjects";
public SubjectsDatabase(Context context) { public SubjectsDatabase(Context context) {

View File

@ -31,8 +31,11 @@ import javax.security.auth.x500.X500Principal;
public class Scrambler { public class Scrambler {
private KeyStore keyStore; private KeyStore keyStore;
private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
public final static String DEBUG_TAG = "KeyStoreSecurity"; public final static String DEBUG_TAG = "KeyStoreSecurity";
public Context context; public Context context;
public Scrambler(Context context) { public Scrambler(Context context) {

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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<JobParameters, Void, Void> {
@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;
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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<String, String> 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;
}
}
}

View File

@ -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();
}
}

View File

@ -10,6 +10,7 @@ import java.util.TimeZone;
abstract public class DateHelper { abstract public class DateHelper {
private static final long TICKS_AT_EPOCH = 621355968000000000L; private static final long TICKS_AT_EPOCH = 621355968000000000L;
private static final long TICKS_PER_MILLISECOND = 10000; private static final long TICKS_PER_MILLISECOND = 10000;
public static long getTicks(Date date) { public static long getTicks(Date date) {

View File

@ -5,7 +5,7 @@ import android.os.Build;
import java.io.File; import java.io.File;
public class RootUtilities { abstract public class RootUtilities {
public static boolean isRooted() { public static boolean isRooted() {