1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 00:49:10 -05:00

Add logger (#131)

This commit is contained in:
Mikołaj Pich 2018-06-06 19:38:54 +02:00 committed by Rafał
parent 0e16519baf
commit a06d114127
31 changed files with 201 additions and 128 deletions

View File

@ -32,6 +32,7 @@ dependencies {
implementation "org.jsoup:jsoup:$jsoup" implementation "org.jsoup:jsoup:$jsoup"
implementation "org.apache.commons:commons-lang3:$apacheLang" implementation "org.apache.commons:commons-lang3:$apacheLang"
implementation "com.google.code.gson:gson:$gson" implementation "com.google.code.gson:gson:$gson"
implementation "org.slf4j:slf4j-api:$slf4jApi"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.api;
import org.jsoup.Connection; import org.jsoup.Connection;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
@ -27,6 +29,8 @@ public class Client {
private Cookies cookies = new Cookies(); private Cookies cookies = new Cookies();
private static final Logger logger = LoggerFactory.getLogger(Client.class);
Client(String email, String password, String symbol) { Client(String email, String password, String symbol) {
this.email = email; this.email = email;
this.password = password; this.password = password;
@ -59,9 +63,13 @@ public class Client {
} }
this.symbol = new Login(this).login(email, password, symbol); this.symbol = new Login(this).login(email, password, symbol);
logger.info("Login successful on {} at {}", getHost(), new Date());
} }
private boolean isLoggedIn() { private boolean isLoggedIn() {
logger.debug("Last success request: {}", lastSuccessRequest);
logger.debug("Cookies: {}", getCookies().size());
return getCookies().size() > 0 && lastSuccessRequest != null && return getCookies().size() > 0 && lastSuccessRequest != null &&
5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); 5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
@ -75,10 +83,6 @@ public class Client {
this.symbol = symbol; this.symbol = symbol;
} }
public void addCookies(Map<String, String> items) {
cookies.addItems(items);
}
private Map<String, String> getCookies() { private Map<String, String> getCookies() {
return cookies.getItems(); return cookies.getItems();
} }
@ -111,7 +115,11 @@ public class Client {
this.cookies.addItems(cookies); this.cookies.addItems(cookies);
} }
Connection.Response response = Jsoup.connect(getFilledUrl(url)) url = getFilledUrl(url);
logger.debug("GET {}", url);
Connection.Response response = Jsoup.connect(url)
.followRedirects(true) .followRedirects(true)
.cookies(getCookies()) .cookies(getCookies())
.execute(); .execute();
@ -128,7 +136,11 @@ public class Client {
} }
public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
Connection connection = Jsoup.connect(getFilledUrl(url)); url = getFilledUrl(url);
logger.debug("POST {}", url);
Connection connection = Jsoup.connect(url);
for (String[] data : params) { for (String[] data : params) {
connection.data(data[0], data[1]); connection.data(data[0], data[1]);
@ -150,7 +162,11 @@ public class Client {
public String getJsonStringByUrl(String url) throws IOException, VulcanException { public String getJsonStringByUrl(String url) throws IOException, VulcanException {
login(); login();
Connection.Response response = Jsoup.connect(getFilledUrl(url)) url = getFilledUrl(url);
logger.debug("GET {}", url);
Connection.Response response = Jsoup.connect(url)
.followRedirects(true) .followRedirects(true)
.ignoreContentType(true) .ignoreContentType(true)
.cookies(getCookies()) .cookies(getCookies())
@ -164,7 +180,11 @@ public class Client {
public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException { public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException {
login(); login();
Connection connection = Jsoup.connect(getFilledUrl(url)); url = getFilledUrl(url);
logger.debug("POST {}", url);
Connection connection = Jsoup.connect(url);
for (String[] data : params) { for (String[] data : params) {
connection.data(data[0], data[1]); connection.data(data[0], data[1]);
@ -196,7 +216,7 @@ public class Client {
} }
if ("Błąd strony".equals(title)) { if ("Błąd strony".equals(title)) {
throw new NotLoggedInErrorException(title + " " + doc.selectFirst("p, body") + ", status: " + code); throw new NotLoggedInErrorException(title + " " + doc.selectFirst("body") + ", status: " + code);
} }
return doc; return doc;

View File

@ -14,9 +14,13 @@ fun getFormattedDate(date: String): String {
} }
fun getFormattedDate(date: String, format: String): String { fun getFormattedDate(date: String, format: String): String {
val sdf = SimpleDateFormat(LOG_DATE_PATTERN, Locale.ROOT) return getFormattedDate(date, LOG_DATE_PATTERN, format)
}
fun getFormattedDate(date: String, fromFormat: String, toFormat: String): String {
val sdf = SimpleDateFormat(fromFormat, Locale.ROOT)
val d = sdf.parse(date) val d = sdf.parse(date)
sdf.applyPattern(format) sdf.applyPattern(toFormat)
return sdf.format(d) return sdf.format(d)
} }

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.api;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
@ -32,6 +34,8 @@ public class StudentAndParent implements SnP {
private String diaryID; private String diaryID;
private static final Logger logger = LoggerFactory.getLogger(StudentAndParent.class);
StudentAndParent(Client client, String schoolID, String studentID, String diaryID) { StudentAndParent(Client client, String schoolID, String studentID, String diaryID) {
this.client = client; this.client = client;
this.schoolID = schoolID; this.schoolID = schoolID;
@ -43,6 +47,11 @@ public class StudentAndParent implements SnP {
if (null == getStudentID() || "".equals(getStudentID())) { if (null == getStudentID() || "".equals(getStudentID())) {
Document doc = client.getPageByUrl(getSnpHomePageUrl()); Document doc = client.getPageByUrl(getSnpHomePageUrl());
if (doc.select("#idSection").isEmpty()) {
logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body"));
throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title());
}
Student student = getCurrent(getStudents(doc)); Student student = getCurrent(getStudents(doc));
studentID = student.getId(); studentID = student.getId();
@ -72,24 +81,27 @@ public class StudentAndParent implements SnP {
// get url to uonetplus-opiekun.fakelog.cf // get url to uonetplus-opiekun.fakelog.cf
Document startPage = client.getPageByUrl(START_PAGE_URL); Document startPage = client.getPageByUrl(START_PAGE_URL);
Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first(); Elements studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a");
if (null == studentTileLink) { logger.debug("studentTileLink: {}", studentTileLink.size());
if (studentTileLink.isEmpty()) {
throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?"); throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?");
} }
String snpPageUrl = studentTileLink.attr("href"); String snpPageUrl = studentTileLink.last().attr("href");
this.schoolID = getExtractedIdFromUrl(snpPageUrl); this.schoolID = getExtractedIdFromUrl(snpPageUrl);
return snpPageUrl; return snpPageUrl;
} }
String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException { String getExtractedIdFromUrl(String snpPageUrl) throws VulcanException {
String[] path = snpPageUrl.split(client.getHost())[1].split("/"); String[] path = snpPageUrl.split(client.getHost())[1].split("/");
if (5 != path.length) { if (5 != path.length) {
throw new NotLoggedInErrorException("You are probably not logged in " + snpPageUrl); logger.error("Expected snp url, got {}", snpPageUrl);
throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?");
} }
return path[2]; return path[2];
@ -107,12 +119,12 @@ public class StudentAndParent implements SnP {
Map<String, String> cookies = new HashMap<>(); Map<String, String> cookies = new HashMap<>();
cookies.put("idBiezacyDziennik", diaryID); cookies.put("idBiezacyDziennik", diaryID);
cookies.put("idBiezacyUczen", studentID); cookies.put("idBiezacyUczen", studentID);
client.addCookies(cookies);
Document doc = client.getPageByUrl(getBaseUrl() + url, true, cookies); Document doc = client.getPageByUrl(getBaseUrl() + url, true, cookies);
if (!doc.title().startsWith("Witryna ucznia i rodzica")) { if (!doc.title().startsWith("Witryna ucznia i rodzica")) {
throw new VulcanException("Expected SnP page, got page with title: " + doc.title()); logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body"));
throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title());
} }
if (doc.title().endsWith("Strona główna")) { if (doc.title().endsWith("Strona główna")) {

View File

@ -1,5 +1,8 @@
package io.github.wulkanowy.api; package io.github.wulkanowy.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import io.github.wulkanowy.api.attendance.AttendanceStatistics; import io.github.wulkanowy.api.attendance.AttendanceStatistics;
@ -30,17 +33,21 @@ public class Vulcan {
private String diaryId; private String diaryId;
private static final Logger logger = LoggerFactory.getLogger(Vulcan.class);
public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) { public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) {
this.schoolId = schoolId; this.schoolId = schoolId;
this.studentId = studentId; this.studentId = studentId;
this.diaryId = diaryId; this.diaryId = diaryId;
client = new Client(email, password, symbol); client = new Client(email, password, symbol);
logger.debug("Client created with symbol " + symbol);
} }
public Client getClient() throws NotLoggedInErrorException { public Client getClient() throws NotLoggedInErrorException {
if (null == client) { if (null == client) {
throw new NotLoggedInErrorException("Use setCredentials() method first"); throw new NotLoggedInErrorException("Vulcan must be initialized by calling setCredentials() prior to fetch data");
} }
return client; return client;

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy.api.mobile package io.github.wulkanowy.api.mobile
import io.github.wulkanowy.api.SnP import io.github.wulkanowy.api.SnP
import java.text.SimpleDateFormat import io.github.wulkanowy.api.getFormattedDate
import java.util.*
class RegisteredDevices(private val snp: SnP) { class RegisteredDevices(private val snp: SnP) {
@ -28,7 +27,7 @@ class RegisteredDevices(private val snp: SnP) {
devices.add(Device( devices.add(Device(
cells[0].text().replace(" ($system)", ""), cells[0].text().replace(" ($system)", ""),
system, system,
formatDate(cells[1].text()), getFormattedDate(cells[1].text(), "dd.MM.yyyy 'godz:' HH:mm:ss", "yyyy-MM-dd HH:mm:ss"),
cells[2].select("a").attr("href") cells[2].select("a").attr("href")
.split("/").last().toInt() .split("/").last().toInt()
)) ))
@ -36,13 +35,4 @@ class RegisteredDevices(private val snp: SnP) {
return devices return devices
} }
// TODO: Move to date utils
private fun formatDate(date: String): String {
val sdf = SimpleDateFormat("dd.MM.yyyy 'godz:' HH:mm:ss", Locale.ROOT)
val d = sdf.parse(date)
sdf.applyPattern("yyyy-MM-dd HH:mm:ss")
return sdf.format(d)
}
} }

View File

@ -83,7 +83,7 @@ public class StudentAndParentTest {
snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/")); snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/"));
} }
@Test(expected = NotLoggedInErrorException.class) @Test(expected = VulcanException.class)
public void getExtractedIDNotLoggedTest() throws Exception { public void getExtractedIDNotLoggedTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
@ -131,8 +131,8 @@ public class StudentAndParentTest {
@Test @Test
public void getDiariesAndStudentTest() throws IOException, VulcanException { public void getDiariesAndStudentTest() throws IOException, VulcanException {
Document snpHome = Jsoup.parse(FixtureHelper.getAsString( String input = FixtureHelper.getAsString(getClass().getResourceAsStream("WitrynaUczniaIRodzica.html"));
getClass().getResourceAsStream("StudentAndParent.html"))); Document snpHome = Jsoup.parse(input);
client = Mockito.mock(Client.class); client = Mockito.mock(Client.class);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome); Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome);

View File

@ -118,6 +118,9 @@ dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP" implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP"
implementation "com.google.android.gms:play-services-oss-licenses:$ossLicenses" implementation "com.google.android.gms:play-services-oss-licenses:$ossLicenses"
implementation "com.jakewharton.timber:timber:$timber"
implementation "at.favre.lib:slf4j-timber:$slf4jTimber"
implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") { implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") {
transitive = true transitive = true
} }

View File

@ -12,11 +12,12 @@ import javax.inject.Inject;
import dagger.android.AndroidInjector; import dagger.android.AndroidInjector;
import dagger.android.support.DaggerApplication; import dagger.android.support.DaggerApplication;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.utils.Log;
import io.fabric.sdk.android.Fabric; import io.fabric.sdk.android.Fabric;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.DaggerAppComponent; import io.github.wulkanowy.di.DaggerAppComponent;
import io.github.wulkanowy.utils.LogUtils; import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.LoggerUtils;
import timber.log.Timber;
public class WulkanowyApp extends DaggerApplication { public class WulkanowyApp extends DaggerApplication {
@ -39,15 +40,18 @@ public class WulkanowyApp extends DaggerApplication {
if (repository.getSharedRepo().isUserLoggedIn()) { if (repository.getSharedRepo().isUserLoggedIn()) {
try { try {
repository.getSyncRepo().initLastUser(); repository.getSyncRepo().initLastUser();
FabricUtils.logLogin("Open app", true);
} catch (Exception e) { } catch (Exception e) {
LogUtils.error("An error occurred when the application was started", e); FabricUtils.logLogin("Open app", false);
Timber.e(e, "An error occurred when the application was started");
} }
} }
} }
private void enableDebugLog() { private void enableDebugLog() {
QueryBuilder.LOG_VALUES = true; QueryBuilder.LOG_VALUES = true;
FlexibleAdapter.enableLogs(Log.Level.DEBUG); FlexibleAdapter.enableLogs(eu.davidea.flexibleadapter.utils.Log.Level.DEBUG);
Timber.plant(new LoggerUtils.DebugLogTree());
} }
private void initializeFabric() { private void initializeFabric() {
@ -60,6 +64,7 @@ public class WulkanowyApp extends DaggerApplication {
) )
.debuggable(BuildConfig.DEBUG) .debuggable(BuildConfig.DEBUG)
.build()); .build());
Timber.plant(new LoggerUtils.CrashlyticsTree());
} }
@Override @Override

View File

@ -22,7 +22,7 @@ import io.github.wulkanowy.data.db.dao.migrations.Migration26;
import io.github.wulkanowy.data.db.dao.migrations.Migration27; import io.github.wulkanowy.data.db.dao.migrations.Migration27;
import io.github.wulkanowy.data.db.dao.migrations.Migration28; import io.github.wulkanowy.data.db.dao.migrations.Migration28;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
@Singleton @Singleton
public class DbHelper extends DaoMaster.OpenHelper { public class DbHelper extends DaoMaster.OpenHelper {
@ -41,7 +41,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
@Override @Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion); Timber.i("Cleaning user data oldVersion=%s newVersion=%s", oldVersion, newVersion);
Database database = new StandardDatabase(db); Database database = new StandardDatabase(db);
recreateDatabase(database); recreateDatabase(database);
} }
@ -54,11 +54,11 @@ public class DbHelper extends DaoMaster.OpenHelper {
for (Migration migration : migrations) { for (Migration migration : migrations) {
if (oldVersion < migration.getVersion()) { if (oldVersion < migration.getVersion()) {
try { try {
LogUtils.info("Applying migration to db schema v" + migration.getVersion() + "..."); Timber.i("Applying migration to db schema v%s...", migration.getVersion());
migration.runMigration(db, sharedPref, vulcan); migration.runMigration(db, sharedPref, vulcan);
LogUtils.info("Migration " + migration.getVersion() + " complete"); Timber.i("Migration %s complete", migration.getVersion());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); Timber.e(e, "Failed to apply migration");
recreateDatabase(db); recreateDatabase(db);
break; break;
} }
@ -67,7 +67,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
} }
private void recreateDatabase(Database db) { private void recreateDatabase(Database db) {
LogUtils.info("Database is recreating..."); Timber.i("Database is recreating...");
sharedPref.setCurrentUserId(0); sharedPref.setCurrentUserId(0);
DaoMaster.dropAllTables(db, true); DaoMaster.dropAllTables(db, true);
onCreate(db); onCreate(db);

View File

@ -9,8 +9,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.data.db.dao.DbHelper; import io.github.wulkanowy.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.security.Scrambler; import io.github.wulkanowy.utils.security.Scrambler;

View File

@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.resources;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import com.crashlytics.android.Crashlytics;
import java.io.IOException; import java.io.IOException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -16,8 +14,8 @@ import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException; import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.utils.AppConstant; import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException; import io.github.wulkanowy.utils.security.CryptoException;
import timber.log.Timber;
@Singleton @Singleton
public class ResourcesRepository implements ResourcesContract { public class ResourcesRepository implements ResourcesContract {
@ -41,8 +39,7 @@ public class ResourcesRepository implements ResourcesContract {
@Override @Override
public String getErrorLoginMessage(Exception exception) { public String getErrorLoginMessage(Exception exception) {
LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception); Timber.e(exception,"%s encountered a error", AppConstant.APP_NAME);
Crashlytics.logException(exception);
if (exception instanceof CryptoException) { if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text); return resources.getString(R.string.encrypt_failed_text);

View File

@ -24,9 +24,9 @@ import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao; import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException; import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Scrambler; import io.github.wulkanowy.utils.security.Scrambler;
import timber.log.Timber;
@Singleton @Singleton
public class AccountSync { public class AccountSync {
@ -73,7 +73,7 @@ public class AccountSync {
} }
private Account insertAccount(String email, String password) throws CryptoException { private Account insertAccount(String email, String password) throws CryptoException {
LogUtils.debug("Register account: " + email); Timber.d("Register account");
Account account = new Account() Account account = new Account()
.setEmail(email) .setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context)); .setPassword(Scrambler.encrypt(email, password, context));
@ -82,10 +82,11 @@ public class AccountSync {
} }
private Symbol insertSymbol(Account account) throws VulcanException, IOException { private Symbol insertSymbol(Account account) throws VulcanException, IOException {
LogUtils.debug("Register symbol: " + vulcan.getSymbol()); String schoolId = vulcan.getStudentAndParent().getSchoolID();
Timber.d("Register symbol %s", vulcan.getSymbol());
Symbol symbol = new Symbol() Symbol symbol = new Symbol()
.setUserId(account.getId()) .setUserId(account.getId())
.setSchoolId(vulcan.getStudentAndParent().getSchoolID()) .setSchoolId(schoolId)
.setSymbol(vulcan.getSymbol()); .setSymbol(vulcan.getSymbol());
daoSession.getSymbolDao().insert(symbol); daoSession.getSymbolDao().insert(symbol);
@ -97,7 +98,7 @@ public class AccountSync {
vulcan.getStudentAndParent().getStudents(), vulcan.getStudentAndParent().getStudents(),
symbol.getId() symbol.getId()
); );
LogUtils.debug("Register students: " + studentList.size()); Timber.d("Register students %s", studentList.size());
daoSession.getStudentDao().insertInTx(studentList); daoSession.getStudentDao().insertInTx(studentList);
} }
@ -108,7 +109,7 @@ public class AccountSync {
StudentDao.Properties.SymbolId.eq(symbolEntity.getId()), StudentDao.Properties.SymbolId.eq(symbolEntity.getId()),
StudentDao.Properties.Current.eq(true) StudentDao.Properties.Current.eq(true)
).unique().getId()); ).unique().getId());
LogUtils.debug("Register diaries: " + diaryList.size()); Timber.d("Register diaries %s", diaryList.size());
daoSession.getDiaryDao().insertInTx(diaryList); daoSession.getDiaryDao().insertInTx(diaryList);
} }
@ -118,7 +119,7 @@ public class AccountSync {
daoSession.getDiaryDao().queryBuilder().where( daoSession.getDiaryDao().queryBuilder().where(
DiaryDao.Properties.Current.eq(true) DiaryDao.Properties.Current.eq(true)
).unique().getId()); ).unique().getId());
LogUtils.debug("Register semesters: " + semesterList.size()); Timber.d("Register semesters %s", semesterList.size());
daoSession.getSemesterDao().insertInTx(semesterList); daoSession.getSemesterDao().insertInTx(semesterList);
} }
@ -130,7 +131,7 @@ public class AccountSync {
throw new NotRegisteredUserException("Can't find user id in SharedPreferences"); throw new NotRegisteredUserException("Can't find user id in SharedPreferences");
} }
LogUtils.debug("Initialization current user id=" + userId); Timber.d("Init current user (%s)", userId);
Account account = daoSession.getAccountDao().load(userId); Account account = daoSession.getAccountDao().load(userId);

View File

@ -18,7 +18,7 @@ import io.github.wulkanowy.data.db.dao.entities.DayDao;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
@Singleton @Singleton
public class AttendanceSync { public class AttendanceSync {
@ -47,7 +47,7 @@ public class AttendanceSync {
daoSession.getAttendanceLessonDao().saveInTx(lessonList); daoSession.getAttendanceLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization attendance lessons (amount = " + lessonList.size() + ")"); Timber.d("Attendance synchronization complete (%s)", lessonList.size());
} }
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date) private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)

View File

@ -17,7 +17,7 @@ import io.github.wulkanowy.data.db.dao.entities.ExamDao;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
public class ExamsSync { public class ExamsSync {
@ -45,7 +45,7 @@ public class ExamsSync {
daoSession.getExamDao().saveInTx(examList); daoSession.getExamDao().saveInTx(examList);
LogUtils.debug("Synchronization exams (amount = " + examList.size() + ")"); Timber.d("Exams synchronization complete (%s)", examList.size());
} }
private Week getWeekFromDb(String date) { private Week getWeekFromDb(String date) {

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync; package io.github.wulkanowy.data.sync;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -16,7 +15,7 @@ import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.SubjectDao; import io.github.wulkanowy.data.db.dao.entities.SubjectDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.EntitiesCompare; import io.github.wulkanowy.utils.EntitiesCompare;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
@Singleton @Singleton
public class GradeSync { public class GradeSync {
@ -33,7 +32,7 @@ public class GradeSync {
this.vulcan = vulcan; this.vulcan = vulcan;
} }
public void sync(long semesterId) throws IOException, VulcanException, ParseException { public void sync(long semesterId) throws IOException, VulcanException {
this.semesterId = semesterId; this.semesterId = semesterId;
Semester semester = daoSession.getSemesterDao().load(semesterId); Semester semester = daoSession.getSemesterDao().load(semesterId);
@ -44,7 +43,7 @@ public class GradeSync {
daoSession.getGradeDao().deleteInTx(semester.getGradeList()); daoSession.getGradeDao().deleteInTx(semester.getGradeList());
daoSession.getGradeDao().insertInTx(lastList); daoSession.getGradeDao().insertInTx(lastList);
LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")"); Timber.d("Grades synchronization complete (%s)", lastList.size());
} }
private void resetSemesterRelations(Semester semester) { private void resetSemesterRelations(Semester semester) {
@ -64,7 +63,7 @@ public class GradeSync {
return updatedList; return updatedList;
} }
private List<Grade> getComparedList(Semester semester) throws IOException, VulcanException, ParseException { private List<Grade> getComparedList(Semester semester) throws IOException, VulcanException {
List<Grade> gradesFromNet = DataObjectConverter.gradesToGradeEntities( List<Grade> gradesFromNet = DataObjectConverter.gradesToGradeEntities(
vulcan.getGradesList().getAll(semester.getValue()), semesterId); vulcan.getGradesList().getAll(semester.getValue()), semesterId);

View File

@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Semester; import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
@Singleton @Singleton
public class SubjectSync { public class SubjectSync {
@ -40,7 +40,7 @@ public class SubjectSync {
daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb()); daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb());
daoSession.getSubjectDao().insertInTx(lastList); daoSession.getSubjectDao().insertInTx(lastList);
LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")"); Timber.d("Subjects synchronization complete (%s)", lastList.size());
} }
private List<Subject> getSubjectsFromNet(Semester semester) throws VulcanException, IOException { private List<Subject> getSubjectsFromNet(Semester semester) throws VulcanException, IOException {

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync; package io.github.wulkanowy.data.sync;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -47,17 +46,17 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void initLastUser() throws IOException, CryptoException { public void initLastUser() throws CryptoException {
accountSync.initLastUser(); accountSync.initLastUser();
} }
@Override @Override
public void syncGrades(int semesterName) throws VulcanException, IOException, ParseException { public void syncGrades(int semesterName) throws VulcanException, IOException {
gradeSync.sync(semesterName); gradeSync.sync(semesterName);
} }
@Override @Override
public void syncGrades() throws VulcanException, IOException, ParseException { public void syncGrades() throws VulcanException, IOException {
gradeSync.sync(database.getCurrentSemesterId()); gradeSync.sync(database.getCurrentSemesterId());
} }
@ -72,12 +71,12 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncAttendance() throws ParseException, IOException, VulcanException { public void syncAttendance() throws IOException, VulcanException {
attendanceSync.syncAttendance(database.getCurrentDiaryId(), null); attendanceSync.syncAttendance(database.getCurrentDiaryId(), null);
} }
@Override @Override
public void syncAttendance(long diaryId, String date) throws ParseException, IOException, VulcanException { public void syncAttendance(long diaryId, String date) throws IOException, VulcanException {
if (diaryId != 0) { if (diaryId != 0) {
attendanceSync.syncAttendance(diaryId, date); attendanceSync.syncAttendance(diaryId, date);
} else { } else {
@ -86,12 +85,12 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncTimetable() throws VulcanException, IOException, ParseException { public void syncTimetable() throws VulcanException, IOException {
timetableSync.syncTimetable(database.getCurrentDiaryId(), null); timetableSync.syncTimetable(database.getCurrentDiaryId(), null);
} }
@Override @Override
public void syncTimetable(long diaryId, String date) throws VulcanException, IOException, ParseException { public void syncTimetable(long diaryId, String date) throws VulcanException, IOException {
if (diaryId != 0) { if (diaryId != 0) {
timetableSync.syncTimetable(diaryId, date); timetableSync.syncTimetable(diaryId, date);
} else { } else {
@ -100,12 +99,12 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncExams() throws VulcanException, IOException, ParseException { public void syncExams() throws VulcanException, IOException {
examsSync.syncExams(database.getCurrentDiaryId(), null); examsSync.syncExams(database.getCurrentDiaryId(), null);
} }
@Override @Override
public void syncExams(long diaryId, String date) throws VulcanException, IOException, ParseException { public void syncExams(long diaryId, String date) throws VulcanException, IOException {
if (diaryId != 0) { if (diaryId != 0) {
examsSync.syncExams(diaryId, date); examsSync.syncExams(diaryId, date);
} else { } else {
@ -114,7 +113,7 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncAll() throws VulcanException, IOException, ParseException { public void syncAll() throws VulcanException, IOException {
syncSubjects(); syncSubjects();
syncGrades(); syncGrades();
syncAttendance(); syncAttendance();

View File

@ -20,7 +20,7 @@ import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
@Singleton @Singleton
public class TimetableSync { public class TimetableSync {
@ -49,7 +49,7 @@ public class TimetableSync {
daoSession.getTimetableLessonDao().saveInTx(lessonList); daoSession.getTimetableLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization timetable lessons (amount = " + lessonList.size() + ")"); Timber.d("Timetable synchronization complete (%s)", lessonList.size());
} }
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> getWeekFromApi(String date) private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> getWeekFromApi(String date)

View File

@ -4,7 +4,6 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import com.crashlytics.android.Crashlytics;
import com.firebase.jobdispatcher.Constraint; import com.firebase.jobdispatcher.Constraint;
import com.firebase.jobdispatcher.FirebaseJobDispatcher; import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver; import com.firebase.jobdispatcher.GooglePlayDriver;
@ -27,7 +26,8 @@ import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.sync.NotRegisteredUserException; import io.github.wulkanowy.data.sync.NotRegisteredUserException;
import io.github.wulkanowy.services.notifies.GradeNotify; import io.github.wulkanowy.services.notifies.GradeNotify;
import io.github.wulkanowy.ui.main.MainActivity; import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.utils.LogUtils; import io.github.wulkanowy.utils.FabricUtils;
import timber.log.Timber;
public class SyncJob extends SimpleJobService { public class SyncJob extends SimpleJobService {
@ -74,13 +74,18 @@ public class SyncJob extends SimpleJobService {
if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) { if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) {
showNotification(); showNotification();
} }
FabricUtils.logLogin("Background", true);
return JobService.RESULT_SUCCESS; return JobService.RESULT_SUCCESS;
} catch (NotRegisteredUserException e) { } catch (NotRegisteredUserException e) {
logError(e); logError(e);
stop(getApplicationContext()); stop(getApplicationContext());
return JobService.RESULT_FAIL_NORETRY; return JobService.RESULT_FAIL_NORETRY;
} catch (Exception e) { } catch (Exception e) {
logError(e); logError(e);
return JobService.RESULT_FAIL_RETRY; return JobService.RESULT_FAIL_RETRY;
} }
} }
@ -122,7 +127,7 @@ public class SyncJob extends SimpleJobService {
} }
private void logError(Exception e) { private void logError(Exception e) {
Crashlytics.logException(e); FabricUtils.logLogin("Background", false);
LogUtils.error("During background synchronization an error occurred", e); Timber.e(e, "During background synchronization an error occurred");
} }
} }

View File

@ -85,7 +85,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
@Override @Override
public void onEndAsync(boolean success, Exception exception) { public void onEndAsync(boolean success, Exception exception) {
if (success) { if (success) {
FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol()); FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol(), "Success");
getView().openMainActivity(); getView().openMainActivity();
return; return;
} else if (exception instanceof BadCredentialsException) { } else if (exception instanceof BadCredentialsException) {
@ -95,7 +95,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
getView().setErrorSymbolRequired(); getView().setErrorSymbolRequired();
getView().showSoftInput(); getView().showSoftInput();
} else { } else {
FabricUtils.logRegister(false, symbol); FabricUtils.logRegister(false, symbol, exception.getMessage());
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
} }

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.ui.main.exams;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout; import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -13,7 +12,6 @@ import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.ui.base.BaseFragment; import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter; import io.github.wulkanowy.ui.base.BasePagerAdapter;

View File

@ -4,7 +4,6 @@ import android.os.Bundle;
import javax.inject.Inject; import javax.inject.Inject;
import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.services.notifies.NotificationService; import io.github.wulkanowy.services.notifies.NotificationService;
import io.github.wulkanowy.ui.base.BaseActivity; import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.login.LoginActivity; import io.github.wulkanowy.ui.login.LoginActivity;

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.utils;
import com.crashlytics.android.answers.Answers; import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.answers.CustomEvent; import com.crashlytics.android.answers.CustomEvent;
import com.crashlytics.android.answers.LoginEvent;
import com.crashlytics.android.answers.SignUpEvent; import com.crashlytics.android.answers.SignUpEvent;
public final class FabricUtils { public final class FabricUtils {
@ -10,17 +11,26 @@ public final class FabricUtils {
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
public static void logRegister(boolean result, String symbol) { public static void logLogin(String method, boolean result) {
Answers.getInstance().logLogin(new LoginEvent()
.putMethod(method)
.putSuccess(result)
);
}
public static void logRegister(boolean result, String symbol, String message) {
Answers.getInstance().logSignUp(new SignUpEvent() Answers.getInstance().logSignUp(new SignUpEvent()
.putMethod("Login activity") .putMethod("Login activity")
.putSuccess(result) .putSuccess(result)
.putCustomAttribute("symbol", symbol)); .putCustomAttribute("symbol", symbol)
.putCustomAttribute("message", message)
);
} }
public static void logRefresh(String name, boolean result, String date) { public static void logRefresh(String name, boolean result, String date) {
Answers.getInstance().logCustom( Answers.getInstance().logCustom(
new CustomEvent(name + " refresh") new CustomEvent(name + " refresh")
.putCustomAttribute("Success", result ? 1 : 0) .putCustomAttribute("Success", result ? "true" : "false")
.putCustomAttribute("Date", date) .putCustomAttribute("Date", date)
); );
} }

View File

@ -1,26 +0,0 @@
package io.github.wulkanowy.utils;
import android.util.Log;
public final class LogUtils {
private LogUtils() {
throw new IllegalStateException("Utility class");
}
public static void debug(String message) {
Log.d(AppConstant.APP_NAME, message);
}
public static void error(String message, Throwable throwable) {
Log.e(AppConstant.APP_NAME, message, throwable);
}
public static void error(String message) {
Log.e(AppConstant.APP_NAME, message);
}
public static void info(String message) {
Log.i(AppConstant.APP_NAME, message);
}
}

View File

@ -0,0 +1,47 @@
package io.github.wulkanowy.utils;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
import timber.log.Timber;
public final class LoggerUtils {
public static class CrashlyticsTree extends Timber.Tree {
@Override
protected void log(int priority, @Nullable String tag, @Nullable String message, @Nullable Throwable t) {
Crashlytics.setInt("priority", priority);
Crashlytics.setString("tag", tag);
if (t == null) {
Crashlytics.log(message);
} else {
Crashlytics.setString("message", message);
Crashlytics.logException(t);
}
}
}
public static class DebugLogTree extends Timber.DebugTree {
@Override
protected void log(int priority, String tag, @NonNull String message, Throwable t) {
if ("HUAWEI".equals(Build.MANUFACTURER) || "samsung".equals(Build.MANUFACTURER)) {
if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) {
priority = Log.ERROR;
}
}
super.log(priority, AppConstant.APP_NAME, message, t);
}
@Override
protected String createStackElementTag(@NonNull StackTraceElement element) {
return super.createStackElementTag(element) + " - " + element.getLineNumber();
}
}
}

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.utils.async;
import android.os.AsyncTask; import android.os.AsyncTask;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
public class AbstractTask extends AsyncTask<Void, Integer, Boolean> { public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
@ -28,7 +28,7 @@ public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
} else if (onRefreshListener != null) { } else if (onRefreshListener != null) {
onRefreshListener.onDoInBackgroundRefresh(); onRefreshListener.onDoInBackgroundRefresh();
} else { } else {
LogUtils.error("AbstractTask does not have a listener assigned"); Timber.e("AbstractTask does not have a listener assigned");
} }
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@ -45,7 +45,7 @@ public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
} else if (onRefreshListener != null) { } else if (onRefreshListener != null) {
onRefreshListener.onCanceledRefreshAsync(); onRefreshListener.onCanceledRefreshAsync();
} else { } else {
LogUtils.error("AbstractTask does not have a listener assigned"); Timber.e("AbstractTask does not have a listener assigned");
} }
} }
@ -57,7 +57,7 @@ public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
} else if (onRefreshListener != null) { } else if (onRefreshListener != null) {
onRefreshListener.onEndRefreshAsync(result, exception); onRefreshListener.onEndRefreshAsync(result, exception);
} else { } else {
LogUtils.error("AbstractTask does not have a listener assigned"); Timber.e("AbstractTask does not have a listener assigned");
} }
} }
} }

View File

@ -26,7 +26,7 @@ import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream; import javax.crypto.CipherOutputStream;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
public final class Scrambler { public final class Scrambler {
@ -111,7 +111,7 @@ public final class Scrambler {
throw new CryptoException("GenerateNewKey - String is empty"); throw new CryptoException("GenerateNewKey - String is empty");
} }
LogUtils.debug("Key pair are create"); Timber.d("Key pair are create");
} }

View File

@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.dao.entities;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import io.github.wulkanowy.R;
public class GradeTest { public class GradeTest {
@Test @Test

View File

@ -50,6 +50,10 @@ ext {
gson = "2.8.5" gson = "2.8.5"
ossLicenses = "15.0.1" ossLicenses = "15.0.1"
slf4jApi = "1.7.25"
slf4jTimber = "1.0.1"
timber = "4.7.0"
debugDb = "1.0.3" debugDb = "1.0.3"
sqlcipher = "3.5.9" sqlcipher = "3.5.9"