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.utils.models.Date; import pl.szczodrzynski.edziennik.utils.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_HOMEWORK; 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("
* 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
");
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