package pl.szczodrzynski.edziennik.api; import android.app.Activity; import android.appwidget.AppWidgetManager; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.text.Html; import android.util.Base64; import android.util.Log; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.WebView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.danimahardhika.cafebar.CafeBar; import com.google.android.gms.common.util.ArrayUtils; import com.google.firebase.iid.FirebaseInstanceId; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.WidgetTimetable; import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface; import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback; import pl.szczodrzynski.edziennik.datamodels.AnnouncementFull; import pl.szczodrzynski.edziennik.datamodels.Attendance; import pl.szczodrzynski.edziennik.datamodels.AttendanceFull; import pl.szczodrzynski.edziennik.datamodels.Event; import pl.szczodrzynski.edziennik.datamodels.EventFull; import pl.szczodrzynski.edziennik.datamodels.EventType; import pl.szczodrzynski.edziennik.datamodels.GradeFull; import pl.szczodrzynski.edziennik.datamodels.LessonFull; import pl.szczodrzynski.edziennik.datamodels.LoginStore; import pl.szczodrzynski.edziennik.datamodels.Message; import pl.szczodrzynski.edziennik.datamodels.MessageFull; import pl.szczodrzynski.edziennik.datamodels.Metadata; import pl.szczodrzynski.edziennik.datamodels.Notice; import pl.szczodrzynski.edziennik.datamodels.NoticeFull; import pl.szczodrzynski.edziennik.datamodels.Profile; import pl.szczodrzynski.edziennik.datamodels.ProfileFull; import pl.szczodrzynski.edziennik.datamodels.Team; import pl.szczodrzynski.edziennik.models.Date; import pl.szczodrzynski.edziennik.models.Notification; import pl.szczodrzynski.edziennik.network.ServerRequest; import pl.szczodrzynski.edziennik.sync.SyncJob; import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber; import pl.szczodrzynski.edziennik.widgets.notifications.WidgetNotifications; import static android.content.Context.CLIPBOARD_SERVICE; import static com.mikepenz.iconics.utils.IconicsConvertersKt.colorInt; import static com.mikepenz.iconics.utils.IconicsConvertersKt.sizeDp; import static pl.szczodrzynski.edziennik.App.APP_URL; import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_HOME; import static pl.szczodrzynski.edziennik.api.AppError.CODE_OK; import static pl.szczodrzynski.edziennik.api.AppError.CODE_OTHER; import static pl.szczodrzynski.edziennik.api.AppError.CODE_PROFILE_ARCHIVED; import static pl.szczodrzynski.edziennik.api.AppError.CODE_PROFILE_NOT_FOUND; import static pl.szczodrzynski.edziennik.api.AppError.stringErrorCode; import static pl.szczodrzynski.edziennik.api.AppError.stringErrorType; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_AGENDA; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ALL; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ANNOUNCEMENTS; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ATTENDANCES; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_GRADES; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_HOMEWORKS; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_MESSAGES_INBOX; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_MESSAGES_OUTBOX; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_NOTICES; import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_TIMETABLE; import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_FINAL; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_PROPOSED; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER2_FINAL; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER2_PROPOSED; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_YEAR_FINAL; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_YEAR_PROPOSED; import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_IUCZNIOWIE; import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_LIBRUS; import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_MOBIDZIENNIK; import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_VULCAN; import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED; import static pl.szczodrzynski.edziennik.sync.SyncService.PROFILE_MAX_PROGRESS; import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.ns; public class Edziennik { //public static final int CODE_NULL = 0; private App app; private static final String TAG = "Edziennik"; private static boolean registerEmpty; public static int oldLuckyNumber; public static EdziennikInterface getApi(App app, int loginType) { switch (loginType) { default: case LOGIN_TYPE_MOBIDZIENNIK: return app.apiMobidziennik; case LOGIN_TYPE_LIBRUS: return app.apiLibrus; case LOGIN_TYPE_IUCZNIOWIE: return app.apiIuczniowie; case LOGIN_TYPE_VULCAN: return app.apiVulcan; } } public Edziennik(App app) { this.app = app; } @SuppressWarnings("deprecation") public static void clearCookies(Context context, String url) { //Log.d(TAG, "Cookies: " + yahooCookies); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { //Log.d(TAG, "Using clearCookies code for API >=" + String.valueOf(Build.VERSION_CODES.LOLLIPOP_MR1)); CookieManager.getInstance().removeAllCookies(null); CookieManager.getInstance().flush(); } else { //Log.d(TAG, "Using clearCookies code for API <" + String.valueOf(Build.VERSION_CODES.LOLLIPOP_MR1)); CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(context); cookieSyncManager.startSync(); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.removeAllCookie(); cookieManager.removeSessionCookie(); cookieSyncManager.stopSync(); cookieSyncManager.sync(); } } public void initMessagesWebView(WebView webView, App app, boolean fullVersion, boolean clearCookies) { if (!app.profile.getEmpty() && app.profile.getLoginStoreType() == LoginStore.LOGIN_TYPE_MOBIDZIENNIK) { String url; if (fullVersion) { url = "https://" + app.profile.getLoginData("serverName", "") + ".mobidziennik.pl/mobile/wiadomosci"; } else { url = "https://" + app.profile.getLoginData("serverName", "") + ".mobidziennik.pl/api/"; } String str1 = "login=" + app.profile.getLoginData("username", "") + "&haslo=" + app.profile.getLoginData("password", ""); if (!fullVersion) { str1 += "&ip=" + app.deviceId + "&wersja=20&token=&webview_wiadomosci=1"; } if (-1L != -1L) { str1 += "&id_wiadomosci=" + -1L; } if (clearCookies) clearCookies(app, "https://" + app.profile.getLoginData("serverName", "") + ".mobidziennik.pl"); //Toast.makeText(app, "URL "+url, Toast.LENGTH_SHORT).show(); webView.postUrl(url, str1.getBytes()); } else if (!app.profile.getEmpty() && app.profile.getLoginStoreType() == LoginStore.LOGIN_TYPE_IUCZNIOWIE) { String url = "https://iuczniowie.progman.pl/idziennik/mod_panelRodzica/Komunikator.aspx"; webView.loadUrl(url); /* if (app.profile.loginServerName.equals("") || app.profile.loginUsername.equals("") || app.profile.loginPassword.equals("")) { webView.loadData("

"+app.getString(R.string.api_error_code_invalid_login)+"

", "text/html", "UTF-8"); return; } if (app.appConfig.deviceId == null || app.appConfig.deviceId.equals("")) { app.appConfig.deviceId = Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID); app.appConfig.savePending = true; } //Ion.getDefault(app.getContext()).getCookieMiddleware().getCookieStore().removeAll(); // TODO remove only cookies for this domain String finalLoginServerName = app.profile.loginServerName; String finalLoginUsername = app.profile.loginUsername; String finalLoginPassword = app.profile.loginPassword; Ion.with(app.getContext()) .load("https://iuczniowie.progman.pl/idziennik/login.aspx") .setTimeout(REQUEST_TIMEOUT) .setHeader("User-Agent", Iuczniowie.userAgent) //.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") .asString() .setCallback((e, result) -> { if (e instanceof java.util.concurrent.TimeoutException) { webView.loadData("

"+app.getString(R.string.api_error_code_timeout)+"

", "text/html", "UTF-8"); return; } app.profile.loggedIn = (result != null && result.equals("ok")); if (result == null || result.equals("")) { // for safety webView.loadData("

"+app.getString(R.string.api_error_code_no_internet)+"

", "text/html", "UTF-8"); return; } if (clearCookies) clearCookies(app, "https://iuczniowie.progman.pl"); String post = ""; try { post += "ctl00$ContentPlaceHolder$nazwaPrzegladarki="+URLEncoder.encode(Iuczniowie.userAgent, "UTF-8"); post += "ctl00$ContentPlaceHolder$NazwaSzkoly="+finalLoginServerName; post += "ctl00$ContentPlaceHolder$UserName="+URLEncoder.encode(finalLoginUsername, "UTF-8"); post += "ctl00$ContentPlaceHolder$Password="+URLEncoder.encode(finalLoginPassword, "UTF-8"); post += "ctl00$ContentPlaceHolder$Logowanie=Zaloguj"; } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } webView.getSettings().setUserAgentString(Iuczniowie.userAgent); });*/ } else if (app.profile.getEmpty()) { webView.loadData("

" + app.getString(R.string.sync_error_invalid_login) + "

", "text/html", "UTF-8"); } else { webView.loadData("

" + app.getString(R.string.settings_register_login_not_implemented_text) + "

", "text/html", "UTF-8"); } } /* _____ _______ _ | __ \ /\ |__ __| | | | |__) | __ ___ ___ ___ ___ ___ / \ ___ _ _ _ __ ___| | __ _ ___| | __ | ___/ '__/ _ \ / __/ _ \/ __/ __| / /\ \ / __| | | | '_ \ / __| |/ _` / __| |/ / | | | | | (_) | (_| __/\__ \__ \/ ____ \\__ \ |_| | | | | (__| | (_| \__ \ < |_| |_| \___/ \___\___||___/___/_/ \_\___/\__, |_| |_|\___|_|\__,_|___/_|\_\ __/ | |__*/ /** * A task for creating notifications and downloading shared events. */ private static class ProcessAsyncTask extends AsyncTask { private App app; private WeakReference activityContext; private SyncCallback callback; private Exception e = null; private String apiResponse = null; private int profileId; private ProfileFull profile; public ProcessAsyncTask(App app, Context activityContext, SyncCallback callback, int profileId, ProfileFull profile) { //d(TAG, "Thread/ProcessAsyncTask/constructor/"+Thread.currentThread().getName()); this.app = app; this.activityContext = new WeakReference<>(activityContext); this.callback = callback; this.profileId = profileId; this.profile = profile; } @Override protected Integer doInBackground(Void... voids) { Context activityContext = this.activityContext.get(); //d(TAG, "Thread/ProcessAsyncTask/doInBackground/"+Thread.currentThread().getName()); try { // UPDATE FCM TOKEN IF EMPTY if (app.appConfig.fcmToken == null || app.appConfig.fcmToken.equals("")) { FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> { app.appConfig.fcmToken = instanceIdResult.getToken(); app.appConfig.savePending = true; }); } callback.onProgress(1); if (profile.getSyncNotifications()) { new Handler(activityContext.getMainLooper()).post(() -> { callback.onActionStarted(R.string.sync_action_creating_notifications); }); for (LessonFull change : app.db.lessonChangeDao().getNotNotifiedNow(profileId)) { String text = app.getContext().getString(R.string.notification_lesson_change_format, change.changeTypeStr(app.getContext()), change.lessonDate == null ? "" : change.lessonDate.getFormattedString(), change.subjectLongName); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_TIMETABLE_LESSON_CHANGE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_TIMETABLE) .withLongExtra("timetableDate", change.lessonDate.getValue()) .withAddedDate(change.addedDate) ); } for (EventFull event : app.db.eventDao().getNotNotifiedNow(profileId)) { String text; if (event.type == TYPE_HOMEWORK) text = app.getContext().getString(R.string.notification_homework_format, ns(app.getString(R.string.notification_event_no_subject), event.subjectLongName), event.eventDate.getFormattedString()); else text = app.getContext().getString(R.string.notification_event_format, event.typeName, event.eventDate.getFormattedString(), ns(app.getString(R.string.notification_event_no_subject), event.subjectLongName)); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(event.type == TYPE_HOMEWORK ? Notification.TYPE_NEW_HOMEWORK : Notification.TYPE_NEW_EVENT) .withFragmentRedirect(event.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORKS : MainActivity.DRAWER_ITEM_AGENDA) .withLongExtra("eventId", event.id) .withLongExtra("eventDate", event.eventDate.getValue()) .withAddedDate(event.addedDate) ); // student's rights abuse - disabled, because this was useless /*if (!event.addedManually && event.type == RegisterEvent.TYPE_EXAM && event.eventDate.combineWith(event.startTime) - event.addedDate < 7 * 24 * 60 * 60 * 1000) { text = app.getContext().getString(R.string.notification_abuse_format, event.typeString(app, app.profile), event.subjectLongName, event.eventDate.getFormattedString()); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.id, profile.name) .withType(Notification.TYPE_GENERAL) .withFragmentRedirect(MainActivity.DRAWER_ITEM_NOTIFICATIONS) ); }*/ } Date today = Date.getToday(); int todayValue = today.getValue(); profile.setCurrentSemester(profile.dateToSemester(today)); for (GradeFull grade : app.db.gradeDao().getNotNotifiedNow(profileId)) { String gradeName = grade.name; if (grade.type == TYPE_SEMESTER1_PROPOSED || grade.type == TYPE_SEMESTER2_PROPOSED) { gradeName = (app.getString(R.string.grade_semester_proposed_format_2, grade.name)); } else if (grade.type == TYPE_SEMESTER1_FINAL || grade.type == TYPE_SEMESTER2_FINAL) { gradeName = (app.getString(R.string.grade_semester_final_format_2, grade.name)); } else if (grade.type == TYPE_YEAR_PROPOSED) { gradeName = (app.getString(R.string.grade_year_proposed_format_2, grade.name)); } else if (grade.type == TYPE_YEAR_FINAL) { gradeName = (app.getString(R.string.grade_year_final_format_2, grade.name)); } String text = app.getContext().getString(R.string.notification_grade_format, gradeName, grade.subjectLongName); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_NEW_GRADE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_GRADES) .withLongExtra("gradesSubjectId", grade.subjectId) .withAddedDate(grade.addedDate) ); } for (NoticeFull notice : app.db.noticeDao().getNotNotifiedNow(profileId)) { String noticeTypeStr = (notice.type == Notice.TYPE_POSITIVE ? app.getString(R.string.notification_notice_praise) : (notice.type == Notice.TYPE_NEGATIVE ? app.getString(R.string.notification_notice_warning) : app.getString(R.string.notification_notice_new))); String text = app.getContext().getString(R.string.notification_notice_format, noticeTypeStr, notice.teacherFullName, Date.fromMillis(notice.addedDate).getFormattedString()); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_NEW_NOTICE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_NOTICES) .withLongExtra("noticeId", notice.id) .withAddedDate(notice.addedDate) ); } for (AttendanceFull attendance : app.db.attendanceDao().getNotNotifiedNow(profileId)) { String attendanceTypeStr = app.getString(R.string.notification_type_attendance); switch (attendance.type) { case Attendance.TYPE_ABSENT: attendanceTypeStr = app.getString(R.string.notification_absence); break; case Attendance.TYPE_ABSENT_EXCUSED: attendanceTypeStr = app.getString(R.string.notification_absence_excused); break; case Attendance.TYPE_BELATED: attendanceTypeStr = app.getString(R.string.notification_belated); break; case Attendance.TYPE_BELATED_EXCUSED: attendanceTypeStr = app.getString(R.string.notification_belated_excused); break; case Attendance.TYPE_RELEASED: attendanceTypeStr = app.getString(R.string.notification_release); break; } String text = app.getContext().getString(R.string.notification_attendance_format, attendanceTypeStr, attendance.subjectLongName, attendance.lessonDate.getFormattedString()); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_NEW_ATTENDANCE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_ATTENDANCES) .withLongExtra("attendanceId", attendance.id) .withAddedDate(attendance.addedDate) ); } for (AnnouncementFull announcement : app.db.announcementDao().getNotNotifiedNow(profileId)) { String text = app.getContext().getString(R.string.notification_announcement_format, announcement.subject); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_NEW_ANNOUNCEMENT) .withFragmentRedirect(MainActivity.DRAWER_ITEM_ANNOUNCEMENTS) .withLongExtra("announcementId", announcement.id) .withAddedDate(announcement.addedDate) ); } for (MessageFull message : app.db.messageDao().getReceivedNotNotifiedNow(profileId)) { String text = app.getContext().getString(R.string.notification_message_format, message.senderFullName, message.subject); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_NEW_MESSAGE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_MESSAGES) .withLongExtra("messageType", Message.TYPE_RECEIVED) .withLongExtra("messageId", message.id) .withAddedDate(message.addedDate) ); } if (profile.getLuckyNumber() != oldLuckyNumber && profile.getLuckyNumber() != -1 && profile.getLuckyNumberDate() != null && profile.getLuckyNumberDate().getValue() >= todayValue) { String text; if (profile.getLuckyNumberDate().getValue() == todayValue) { // LN for today text = app.getString((profile.getStudentNumber() != -1 && profile.getStudentNumber() == profile.getLuckyNumber() ? R.string.notification_lucky_number_yours_format : R.string.notification_lucky_number_format), profile.getLuckyNumber()); } else if (profile.getLuckyNumberDate().getValue() == todayValue + 1) { // LN for tomorrow text = app.getString((profile.getStudentNumber() != -1 && profile.getStudentNumber() == profile.getLuckyNumber() ? R.string.notification_lucky_number_yours_tomorrow_format : R.string.notification_lucky_number_tomorrow_format), profile.getLuckyNumber()); } else { // LN for later text = app.getString((profile.getStudentNumber() != -1 && profile.getStudentNumber() == profile.getLuckyNumber() ? R.string.notification_lucky_number_yours_later_format : R.string.notification_lucky_number_later_format), profile.getLuckyNumberDate().getFormattedString(), profile.getLuckyNumber()); } app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_LUCKY_NUMBER) .withFragmentRedirect(MainActivity.DRAWER_ITEM_HOME) ); oldLuckyNumber = profile.getLuckyNumber(); } } app.db.metadataDao().setAllNotified(profileId, true); callback.onProgress(1); // SEND WEB PUSH, if registration allowed // otherwise, UNREGISTER THE USER if (profile.getRegistration() == REGISTRATION_ENABLED) { new Handler(activityContext.getMainLooper()).post(() -> { callback.onActionStarted(R.string.sync_action_syncing_shared_events); }); //if (profile.registrationUsername == null || profile.registrationUsername.equals("")) { //} ServerRequest syncRequest = new ServerRequest(app, app.requestScheme + APP_URL + "main.php?sync", "Edziennik/REG", profile); if (registerEmpty) { syncRequest.setBodyParameter("first_run", "true"); } // ALSO SEND NEW DATA TO BROWSER *excluding* all Shared Events !!! // because they will be sent by the server, as soon as it's shared, by FCM if (app.appConfig.webPushEnabled) { int position = 0; for (Notification notification : app.appConfig.notifications) { //Log.d(TAG, notification.text); if (!notification.notified) { if (notification.type != Notification.TYPE_NEW_SHARED_EVENT && notification.type != Notification.TYPE_SERVER_MESSAGE && notification.type != Notification.TYPE_NEW_SHARED_HOMEWORK) // these are automatically sent to the browser by the server { //Log.d(TAG, "Adding notify[" + position + "]"); syncRequest.setBodyParameter("notify[" + position + "][type]", Integer.toString(notification.type)); syncRequest.setBodyParameter("notify[" + position + "][title]", notification.title); syncRequest.setBodyParameter("notify[" + position + "][text]", notification.text); position++; } } } } callback.onProgress(1); if (app.appConfig.webPushEnabled || profile.getEnableSharedEvents()) { JsonObject result = syncRequest.runSync(); callback.onProgress(1); //Log.d(TAG, "Executed request"); if (result == null) { return AppError.CODE_APP_SERVER_ERROR; } apiResponse = result.toString(); if (!result.get("success").getAsString().equals("true")) { return AppError.CODE_APP_SERVER_ERROR; } // HERE PROCESS ALL THE RECEIVED EVENTS // add them to the profile and create appropriate notifications for (JsonElement jEventEl : result.getAsJsonArray("events")) { JsonObject jEvent = jEventEl.getAsJsonObject(); String teamCode = jEvent.get("team").getAsString(); //d(TAG, "An event is there! "+jEvent.toString()); // get the target Team from teamCode Team team = app.db.teamDao().getByCodeNow(profile.getId(), teamCode); if (team != null) { //d(TAG, "The target team is "+team.name+", ID "+team.id); // create the event from Json. Add the missing teamId and !!profileId!! Event event = app.gson.fromJson(jEvent.toString(), Event.class); // proguard. disable for Event.class if (event.eventDate == null) { apiResponse += "\n\nEventDate == null\n" + jEvent.toString(); throw new Exception("null eventDate"); } event.profileId = profile.getId(); event.teamId = team.id; event.addedManually = true; //d(TAG, "Created the event! "+event); if (event.sharedBy != null && event.sharedBy.equals(profile.getUsernameId())) { //d(TAG, "Shared by self! Changing name"); event.sharedBy = "self"; event.sharedByName = profile.getStudentNameLong(); } EventType type = app.db.eventTypeDao().getByIdNow(profileId, event.type); //d(TAG, "Finishing adding event "+event); app.db.eventDao().add(event); Metadata metadata = new Metadata(profile.getId(), event.type == TYPE_HOMEWORK ? Metadata.TYPE_HOMEWORK : Metadata.TYPE_EVENT, event.id, registerEmpty, true, jEvent.get("addedDate").getAsLong()); long metadataId = app.db.metadataDao().add(metadata); if (metadataId != -1 && !registerEmpty) { app.notifier.add(new Notification(app.getContext(), app.getString(R.string.notification_shared_event_format, event.sharedByName, type != null ? type.name : "wydarzenie", event.eventDate == null ? "nieznana data" : event.eventDate.getFormattedString(), event.topic)) .withProfileData(profile.getId(), profile.getName()) .withType(event.type == TYPE_HOMEWORK ? Notification.TYPE_NEW_SHARED_HOMEWORK : Notification.TYPE_NEW_SHARED_EVENT) .withFragmentRedirect(event.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORKS : MainActivity.DRAWER_ITEM_AGENDA) .withLongExtra("eventDate", event.eventDate.getValue()) ); } } } callback.onProgress(5); return CODE_OK; } else { callback.onProgress(6); return CODE_OK; } } else { // the user does not want to be registered callback.onProgress(7); return CODE_OK; } } catch (Exception e) { e.printStackTrace(); this.e = e; return null; } //return null; } @Override protected void onPostExecute(Integer errorCode) { //d(TAG, "Thread/ProcessAsyncTask/onPostExecute/"+Thread.currentThread().getName()); Context activityContext = this.activityContext.get(); app.profileSaveFull(profile); if (app.profile != null && profile.getId() == app.profile.getId()) { app.profile = profile; } if (errorCode == null) { // this means an Exception was thrown callback.onError(activityContext, new AppError(TAG, 513, CODE_OTHER, e, apiResponse)); return; } //Log.d(TAG, "Finishing"); callback.onProgress(1); if (errorCode == CODE_OK) callback.onSuccess(activityContext, profile); else { try { // oh that's useless throw new RuntimeException(stringErrorCode(app, errorCode, "")); } catch (Exception e) { callback.onError(activityContext, new AppError(TAG, 528, errorCode, e, (String) null)); } } super.onPostExecute(errorCode); } } public void notifyAndReload() { app.notifier.postAll(null); app.saveConfig(); SyncJob.schedule(app); Intent i = new Intent(Intent.ACTION_MAIN) .putExtra("reloadProfileId", -1); app.sendBroadcast(i); Intent intent = new Intent(app.getContext(), WidgetTimetable.class); intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int[] ids = AppWidgetManager.getInstance(app).getAppWidgetIds(new ComponentName(app, WidgetTimetable.class)); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); app.sendBroadcast(intent); intent = new Intent(app.getContext(), WidgetNotifications.class); intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); ids = AppWidgetManager.getInstance(app).getAppWidgetIds(new ComponentName(app, WidgetNotifications.class)); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); app.sendBroadcast(intent); intent = new Intent(app.getContext(), WidgetLuckyNumber.class); intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); ids = AppWidgetManager.getInstance(app).getAppWidgetIds(new ComponentName(app, WidgetLuckyNumber.class)); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); app.sendBroadcast(intent); } /* _____ / ____| | (___ _ _ _ __ ___ \___ \| | | | '_ \ / __| ____) | |_| | | | | (__ |_____/ \__, |_| |_|\___| __/ | |__*/ // DataCallbacks that are *not* in Edziennik.sync need to be executed on the main thread. // EdziennikInterface.sync is executed on a worker thread // in Edziennik.sync/newCallback methods are called on a worker thread // callback passed to Edziennik.sync is executed on the main thread // thus, callback which is in guiSync is also on the main thread /** * Sync all Edziennik data. * Used in services, login form and {@code guiSync} *

* May be ran on worker thread. * {@link EdziennikInterface}.sync is ran always on worker thread. * Every callback is ran on the UI thread. * * @param app * @param activityContext * @param callback * @param profileId */ public void sync(@NonNull App app, @NonNull Context activityContext, @NonNull SyncCallback callback, int profileId) { sync(app, activityContext, callback, profileId, (int[])null); } public void sync(@NonNull App app, @NonNull Context activityContext, @NonNull SyncCallback callback, int profileId, @Nullable int ... featureList) { // empty: no unread notifications, all shared events (current+past) // only if there is no data, and we are not logged in yet SyncCallback newCallback = new SyncCallback() { @Override public void onLoginFirst(List profileList, LoginStore loginStore) { new Handler(activityContext.getMainLooper()).post(() -> { callback.onLoginFirst(profileList, loginStore); }); } @Override public void onSuccess(Context activityContext, ProfileFull profileFull) { new Handler(activityContext.getMainLooper()).post(() -> { new ProcessAsyncTask(app, activityContext, callback, profileId, profileFull).execute(); }); } @Override public void onError(Context activityContext, AppError error) { new Handler(activityContext.getMainLooper()).post(() -> { callback.onError(activityContext, error); }); } @Override public void onProgress(int progressStep) { new Handler(activityContext.getMainLooper()).post(() -> { callback.onProgress(progressStep); }); } @Override public void onActionStarted(int stringResId) { new Handler(activityContext.getMainLooper()).post(() -> { callback.onActionStarted(stringResId); }); } }; AsyncTask.execute(() -> { ProfileFull profile = app.db.profileDao().getByIdNow(profileId); if (profile != null) { if (profile.getArchived()) { newCallback.onError(activityContext, new AppError(TAG, 678, CODE_PROFILE_ARCHIVED, profile.getName())); return; } else if (profile.getDateYearEnd() != null && Date.getToday().getValue() >= profile.getDateYearEnd().getValue()) { profile.setArchived(true); app.notifier.add(new Notification(app.getContext(), app.getString(R.string.profile_auto_archiving_format, profile.getName(), profile.getDateYearEnd().getFormattedString())) .withProfileData(profile.getId(), profile.getName()) .withType(Notification.TYPE_AUTO_ARCHIVING) .withFragmentRedirect(DRAWER_ITEM_HOME) .withLongExtra("autoArchiving", 1L) ); app.notifier.postAll(null); app.db.profileDao().add(profile); if (App.profileId == profile.getId()) { app.profile.setArchived(true); } newCallback.onSuccess(activityContext, profile); return; } registerEmpty = profile.getEmpty(); oldLuckyNumber = profile.getLuckyNumber(); getApi(app, profile.getLoginStoreType()).syncFeature(activityContext, newCallback, profile, featureList); } else { new Handler(activityContext.getMainLooper()).post(() -> callback.onError(activityContext, new AppError(TAG, 609, CODE_PROFILE_NOT_FOUND, (String) null))); } }); } /* _____ _ _ _____ / ____| | | |_ _| | | __| | | | | | __ ___ __ __ _ _ __ _ __ ___ _ __ ___ | | |_ | | | | | | \ \ /\ / / '__/ _` | '_ \| '_ \ / _ \ '__/ __| | |__| | |__| |_| |_ \ V V /| | | (_| | |_) | |_) | __/ | \__ \ \_____|\____/|_____| \_/\_/ |_| \__,_| .__/| .__/ \___|_| |___/ | | | | |_| |*/ /** * Sync all Edziennik data while showing a progress dialog. * A wrapper for {@code sync} * * Does not switch between threads. * All callbacks have to be executed on the UI thread. * * @param app an App singleton instance * @param activity a parent activity * @param profileId ID of the profile to sync * @param dialogTitle a title of the dialog to show * @param dialogText dialog's content * @param successText a toast to show on success */ public void guiSync(@NonNull App app, @NonNull Activity activity, int profileId, @StringRes int dialogTitle, @StringRes int dialogText, @StringRes int successText) { guiSync(app, activity, profileId, dialogTitle, dialogText, successText, (int[])null); } public void guiSync(@NonNull App app, @NonNull Activity activity, int profileId, @StringRes int dialogTitle, @StringRes int dialogText, @StringRes int successText, int ... featureList) { MaterialDialog progressDialog = new MaterialDialog.Builder(activity) .title(dialogTitle) .content(dialogText) .progress(false, PROFILE_MAX_PROGRESS, false) .canceledOnTouchOutside(false) .show(); SyncCallback guiSyncCallback = new SyncCallback() { @Override public void onLoginFirst(List profileList, LoginStore loginStore) { } @Override public void onSuccess(Context activityContext, ProfileFull profileFull) { progressDialog.dismiss(); Toast.makeText(activityContext, successText, Toast.LENGTH_SHORT).show(); notifyAndReload(); // profiles are saved automatically, during app.saveConfig in processFinish /*if (activityContext instanceof MainActivity) { //((MainActivity) activityContext).reloadCurrentFragment("GuiSync"); ((MainActivity) activityContext).accountHeaderAddProfiles(); }*/ } @Override public void onError(Context activityContext, AppError error) { progressDialog.dismiss(); guiShowErrorDialog((Activity) activityContext, error, R.string.sync_error_dialog_title); } @Override public void onProgress(int progressStep) { progressDialog.incrementProgress(progressStep); } @Override public void onActionStarted(int stringResId) { progressDialog.setContent(activity.getString(R.string.sync_action_format, activity.getString(stringResId))); } }; app.apiEdziennik.sync(app, activity, guiSyncCallback, profileId, featureList); } /** * Sync all Edziennik data in background. * A callback is executed on main thread. * A wrapper for {@code sync} * * @param app an App singleton instance * @param activity a parent activity * @param profileId ID of the profile to sync * @param syncCallback a callback * @param feature a feature to sync */ public void guiSyncSilent(@NonNull App app, @NonNull Activity activity, int profileId, SyncCallback syncCallback, int feature) { SyncCallback guiSyncCallback = new SyncCallback() { @Override public void onLoginFirst(List profileList, LoginStore loginStore) { } @Override public void onSuccess(Context activityContext, ProfileFull profileFull) { notifyAndReload(); syncCallback.onSuccess(activityContext, profileFull); } @Override public void onError(Context activityContext, AppError error) { syncCallback.onError(activityContext, error); } @Override public void onProgress(int progressStep) { syncCallback.onProgress(progressStep); } @Override public void onActionStarted(int stringResId) { syncCallback.onActionStarted(stringResId); } }; app.apiEdziennik.sync(app, activity, guiSyncCallback, profileId, feature == FEATURE_ALL ? null : new int[]{feature}); } /** * Show a dialog allowing the user to choose which features to sync. * Handles everything including pre-selecting the features basing on the current fragment. * * Will execute {@code sync} after the selection is made. * * A normal progress dialog is shown during the sync. * * @param app an App singleton instance * @param activity a parent activity * @param profileId ID of the profile to sync * @param dialogTitle a title of the dialog to show * @param dialogText dialog's content * @param successText a toast to show on success * @param currentFeature a feature id representing the currently opened fragment or caller */ public void guiSyncFeature(@NonNull App app, @NonNull Activity activity, int profileId, @StringRes int dialogTitle, @StringRes int dialogText, @StringRes int successText, int currentFeature) { String[] items = new String[]{ app.getString(R.string.menu_timetable), app.getString(R.string.menu_agenda), app.getString(R.string.menu_grades), app.getString(R.string.menu_homework), app.getString(R.string.menu_notices), app.getString(R.string.menu_attendances), app.getString(R.string.title_messages_inbox_single), app.getString(R.string.title_messages_sent_single), app.getString(R.string.menu_announcements) }; int[] itemsIds = new int[]{ FEATURE_TIMETABLE, FEATURE_AGENDA, FEATURE_GRADES, FEATURE_HOMEWORKS, FEATURE_NOTICES, FEATURE_ATTENDANCES, FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX, FEATURE_ANNOUNCEMENTS }; int[] selectedIndices; if (currentFeature == FEATURE_ALL) { selectedIndices = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8}; } else { selectedIndices = new int[]{Arrays.binarySearch(itemsIds, currentFeature)}; } MaterialDialog dialog = new MaterialDialog.Builder(activity) .title(R.string.sync_feature_title) .content(R.string.sync_feature_text) .positiveText(R.string.ok) .negativeText(R.string.cancel) .neutralText(R.string.sync_feature_all) .items(items) .itemsIds(itemsIds) .itemsCallbackMultiChoice(ArrayUtils.toWrapperArray(selectedIndices), (dialog1, which, text) -> { dialog1.getActionButton(DialogAction.POSITIVE).setEnabled(which.length > 0); return true; }) .alwaysCallMultiChoiceCallback() .onPositive(((dialog1, which) -> { List featureList = new ArrayList<>(); for (int i: dialog1.getSelectedIndices()) { featureList.add(itemsIds[i]); } guiSync(app, activity, profileId, dialogTitle, dialogText, successText, ArrayUtils.toPrimitiveArray(featureList)); })) .onNeutral(((dialog1, which) -> { guiSync(app, activity, profileId, dialogTitle, dialogText, successText); })) .show(); } public void guiShowArchivedDialog(Activity activity, String profileName) { new MaterialDialog.Builder(activity) .title(R.string.profile_archived_dialog_title) .content(activity.getString(R.string.profile_archived_dialog_text_format, profileName)) .positiveText(R.string.ok) .onPositive(((dialog, which) -> dialog.dismiss())) .autoDismiss(false) .show(); } /* _____ _ _ _____ / ____| | | |_ _| | | __| | | | | | ___ _ __ _ __ ___ _ __ ___ | | |_ | | | | | | / _ \ '__| '__/ _ \| '__/ __| | |__| | |__| |_| |_ | __/ | | | | (_) | | \__ \ \_____|\____/|_____| \___|_| |_| \___/|_| |__*/ /** * Used for reporting an exception somewhere in the code that is not part of Edziennik APIs. * * @param activity a parent activity * @param errorLine the line of code where the error occurred * @param e an Exception object */ public void guiReportException(Activity activity, int errorLine, Exception e) { guiReportError(activity, new AppError(TAG, errorLine, CODE_OTHER, "Błąd wewnętrzny aplikacji ("+errorLine+")", null, null, e, null), null); } public void guiShowErrorDialog(Activity activity, @NonNull AppError error, @StringRes int dialogTitle) { if (error.errorCode == CODE_PROFILE_ARCHIVED) { guiShowArchivedDialog(activity, error.errorText); return; } error.changeIfCodeOther(); new MaterialDialog.Builder(activity) .title(dialogTitle) .content(error.asReadableString(activity)) .positiveText(R.string.ok) .onPositive(((dialog, which) -> dialog.dismiss())) .neutralText(R.string.sync_error_dialog_report_button) .onNeutral(((dialog, which) -> { guiReportError(activity, error, dialog); })) .autoDismiss(false) .show(); } public void guiShowErrorSnackbar(MainActivity activity, @NonNull AppError error) { if (error.errorCode == CODE_PROFILE_ARCHIVED) { guiShowArchivedDialog(activity, error.errorText); return; } // TODO: 2019-08-28 IconicsDrawable icon = new IconicsDrawable(activity) .icon(CommunityMaterial.Icon.cmd_alert_circle); sizeDp(icon, 20); colorInt(icon, Themes.INSTANCE.getPrimaryTextColor(activity)); error.changeIfCodeOther(); CafeBar.builder(activity) .to(activity.findViewById(R.id.coordinator)) .content(error.asReadableString(activity)) .icon(icon) .positiveText(R.string.more) .positiveColor(0xff4caf50) .negativeText(R.string.ok) .negativeColor(0x66ffffff) .onPositive((cafeBar -> guiReportError(activity, error, null))) .onNegative((cafeBar -> cafeBar.dismiss())) .autoDismiss(false) .swipeToDismiss(true) .floating(true) .show(); } public void guiReportError(Activity activity, AppError error, @Nullable MaterialDialog parentDialogToDisableNeutral) { String errorDetails = error.getDetails(activity); String htmlErrorDetails = ""+errorDetails+""; htmlErrorDetails = htmlErrorDetails.replaceAll(activity.getPackageName(), ""+activity.getPackageName()+""); htmlErrorDetails = htmlErrorDetails.replaceAll("\n", "
"); new MaterialDialog.Builder(activity) .title(R.string.sync_report_dialog_title) .content(Html.fromHtml(htmlErrorDetails)) .typeface(null, "RobotoMono-Regular.ttf") .negativeText(R.string.close) .onNegative(((dialog1, which1) -> dialog1.dismiss())) .neutralText(R.string.copy_to_clipboard) .onNeutral((dialog1, which1) -> { ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(CLIPBOARD_SERVICE); if (clipboard != null) { ClipData clip = ClipData.newPlainText("Error report", errorDetails); clipboard.setPrimaryClip(clip); Toast.makeText(activity, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); } }) .autoDismiss(false) .positiveText(R.string.sync_report_dialog_button) .checkBoxPromptRes(R.string.sync_report_dialog_include_api_response, true, null) .onPositive(((dialog1, which1) -> AsyncTask.execute(() -> error.getApiResponse(activity, apiResponse -> { new ServerRequest(app, app.requestScheme + APP_URL + "main.php?report", "Edziennik/Report") .setBodyParameter("base64_encoded", Base64.encodeToString(errorDetails.getBytes(), Base64.DEFAULT)) .setBodyParameter("api_response", dialog1.isPromptCheckBoxChecked() ? Base64.encodeToString(apiResponse.getBytes(), Base64.DEFAULT) : "VW5jaGVja2Vk"/*Unchecked*/) .run((e, result) -> { new Handler(activity.getMainLooper()).post(() -> { if (result != null) { if (result.get("success").getAsBoolean()) { Toast.makeText(activity, activity.getString(R.string.crash_report_sent), Toast.LENGTH_SHORT).show(); dialog1.getActionButton(DialogAction.POSITIVE).setEnabled(false); if (parentDialogToDisableNeutral != null) parentDialogToDisableNeutral.getActionButton(DialogAction.NEUTRAL).setEnabled(false); } else { Toast.makeText(activity, activity.getString(R.string.crash_report_cannot_send) + ": " + result.get("reason").getAsString(), Toast.LENGTH_LONG).show(); } } else { Toast.makeText(activity, activity.getString(R.string.crash_report_cannot_send)+" brak internetu", Toast.LENGTH_LONG).show(); } }); }); })))) .show(); } /** * A method that displays a dialog allowing the user to report an error that has occurred. * * @param activity a parent activity * @param errorCode self-explanatory * @param errorText additional error information, that replaces text based on {@code errorCode} if it's {@code CODE_OTHER} * @param throwable a {@link Throwable} containing the error details * @param apiResponse response of the Edziennik API * @param parentDialogToDisableNeutral if not null, an instance of {@link MaterialDialog} in which the neutral button should be disabled after submitting an error report */ public void guiReportError(Activity activity, int errorCode, String errorText, Throwable throwable, String apiResponse, @Nullable MaterialDialog parentDialogToDisableNeutral) { // build a string containing the stack trace and the device name + user's registration data String contentPlain = "Application Internal Error "+stringErrorType(errorCode)+":\n"+stringErrorCode(activity, errorCode, "")+"\n"+errorText+"\n\n"; contentPlain += Log.getStackTraceString(throwable); String content = ""+contentPlain+""; content = content.replaceAll(activity.getPackageName(), ""+activity.getPackageName()+""); content = content.replaceAll("\n", "
"); contentPlain += "\n"+Build.MANUFACTURER+"\n"+Build.BRAND+"\n"+Build.MODEL+"\n"+Build.DEVICE+"\n"; if (app.profile != null && app.profile.getRegistration() == REGISTRATION_ENABLED) { contentPlain += "U: "+app.profile.getUsernameId()+"\nS: "+ app.profile.getStudentNameLong() +"\nT: "+app.profile.loginStoreType()+"\n"; } contentPlain += BuildConfig.VERSION_NAME+" "+BuildConfig.BUILD_TYPE+"\nAndroid "+Build.VERSION.RELEASE; d(TAG, contentPlain); d(TAG, apiResponse == null ? "API Response = null" : apiResponse); // show a dialog containing the error details in HTML String finalContentPlain = contentPlain; new MaterialDialog.Builder(activity) .title(R.string.sync_report_dialog_title) .content(Html.fromHtml(content)) .typeface(null, "RobotoMono-Regular.ttf") .negativeText(R.string.close) .onNegative(((dialog1, which1) -> dialog1.dismiss())) .neutralText(R.string.copy_to_clipboard) .onNeutral((dialog1, which1) -> { ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(CLIPBOARD_SERVICE); if (clipboard != null) { ClipData clip = ClipData.newPlainText("Error report", finalContentPlain); clipboard.setPrimaryClip(clip); Toast.makeText(activity, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); } }) .autoDismiss(false) .positiveText(R.string.sync_report_dialog_button) .checkBoxPromptRes(R.string.sync_report_dialog_include_api_response, true, null) .onPositive(((dialog1, which1) -> { // send the error report new ServerRequest(app, app.requestScheme + APP_URL + "main.php?report", "Edziennik/Report") .setBodyParameter("base64_encoded", Base64.encodeToString(finalContentPlain.getBytes(), Base64.DEFAULT)) .setBodyParameter("api_response", dialog1.isPromptCheckBoxChecked() ? apiResponse == null ? Base64.encodeToString("NULL XD".getBytes(), Base64.DEFAULT) : Base64.encodeToString(apiResponse.getBytes(), Base64.DEFAULT) : "VW5jaGVja2Vk"/*Unchecked*/) .run((e, result) -> { new Handler(Looper.getMainLooper()).post(() -> { if (result != null) { if (result.get("success").getAsBoolean()) { Toast.makeText(activity, activity.getString(R.string.crash_report_sent), Toast.LENGTH_SHORT).show(); dialog1.getActionButton(DialogAction.POSITIVE).setEnabled(false); if (parentDialogToDisableNeutral != null) parentDialogToDisableNeutral.getActionButton(DialogAction.NEUTRAL).setEnabled(false); } else { Toast.makeText(activity, activity.getString(R.string.crash_report_cannot_send) + ": " + result.get("reason").getAsString(), Toast.LENGTH_LONG).show(); } } else { Toast.makeText(activity, activity.getString(R.string.crash_report_cannot_send)+" JsonObject equals null", Toast.LENGTH_LONG).show(); } }); }); })) .show(); } /* _____ __ _ _ _ | __ \ / _(_) | | | | |__) | __ ___ | |_ _| | ___ _ __ ___ _ __ ___ _____ ____ _| | | ___/ '__/ _ \| _| | |/ _ \ | '__/ _ \ '_ ` _ \ / _ \ \ / / _` | | | | | | | (_) | | | | | __/ | | | __/ | | | | | (_) \ V / (_| | | |_| |_| \___/|_| |_|_|\___| |_| \___|_| |_| |_|\___/ \_/ \__,_|*/ public void guiRemoveProfile(MainActivity activity, int profileId, String profileName) { new MaterialDialog.Builder(activity) .title(R.string.profile_menu_remove_confirm) .content(activity.getString(R.string.profile_menu_remove_confirm_text_format, profileName, profileName)) .positiveText(R.string.remove) .negativeText(R.string.cancel) .onPositive(((dialog, which) -> { AsyncTask.execute(() -> { removeProfile(profileId); activity.runOnUiThread(() -> { //activity.drawer.loadItem(DRAWER_ITEM_HOME, null, "ProfileRemoving"); //activity.recreate(DRAWER_ITEM_HOME); activity.reloadTarget(); Toast.makeText(activity, "Profil został usunięty.", Toast.LENGTH_LONG).show(); }); }); })) .show(); } public void removeProfile(int profileId) { Profile profileObject = app.db.profileDao().getByIdNow(profileId); if (profileObject == null) return; app.db.announcementDao().clear(profileId); app.db.attendanceDao().clear(profileId); app.db.eventDao().clear(profileId); app.db.eventTypeDao().clear(profileId); app.db.gradeDao().clear(profileId); app.db.gradeCategoryDao().clear(profileId); app.db.lessonDao().clear(profileId); app.db.lessonChangeDao().clear(profileId); app.db.luckyNumberDao().clear(profileId); app.db.noticeDao().clear(profileId); app.db.subjectDao().clear(profileId); app.db.teacherDao().clear(profileId); app.db.teamDao().clear(profileId); app.db.messageRecipientDao().clear(profileId); app.db.messageDao().clear(profileId); int loginStoreId = profileObject.getLoginStoreId(); List profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId); if (profilesUsingLoginStore.size() == 1) { app.db.loginStoreDao().remove(loginStoreId); } app.db.profileDao().remove(profileId); app.db.metadataDao().deleteAll(profileId); List toRemove = new ArrayList<>(); for (Notification notification: app.appConfig.notifications) { if (notification.profileId == profileId) { toRemove.add(notification); } } app.appConfig.notifications.removeAll(toRemove); app.profile = null; App.profileId = -1; } }