diff --git a/app/build.gradle b/app/build.gradle index c9602d71..1e02f040 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -140,22 +140,20 @@ dependencies { implementation "eu.szkolny:nachos:0e5dfcaceb" implementation "eu.szkolny.selective-dao:annotation:27f8f3f194" implementation "eu.szkolny:ssl-provider:1.0.0" - implementation "pl.szczodrzynski:navlib:0.7.2" + implementation "pl.szczodrzynski:navlib:0.8.0" implementation "pl.szczodrzynski:numberslidingpicker:2921225f76" implementation "pl.szczodrzynski:recyclertablayout:700f980584" implementation "pl.szczodrzynski:tachyon:551943a6b5" kapt "eu.szkolny.selective-dao:codegen:27f8f3f194" // Iconics & related - implementation "com.mikepenz:iconics-core:5.2.8" - implementation "com.mikepenz:iconics-views:5.2.8" + implementation "com.mikepenz:iconics-core:5.3.0-b01" + implementation "com.mikepenz:iconics-views:5.3.0-b01" implementation "com.mikepenz:community-material-typeface:5.8.55.0-kotlin@aar" implementation "eu.szkolny:szkolny-font:1.3" // Other dependencies implementation "cat.ereza:customactivityoncrash:2.3.0" - implementation "com.afollestad.material-dialogs:commons:0.9.6.0" - implementation "com.afollestad.material-dialogs:core:0.9.6.0" implementation "com.applandeo:material-calendar-view:1.5.0" implementation "com.daimajia.swipelayout:library:1.2.0@aar" implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2" @@ -169,7 +167,6 @@ dependencies { implementation "com.jaredrummler:colorpicker:1.1.0" implementation "com.qifan.powerpermission:powerpermission-coroutines:1.3.0" implementation "com.qifan.powerpermission:powerpermission:1.3.0" - implementation "com.wdullaer:materialdatetimepicker:4.2.3" implementation "com.yuyh.json:jsonviewer:1.0.6" implementation "io.coil-kt:coil:1.1.1" implementation "me.dm7.barcodescanner:zxing:1.9.8" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bf1d5732..5f295533 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -123,9 +123,6 @@ android:configChanges="orientation|screenSize|keyboardHidden" android:process=":error_activity" android:theme="@style/DeadTheme" /> - - diff --git a/app/src/main/assets/certificate.cer b/app/src/main/assets/certificate.cer deleted file mode 100644 index 0002462c..00000000 --- a/app/src/main/assets/certificate.cer +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt index e8aa4cd2..a9186d94 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt @@ -38,7 +38,6 @@ import pl.szczodrzynski.edziennik.data.api.events.ProfileListEmptyEvent import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.entity.Profile -import pl.szczodrzynski.edziennik.network.NetworkUtils import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar import pl.szczodrzynski.edziennik.sync.SyncWorker import pl.szczodrzynski.edziennik.sync.UpdateWorker @@ -89,7 +88,6 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope { .build() val permissionChecker by lazy { PermissionChecker(this) } - val networkUtils by lazy { NetworkUtils(this) } val gson by lazy { Gson() } /* _ _ _______ _______ _____ @@ -166,6 +164,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope { .errorActivity(CrashActivity::class.java) .apply() Iconics.init(applicationContext) + Iconics.respectFontBoundsDefault = true // initialize companion object values App.db = AppDb(this) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 350bf67c..a07634a1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -37,6 +37,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.viewpager.widget.ViewPager import com.google.android.material.button.MaterialButton +import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.gson.* import com.google.gson.JsonArray @@ -1279,3 +1280,10 @@ fun Context.getSyncInterval(interval: Int): String { "" return hoursText?.plus(" $minutesText") ?: minutesText } + +fun Profile.getSchoolYearConstrains(): CalendarConstraints { + return CalendarConstraints.Builder() + .setStart(dateSemester1Start.inMillisUtc) + .setEnd(dateYearEnd.inMillisUtc) + .build() +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/GenericFileProvider.java b/app/src/main/java/pl/szczodrzynski/edziennik/GenericFileProvider.java deleted file mode 100644 index 69aef167..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/GenericFileProvider.java +++ /dev/null @@ -1,5 +0,0 @@ -package pl.szczodrzynski.edziennik; - -import androidx.core.content.FileProvider; - -public class GenericFileProvider extends FileProvider {} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index b7b2d6f2..8dd83bf2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -68,7 +68,6 @@ import pl.szczodrzynski.edziennik.ui.modules.debug.LabFragment import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment -import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment @@ -128,7 +127,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope { const val DRAWER_ITEM_DEBUG = 102 const val TARGET_GRADES_EDITOR = 501 - const val TARGET_HELP = 502 const val TARGET_FEEDBACK = 120 const val TARGET_MESSAGES_DETAILS = 503 const val TARGET_MESSAGES_COMPOSE = 504 @@ -226,7 +224,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope { // other target items, not directly navigated list += NavTarget(TARGET_GRADES_EDITOR, R.string.menu_grades_editor, GradesEditorFragment::class) - list += NavTarget(TARGET_HELP, R.string.menu_help, HelpFragment::class) list += NavTarget(TARGET_FEEDBACK, R.string.menu_feedback, FeedbackFragment::class) list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES) list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class) @@ -497,7 +494,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope { .content(R.string.rate_snackbar_text) .icon(IconicsDrawable(this).apply { icon = CommunityMaterial.Icon3.cmd_star_outline - sizeDp = 20 + sizeDp = 24 colorInt = Themes.getPrimaryTextColor(this@MainActivity) }) .positiveText(R.string.rate_snackbar_positive) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/FirebaseBroadcastReceiver.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/FirebaseBroadcastReceiver.kt deleted file mode 100644 index 25c2de48..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/FirebaseBroadcastReceiver.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-11. - */ - -package pl.szczodrzynski.edziennik.data.firebase - -import android.content.Context -import android.content.Intent -import android.util.Log -import androidx.legacy.content.WakefulBroadcastReceiver -import com.google.gson.JsonObject - -class FirebaseBroadcastReceiver : WakefulBroadcastReceiver() { - companion object { - private const val TAG = "FirebaseBroadcast" - } - - override fun onReceive(context: Context, intent: Intent) { - val extras = intent.extras - val json = JsonObject() - extras?.keySet()?.forEach { key -> - extras.get(key)?.let { - when (it) { - is String -> json.addProperty(key, it) - is Int -> json.addProperty(key, it) - is Long -> json.addProperty(key, it) - is Float -> json.addProperty(key, it) - is Boolean -> json.addProperty(key, it) - else -> json.addProperty(key, it.toString()) - } - } - } - Log.d(TAG, "Intent(action=${intent?.action}, extras=$json)") - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/MyFirebaseMessagingService.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/MyFirebaseMessagingService.java deleted file mode 100644 index 34e341ed..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/MyFirebaseMessagingService.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-11. - */ - -package pl.szczodrzynski.edziennik.data.firebase; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.AsyncTask; - -import com.google.firebase.messaging.FirebaseMessagingService; -import com.google.firebase.messaging.RemoteMessage; - -import java.util.List; - -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask; -import pl.szczodrzynski.edziennik.data.db.entity.LoginStore; -import pl.szczodrzynski.edziennik.data.db.entity.Profile; - -import static pl.szczodrzynski.edziennik.utils.Utils.d; -import static pl.szczodrzynski.edziennik.utils.Utils.strToInt; - -public class MyFirebaseMessagingService extends FirebaseMessagingService { - private static final String TAG = "FirebaseMessaging"; - - @Override - public void onNewToken(String s) { - super.onNewToken(s); - - /* Log.d(TAG, "New token: "+s); - App app = (App)getApplicationContext(); - if (app.config.getSync().getTokenApp() == null || !app.config.getSync().getTokenApp().equals(s)) { - app.config.getSync().setTokenApp(s); - }*/ - } - - @Override - public void onMessageReceived(RemoteMessage remoteMessage) { - /*App app = ((App) getApplicationContext()); - // Not getting messages here? See why this may be: https://goo.gl/39bRNJ - - String from = remoteMessage.getFrom(); - if (from != null) { - switch (from) { - case "640759989760": - app.debugLog("Firebase got push from App "+remoteMessage.getData().toString()); - //processAppPush - processAppPush(app, remoteMessage); - break; - case "747285019373": - app.debugLog("Firebase got push from Mobidziennik "+remoteMessage.getData().toString()); - processMobidziennikPush(app, remoteMessage); - break; - case "513056078587": - app.debugLog("Firebase got push from Librus "+remoteMessage.getData().toString()); - processLibrusPush(app, remoteMessage); - break; - case "987828170337": - app.debugLog("Firebase got push from Vulcan "+remoteMessage.getData().toString()); - processVulcanPush(app, remoteMessage); - break; - } - }*/ - } - - private void processMobidziennikPush(App app, RemoteMessage remoteMessage) { - SharedPreferences sharedPreferences = getSharedPreferences("pushtest_mobidziennik", Context.MODE_PRIVATE); - sharedPreferences.edit().putString(Long.toString(System.currentTimeMillis()), remoteMessage.getData().toString()+"\n"+remoteMessage.toString()+"\n"+remoteMessage.getMessageType()).apply(); - String studentIdStr = remoteMessage.getData().get("id_ucznia"); - if (studentIdStr != null) { - int studentId = strToInt(studentIdStr); - AsyncTask.execute(() -> { - List profileList = app.db.profileDao().getAllNow(); - - Profile profile = null; - - for (Profile profileFull: profileList) { - if (profileFull.getLoginStoreType() == LoginStore.LOGIN_TYPE_MOBIDZIENNIK - && studentId == profileFull.getStudentData("studentId", -1)) { - profile = profileFull; - break; - } - } - - if (profile != null) { - if (remoteMessage.getData().get("id_wiadomosci") != null) { - - d(TAG, "Syncing profile " + profile.getId()); - EdziennikTask.Companion.syncProfile(profile.getId(), null, null, null).enqueue(app); - } else { - /*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) - .withProfileData(profile.id, profile.name) - .withTitle(remoteMessage.getData().get("title")) - .withType(Notification.TYPE_SERVER_MESSAGE) - .withFragmentRedirect(MainActivity.DRAWER_ITEM_HOME) - ); - app.notifier.postAll(profile); - app.saveConfig("notifications");*/ - d(TAG, "Syncing profile " + profile.getId()); - EdziennikTask.Companion.syncProfile(profile.getId(), null, null, null).enqueue(app); - } - } - }); - } - } - - private void processLibrusPush(App app, RemoteMessage remoteMessage) { - SharedPreferences sharedPreferences = getSharedPreferences("pushtest_librus", Context.MODE_PRIVATE); - sharedPreferences.edit().putString(Long.toString(System.currentTimeMillis()), remoteMessage.getData().toString()+"\n"+remoteMessage.toString()+"\n"+remoteMessage.getMessageType()).apply(); - } - - private void processVulcanPush(App app, RemoteMessage remoteMessage) { - SharedPreferences sharedPreferences = getSharedPreferences("pushtest_vulcan", Context.MODE_PRIVATE); - sharedPreferences.edit().putString(Long.toString(System.currentTimeMillis()), remoteMessage.getData().toString()+"\n"+remoteMessage.toString()+"\n"+remoteMessage.getMessageType()).apply(); - } - - private void processAppPush(App app, RemoteMessage remoteMessage) { - // Check if message contains a data payload. - /*String type = remoteMessage.getData().get("type"); - if (remoteMessage.getData().size() > 0 - && type != null) { - //Log.d(TAG, "Message data payload: " + remoteMessage.sync()); - switch (type) { - case "app_update": - int versionCode = Integer.parseInt(remoteMessage.getData().get("update_version_code")); - if (BuildConfig.VERSION_CODE < versionCode) { - String updateVersion = remoteMessage.getData().get("update_version"); - String updateUrl = remoteMessage.getData().get("update_url"); - String updateFilename = remoteMessage.getData().get("update_filename"); - boolean updateMandatory = Boolean.parseBoolean(remoteMessage.getData().get("update_mandatory")); - boolean updateDirect = Boolean.parseBoolean(remoteMessage.getData().get("update_direct")); - - if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals(updateVersion)) { - app.appConfig.updateVersion = updateVersion; - app.appConfig.updateUrl = updateUrl; - app.appConfig.updateFilename = updateFilename; - app.appConfig.updateMandatory = updateMandatory; - app.appConfig.updateDirect = updateDirect; - app.saveConfig("updateVersion", "updateUrl", "updateFilename", "updateMandatory"); - } - if (!remoteMessage.getData().containsKey("update_silent")) { - app.notifier.notificationUpdatesShow( - updateVersion, - updateUrl, - updateFilename, - updateDirect); - } - } else { - if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals("")) { - app.appConfig.updateVersion = ""; - app.appConfig.updateMandatory = false; - app.saveConfig("updateVersion", "updateMandatory"); - } - app.notifier.notificationUpdatesHide(); - } - break; - case "message": - app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) - .withTitle(remoteMessage.getData().get("title")) - .withType(pl.szczodrzynski.edziennik.data.db.entity.Notification.TYPE_SERVER_MESSAGE) - .withFragmentRedirect(MainActivity.DRAWER_ITEM_NOTIFICATIONS) - ); - app.notifier.postAll(); - app.saveConfig("notifications"); - break; - case "feedback_message_from_dev": - AsyncTask.execute(() -> { - FeedbackMessage feedbackMessage = new FeedbackMessage(true, remoteMessage.getData().get("message")); - if (feedbackMessage.text.startsWith("test")) { - // todo - } - else { - feedbackMessage.sentTime = Long.parseLong(remoteMessage.getData().get("sent_time")); - if (feedbackMessage.text.startsWith("devmode")) { - app.config.setDevModePassword(feedbackMessage.text.replace("devmode", "")); - app.checkDevModePassword(); - feedbackMessage.text = "devmode "+(App.devMode ? "allowed" : "disallowed"); - } - Intent intent = new Intent("pl.szczodrzynski.edziennik.ui.modules.base.FeedbackActivity"); - intent.putExtra("type", "user_chat"); - intent.putExtra("message", app.gson.toJson(feedbackMessage)); - app.sendBroadcast(intent); - app.db.feedbackMessageDao().add(feedbackMessage); - - app.notifier.add(new Notification(app.getContext(), feedbackMessage.text) - .withTitle(remoteMessage.getData().get("title")) - .withType(pl.szczodrzynski.edziennik.data.db.entity.Notification.TYPE_FEEDBACK_MESSAGE) - .withFragmentRedirect(MainActivity.TARGET_FEEDBACK) - ); - app.notifier.postAll(); - app.saveConfig("notifications"); - } - }); - break; - case "feedback_message_from_user": - AsyncTask.execute(() -> { - FeedbackMessage feedbackMessage = new FeedbackMessage(true, remoteMessage.getData().get("message")); - feedbackMessage.fromUser = remoteMessage.getData().get("from_user"); - feedbackMessage.fromUserName = remoteMessage.getData().get("from_user_name"); - feedbackMessage.sentTime = Long.parseLong(remoteMessage.getData().get("sent_time")); - Intent intent = new Intent("pl.szczodrzynski.edziennik.ui.modules.base.FeedbackActivity"); - intent.putExtra("type", "user_chat"); - intent.putExtra("message", app.gson.toJson(feedbackMessage)); - app.sendBroadcast(intent); - app.db.feedbackMessageDao().add(feedbackMessage); - }); - app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) - .withTitle(remoteMessage.getData().get("title")) - .withType(pl.szczodrzynski.edziennik.data.db.entity.Notification.TYPE_FEEDBACK_MESSAGE) - .withFragmentRedirect(MainActivity.TARGET_FEEDBACK) - ); - app.notifier.postAll(); - app.saveConfig("notifications"); - break; - case "ping": - // just a ping - break - } - }*/ - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/network/NetworkUtils.java b/app/src/main/java/pl/szczodrzynski/edziennik/network/NetworkUtils.java deleted file mode 100644 index dac636c6..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/network/NetworkUtils.java +++ /dev/null @@ -1,142 +0,0 @@ -package pl.szczodrzynski.edziennik.network; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Build; - -import pl.szczodrzynski.edziennik.App; - -import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; -import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; -import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED; - -public class NetworkUtils { - private App app; - - public NetworkUtils(App _app) - { - this.app = _app; - } - - public boolean isOnline() { - assert app != null; - ConnectivityManager cm = - (ConnectivityManager) app.getSystemService(Context.CONNECTIVITY_SERVICE); - assert cm != null; - NetworkInfo netInfo = cm.getActiveNetworkInfo(); - return netInfo != null && netInfo.isConnectedOrConnecting(); - } - - public int checkBackgroundDataRestricted() { - - ConnectivityManager connMgr = (ConnectivityManager) app.getSystemService(Context.CONNECTIVITY_SERVICE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - assert connMgr != null; - switch (connMgr.getRestrictBackgroundStatus()) { - case RESTRICT_BACKGROUND_STATUS_ENABLED: - return 2; - - case RESTRICT_BACKGROUND_STATUS_WHITELISTED: - return 1; - - case RESTRICT_BACKGROUND_STATUS_DISABLED: - return 0; - } - } - else - { - return 0; - } - return 0; - } - - /*public void setSelfSignedSSL(Context mContext, @Nullable String instanceName){ - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - // cert file stored in \app\src\main\assets - Log.d("ION", "certificate: before"); - AssetManager am = mContext.getAssets(); - InputStream caInput = new BufferedInputStream(am.open("certificate.cer")); - Log.d("ION", "certificate: after"); - - Certificate ca = cf.generateCertificate(caInput); - caInput.close(); - - KeyStore keyStore = KeyStore.getInstance("BKS"); - keyStore.load(null, null); - keyStore.setCertificateEntry("ca", ca); - - String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); - TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); - tmf.init(keyStore); - - TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers()); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, wrappedTrustManagers, null); - - AsyncSSLSocketMiddleware sslMiddleWare; - if(TextUtils.isEmpty(instanceName)){ - sslMiddleWare = Ion.getDefault(mContext).getHttpClient().getSSLSocketMiddleware(); - }else { - sslMiddleWare = Ion - .getInstance(mContext, instanceName) - .getHttpClient().getSSLSocketMiddleware(); - } - sslMiddleWare.setTrustManagers(wrappedTrustManagers); - sslMiddleWare.setHostnameVerifier(getHostnameVerifier()); - sslMiddleWare.setSSLContext(sslContext); - }catch (Exception e){ - e.printStackTrace(); - } - } - - private HostnameVerifier getHostnameVerifier() { - return new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - // or the following: - // HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); - // return hv.verify("www.yourserver.com", session); - } - }; - } - - public TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) { - final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0]; - return new TrustManager[]{ - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return originalTrustManager.getAcceptedIssuers(); - } - - public void checkClientTrusted(X509Certificate[] certs, String authType) { - try { - if (certs != null && certs.length > 0){ - certs[0].checkValidity(); - } else { - originalTrustManager.checkClientTrusted(certs, authType); - } - } catch (CertificateException e) { - Log.w("checkClientTrusted", e.toString()); - } - } - - public void checkServerTrusted(X509Certificate[] certs, String authType) { - try { - if (certs != null && certs.length > 0){ - certs[0].checkValidity(); - } else { - originalTrustManager.checkServerTrusted(certs, authType); - } - } catch (CertificateException e) { - Log.w("checkServerTrusted", e.toString()); - } - } - } - }; - }*/ -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/network/TLSSocketFactory.java b/app/src/main/java/pl/szczodrzynski/edziennik/network/TLSSocketFactory.java deleted file mode 100644 index 7cae49a2..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/network/TLSSocketFactory.java +++ /dev/null @@ -1,421 +0,0 @@ -package pl.szczodrzynski.edziennik.network; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.Socket; -import java.net.SocketAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -/** - * Enables TLS v1.2 when creating SSLSockets. - *

- * For some reason, android supports TLS v1.2 from API 16, but enables it by - * default only from API 20. - * @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html - * @see SSLSocketFactory - */ -public class TLSSocketFactory extends SSLSocketFactory { - private static final String[] TLS_V12 = {"TLSv1.2"}; - private static final String[] TLS_V11_V12 = {"TLSv1.1", "TLSv1.2"}; - private static final String[] TLS_V10_V11_V12 = {"TLSv1", "TLSv1.1", "TLSv1.2"}; - - private final SSLSocketFactory delegate; - - public TLSSocketFactory(SSLSocketFactory base) { - this.delegate = base; - } - - @Override - public String[] getDefaultCipherSuites() { - return delegate.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return delegate.getSupportedCipherSuites(); - } - - @Override - public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - return patch(delegate.createSocket(s, host, port, autoClose)); - } - - @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - return patch(delegate.createSocket(host, port)); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { - return patch(delegate.createSocket(host, port, localHost, localPort)); - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - return patch(delegate.createSocket(host, port)); - } - - @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { - return patch(delegate.createSocket(address, port, localAddress, localPort)); - } - - private Socket patch(Socket socket) { - if (socket instanceof SSLSocket) { - ((SSLSocket) socket).setEnabledProtocols(TLS_V10_V11_V12); - //socket = new NoSSLv3SSLSocket((SSLSocket) socket); - } - return socket; - } - - - public class DelegateSSLSocket extends SSLSocket { - - protected final SSLSocket delegate; - - DelegateSSLSocket(SSLSocket delegate) { - this.delegate = delegate; - } - - @Override - public String[] getSupportedCipherSuites() { - return delegate.getSupportedCipherSuites(); - } - - @Override - public String[] getEnabledCipherSuites() { - return delegate.getEnabledCipherSuites(); - } - - @Override - public void setEnabledCipherSuites(String[] suites) { - delegate.setEnabledCipherSuites(suites); - } - - @Override - public String[] getSupportedProtocols() { - return delegate.getSupportedProtocols(); - } - - @Override - public String[] getEnabledProtocols() { - return delegate.getEnabledProtocols(); - } - - @Override - public void setEnabledProtocols(String[] protocols) { - delegate.setEnabledProtocols(protocols); - } - - @Override - public SSLSession getSession() { - return delegate.getSession(); - } - - @Override - public void addHandshakeCompletedListener(HandshakeCompletedListener listener) { - delegate.addHandshakeCompletedListener(listener); - } - - @Override - public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) { - delegate.removeHandshakeCompletedListener(listener); - } - - @Override - public void startHandshake() throws IOException { - delegate.startHandshake(); - } - - @Override - public void setUseClientMode(boolean mode) { - delegate.setUseClientMode(mode); - } - - @Override - public boolean getUseClientMode() { - return delegate.getUseClientMode(); - } - - @Override - public void setNeedClientAuth(boolean need) { - delegate.setNeedClientAuth(need); - } - - @Override - public void setWantClientAuth(boolean want) { - delegate.setWantClientAuth(want); - } - - @Override - public boolean getNeedClientAuth() { - return delegate.getNeedClientAuth(); - } - - @Override - public boolean getWantClientAuth() { - return delegate.getWantClientAuth(); - } - - @Override - public void setEnableSessionCreation(boolean flag) { - delegate.setEnableSessionCreation(flag); - } - - @Override - public boolean getEnableSessionCreation() { - return delegate.getEnableSessionCreation(); - } - - @Override - public void bind(SocketAddress localAddr) throws IOException { - delegate.bind(localAddr); - } - - @Override - public synchronized void close() throws IOException { - delegate.close(); - } - - @Override - public void connect(SocketAddress remoteAddr) throws IOException { - delegate.connect(remoteAddr); - } - - @Override - public void connect(SocketAddress remoteAddr, int timeout) throws IOException { - delegate.connect(remoteAddr, timeout); - } - - @Override - public SocketChannel getChannel() { - return delegate.getChannel(); - } - - @Override - public InetAddress getInetAddress() { - return delegate.getInetAddress(); - } - - @Override - public InputStream getInputStream() throws IOException { - return delegate.getInputStream(); - } - - @Override - public boolean getKeepAlive() throws SocketException { - return delegate.getKeepAlive(); - } - - @Override - public InetAddress getLocalAddress() { - return delegate.getLocalAddress(); - } - - @Override - public int getLocalPort() { - return delegate.getLocalPort(); - } - - @Override - public SocketAddress getLocalSocketAddress() { - return delegate.getLocalSocketAddress(); - } - - @Override - public boolean getOOBInline() throws SocketException { - return delegate.getOOBInline(); - } - - @Override - public OutputStream getOutputStream() throws IOException { - return delegate.getOutputStream(); - } - - @Override - public int getPort() { - return delegate.getPort(); - } - - @Override - public synchronized int getReceiveBufferSize() throws SocketException { - return delegate.getReceiveBufferSize(); - } - - @Override - public SocketAddress getRemoteSocketAddress() { - return delegate.getRemoteSocketAddress(); - } - - @Override - public boolean getReuseAddress() throws SocketException { - return delegate.getReuseAddress(); - } - - @Override - public synchronized int getSendBufferSize() throws SocketException { - return delegate.getSendBufferSize(); - } - - @Override - public int getSoLinger() throws SocketException { - return delegate.getSoLinger(); - } - - @Override - public synchronized int getSoTimeout() throws SocketException { - return delegate.getSoTimeout(); - } - - @Override - public boolean getTcpNoDelay() throws SocketException { - return delegate.getTcpNoDelay(); - } - - @Override - public int getTrafficClass() throws SocketException { - return delegate.getTrafficClass(); - } - - @Override - public boolean isBound() { - return delegate.isBound(); - } - - @Override - public boolean isClosed() { - return delegate.isClosed(); - } - - @Override - public boolean isConnected() { - return delegate.isConnected(); - } - - @Override - public boolean isInputShutdown() { - return delegate.isInputShutdown(); - } - - @Override - public boolean isOutputShutdown() { - return delegate.isOutputShutdown(); - } - - @Override - public void sendUrgentData(int value) throws IOException { - delegate.sendUrgentData(value); - } - - @Override - public void setKeepAlive(boolean keepAlive) throws SocketException { - delegate.setKeepAlive(keepAlive); - } - - @Override - public void setOOBInline(boolean oobinline) throws SocketException { - delegate.setOOBInline(oobinline); - } - - @Override - public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { - delegate.setPerformancePreferences(connectionTime, latency, bandwidth); - } - - @Override - public synchronized void setReceiveBufferSize(int size) throws SocketException { - delegate.setReceiveBufferSize(size); - } - - @Override - public void setReuseAddress(boolean reuse) throws SocketException { - delegate.setReuseAddress(reuse); - } - - @Override - public synchronized void setSendBufferSize(int size) throws SocketException { - delegate.setSendBufferSize(size); - } - - @Override - public void setSoLinger(boolean on, int timeout) throws SocketException { - delegate.setSoLinger(on, timeout); - } - - @Override - public synchronized void setSoTimeout(int timeout) throws SocketException { - delegate.setSoTimeout(timeout); - } - - @Override - public void setTcpNoDelay(boolean on) throws SocketException { - delegate.setTcpNoDelay(on); - } - - @Override - public void setTrafficClass(int value) throws SocketException { - delegate.setTrafficClass(value); - } - - @Override - public void shutdownInput() throws IOException { - delegate.shutdownInput(); - } - - @Override - public void shutdownOutput() throws IOException { - delegate.shutdownOutput(); - } - - @Override - public String toString() { - return delegate.toString(); - } - - @Override - public boolean equals(Object o) { - return delegate.equals(o); - } - } - - private class NoSSLv3SSLSocket extends DelegateSSLSocket { - - private NoSSLv3SSLSocket(SSLSocket delegate) { - super(delegate); - - } - - @Override - public void setEnabledProtocols(String[] protocols) { - if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) { - - List enabledProtocols = new ArrayList<>(Arrays.asList(delegate.getEnabledProtocols())); - if (enabledProtocols.size() > 1) { - if (enabledProtocols.remove("SSLv3")) { - System.out.println("Removed SSLv3 from enabled protocols"); - } - else { - System.out.println("SSLv3 was not an enabled protocol"); - } - } else { - System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols)); - } - protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]); - } - - super.setEnabledProtocols(protocols); - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/DialogExtensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/DialogExtensions.kt new file mode 100644 index 00000000..5bf46d16 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/DialogExtensions.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2021-3-30. + */ + +package pl.szczodrzynski.edziennik.ui.dialogs + +import android.text.InputType +import android.view.LayoutInflater +import androidx.core.view.isVisible +import androidx.core.widget.addTextChangedListener +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.textfield.TextInputEditText +import pl.szczodrzynski.edziennik.databinding.DialogEditTextBinding +import pl.szczodrzynski.edziennik.isNotNullNorBlank + +fun MaterialAlertDialogBuilder.input( + message: CharSequence? = null, + type: Int = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE, + hint: CharSequence? = null, + value: CharSequence? = null, + changeListener: ((editText: TextInputEditText, input: String) -> Boolean)? = null, + positiveButton: Int? = null, + positiveListener: ((editText: TextInputEditText, input: String) -> Boolean)? = null +): MaterialAlertDialogBuilder { + val b = DialogEditTextBinding.inflate(LayoutInflater.from(context), null, false) + b.title.text = message + b.title.isVisible = message.isNotNullNorBlank() + b.text1.hint = hint + b.text1.inputType = type + b.text1.setText(value) + b.text1.addTextChangedListener { text -> + if (changeListener?.invoke(b.text1, text?.toString() ?: "") != false) + b.text1.error = null + } + if (positiveButton != null) { + setPositiveButton(positiveButton) { dialog, _ -> + if (positiveListener?.invoke(b.text1, b.text1.text?.toString() ?: "") != false) + dialog.dismiss() + } + } + setView(b.root) + + return this +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/home/StudentNumberDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/home/StudentNumberDialog.kt index 265bd17b..3f3f8176 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/home/StudentNumberDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/home/StudentNumberDialog.kt @@ -5,12 +5,11 @@ package pl.szczodrzynski.edziennik.ui.dialogs.home import android.text.InputType -import android.widget.Toast -import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import com.afollestad.materialdialogs.MaterialDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.db.entity.Profile +import pl.szczodrzynski.edziennik.ui.dialogs.input class StudentNumberDialog( val activity: AppCompatActivity, @@ -22,25 +21,27 @@ class StudentNumberDialog( private const val TAG = "StudentNumberDialog" } - private lateinit var dialog: AlertDialog - init { run { if (activity.isFinishing) return@run onShowListener?.invoke(TAG) - MaterialDialog.Builder(activity) - .title(R.string.card_lucky_number_set_title) - .content(R.string.card_lucky_number_set_text) - .inputType(InputType.TYPE_CLASS_NUMBER) - .input(null, if (profile.studentNumber == -1) "" else profile.studentNumber.toString()) { _: MaterialDialog?, input: CharSequence -> - try { - profile.studentNumber = input.toString().toInt() - } catch (e: Exception) { - Toast.makeText(activity, R.string.incorrect_format, Toast.LENGTH_SHORT).show() - } + MaterialAlertDialogBuilder(activity) + .setTitle(R.string.card_lucky_number_set_title) + .input( + message = activity.getString(R.string.card_lucky_number_set_text), + type = InputType.TYPE_CLASS_NUMBER, + hint = null, + value = if (profile.studentNumber == -1) null else profile.studentNumber.toString(), + positiveButton = R.string.ok, + positiveListener = { _, input -> + profile.studentNumber = input.toIntOrNull() ?: -1 + true } - .dismissListener { - onDismissListener?.invoke(TAG) - }.show() + ) + .setNegativeButton(R.string.cancel, null) + .setOnDismissListener { + onDismissListener?.invoke(TAG) + } + .show() }} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/GenerateBlockTimetableDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/GenerateBlockTimetableDialog.kt index aefd16f9..c93d5a6e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/GenerateBlockTimetableDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/GenerateBlockTimetableDialog.kt @@ -18,8 +18,8 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.cardview.widget.CardView import androidx.core.content.FileProvider +import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import kotlinx.coroutines.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -107,25 +107,27 @@ class GenerateBlockTimetableDialog( .show() dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick { - when (b.weekSelectionRadioGroup.checkedRadioButtonId) { - R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd) - R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd) - R.id.forSelectedWeekRadio -> selectDate() + app.permissionManager.requestStoragePermission(activity, permissionMessage = R.string.permissions_generate_timetable) { + when (b.weekSelectionRadioGroup.checkedRadioButtonId) { + R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd) + R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd) + R.id.forSelectedWeekRadio -> selectDate() + } } } }} private fun selectDate() { - val date = Date.getToday() - DatePickerDialog - .newInstance({ _, year, monthOfYear, dayOfMonth -> - val dateSelected = Date(year, monthOfYear, dayOfMonth) + MaterialDatePicker.Builder.datePicker() + .setCalendarConstraints(app.profile.getSchoolYearConstrains()) + .build() + .apply { + addOnPositiveButtonClickListener { millis -> + val dateSelected = Date.fromMillisUtc(millis) generateBlockTimetable(dateSelected.weekStart, dateSelected.weekEnd) - }, date.year, date.month, date.day) - .apply { - accentColor = R.attr.colorPrimary.resolveAttr(this@GenerateBlockTimetableDialog.activity) - show(this@GenerateBlockTimetableDialog.activity.supportFragmentManager, "DatePickerDialog") } + } + .show(activity.supportFragmentManager, TAG) } @Subscribe(threadMode = ThreadMode.MAIN) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsFragment.java index 9529f268..d8cdbc2c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsFragment.java @@ -14,7 +14,7 @@ import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.afollestad.materialdialogs.MaterialDialog; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; import org.greenrobot.eventbus.EventBus; @@ -120,7 +120,7 @@ public class AnnouncementsFragment extends Fragment { return; }*/ AnnouncementsAdapter announcementsAdapter = new AnnouncementsAdapter(activity, announcements, (v, announcement) -> { - if (announcement.getText() == null || (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.getSeen() && app.getNetworkUtils().isOnline())) { + if (announcement.getText() == null || (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.getSeen())) { EdziennikTask.Companion.announcementGet(App.Companion.getProfileId(), announcement).enqueue(requireContext()); } else { showAnnouncementDetailsDialog(announcement); @@ -161,12 +161,12 @@ public class AnnouncementsFragment extends Fragment { } private void showAnnouncementDetailsDialog(AnnouncementFull announcement) { - MaterialDialog dialog = new MaterialDialog.Builder(activity) - .title(announcement.getSubject()) - .customView(R.layout.dialog_announcement, true) - .positiveText(R.string.ok) + DialogAnnouncementBinding b = DialogAnnouncementBinding.inflate(LayoutInflater.from(activity), null, false); + new MaterialAlertDialogBuilder(activity) + .setTitle(announcement.getSubject()) + .setView(b.getRoot()) + .setPositiveButton(R.string.ok, null) .show(); - DialogAnnouncementBinding b = DialogAnnouncementBinding.bind(dialog.getCustomView()); b.text.setText(announcement.getTeacherName() +"\n\n"+ (announcement.getStartDate() != null ? announcement.getStartDate().getFormattedString() : "-") + (announcement.getEndDate() != null ? " do " + announcement.getEndDate().getFormattedString() : "")+"\n\n" +announcement.getText()); if (!announcement.getSeen() && app.getProfile().getLoginStoreType() != LOGIN_TYPE_LIBRUS) { announcement.setSeen(true); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java deleted file mode 100644 index c46b3e09..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java +++ /dev/null @@ -1,361 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.attendance; - -import android.graphics.Color; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.util.LongSparseArray; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.Transformation; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.PopupMenu; -import androidx.core.graphics.ColorUtils; -import androidx.databinding.DataBindingUtil; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import antonkozyriatskyi.circularprogressindicator.CircularProgressIndicator; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.MainActivity; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.data.db.entity.Subject; -import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull; -import pl.szczodrzynski.edziennik.databinding.FragmentAttendanceBinding; -import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration; -import pl.szczodrzynski.edziennik.utils.Themes; -import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem; - -import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT_EXCUSED; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED_EXCUSED; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_PRESENT; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_RELEASED; -import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_VULCAN; -import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE; - -public class AttendanceFragment_ extends Fragment { - - private App app = null; - private MainActivity activity = null; - private FragmentAttendanceBinding b = null; - - private int displayMode = MODE_YEAR; - private static final int MODE_YEAR = 0; - private static final int MODE_SEMESTER_1 = 1; - private static final int MODE_SEMESTER_2 = 2; - private long subjectIdFilter = -1; - private LongSparseArray subjectTotalCount; - private LongSparseArray subjectAbsentCount; - private LongSparseArray subjectAttendancePercentage; - - private List attendanceList = null; - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - activity = (MainActivity) getActivity(); - if (getActivity() == null || getContext() == null) - return null; - app = (App) activity.getApplication(); - getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true); - // activity, context and profile is valid - b = DataBindingUtil.inflate(inflater, R.layout.fragment_attendance, container, false); - b.refreshLayout.setParent(activity.getSwipeRefreshLayout()); - return b.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - if (app == null || activity == null || b == null || !isAdded()) - return; - - activity.getBottomSheet().prependItems( - new BottomSheetPrimaryItem(true) - .withTitle(R.string.menu_mark_as_read) - .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) - .withOnClickListener(v3 -> { - activity.getBottomSheet().close(); - AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_ATTENDANCE, true)); - Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show(); - }) - ); - - /*b.refreshLayout.setOnRefreshListener(() -> { - activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_ATTENDANCE, b.refreshLayout); - });*/ - - b.attendancePercentage.setProgressTextAdapter(PERCENTAGE_ADAPTER); - b.attendancePercentage.setMaxProgress(100.0f); - - b.attendanceSummaryTitle.setOnClickListener((v -> { - PopupMenu popupMenu = new PopupMenu(activity, b.attendanceSummaryTitle, Gravity.END); - popupMenu.getMenu().add(0, 0, 0, R.string.summary_mode_year); - popupMenu.getMenu().add(0, 1, 1, R.string.summary_mode_semester_1); - popupMenu.getMenu().add(0, 2, 2, R.string.summary_mode_semester_2); - popupMenu.setOnMenuItemClickListener((item -> { - displayMode = item.getItemId(); - updateList(); - return true; - })); - popupMenu.show(); - })); - - /*if (app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK) { - long attendanceLastSync = app.profile.getStudentData("attendanceLastSync", (long)0); - if (attendanceLastSync == 0) { - attendanceLastSync = app.profile.getSemesterStart(1).getInMillis(); - } - Date lastSyncDate = Date.fromMillis(attendanceLastSync); - if (lastSyncDate.getValue() < Week.getWeekStart().getValue()) { - CafeBar.builder(activity) - .to(activity.getNavView().getCoordinator()) - .content(R.string.sync_old_data_info) - .icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_download_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity)))) - .positiveText(R.string.refresh) - .positiveColor(0xff4caf50) - .negativeText(R.string.ok) - .negativeColor(0x66ffffff) - .onPositive((cafeBar -> { - if (!activity.getSwipeRefreshLayout().isRefreshing()) { - cafeBar.dismiss(); - activity.syncCurrentFeature(); - } - else { - Toast.makeText(app, R.string.please_wait, Toast.LENGTH_SHORT).show(); - } - })) - .onNegative(CafeBar::dismiss) - .autoDismiss(false) - .swipeToDismiss(true) - .floating(true) - .show(); - } - }*/ - - b.attendanceSummarySubject.setOnClickListener((v -> { - AsyncTask.execute(() -> { - List subjectList = App.db.subjectDao().getAllNow(App.Companion.getProfileId()); - PopupMenu popupMenu = new PopupMenu(activity, b.attendanceSummarySubject, Gravity.END); - popupMenu.getMenu().add(0, -1, 0, R.string.subject_filter_disabled); - int index = 0; - DecimalFormat format = new DecimalFormat("0.00"); - for (Subject subject: subjectList) { - int total = subjectTotalCount.get(subject.id, new int[3])[displayMode]; - int absent = subjectAbsentCount.get(subject.id, new int[3])[displayMode]; - if (total == 0) - continue; - int present = total - absent; - float percentage = (float)present / (float)total * 100.0f; - String percentageStr = format.format(percentage); - popupMenu.getMenu().add(0, (int)subject.id, index++, getString(R.string.subject_filter_format, subject.longName, percentageStr)); - } - popupMenu.setOnMenuItemClickListener((item -> { - subjectIdFilter = item.getItemId(); - b.attendanceSummarySubject.setText(item.getTitle().toString().replaceAll("\\s-\\s[0-9]{1,2}\\.[0-9]{1,2}%", "")); - updateList(); - return true; - })); - new Handler(activity.getMainLooper()).post(popupMenu::show); - }); - - })); - - LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); - - b.attendanceView.setHasFixedSize(true); - b.attendanceView.setLayoutManager(linearLayoutManager); - b.attendanceView.addItemDecoration(new SimpleDividerItemDecoration(getContext())); - - b.attendanceView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { - if (recyclerView.canScrollVertically(-1)) { - b.refreshLayout.setEnabled(false); - } - if (!recyclerView.canScrollVertically(-1) && newState == SCROLL_STATE_IDLE) { - b.refreshLayout.setEnabled(true); - } - } - }); - - App.db.attendanceDao().getAll(App.Companion.getProfileId()).observe(this, attendance -> { - if (app == null || activity == null || b == null || !isAdded()) - return; - - if (attendance == null) { - b.attendanceView.setVisibility(View.GONE); - b.attendanceNoData.setVisibility(View.VISIBLE); - return; - } - - attendanceList = attendance; - - countSubjectStats(); - - updateList(); - }); - } - - private void countSubjectStats() { - subjectTotalCount = new LongSparseArray<>(); - subjectAbsentCount = new LongSparseArray<>(); - for (AttendanceFull attendance: attendanceList) { - if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.getBaseType() == TYPE_RELEASED) - continue; - int[] subjectTotal = subjectTotalCount.get(attendance.getSubjectId(), new int[3]); - int[] subjectAbsent = subjectAbsentCount.get(attendance.getSubjectId(), new int[3]); - - subjectTotal[0]++; - subjectTotal[attendance.getSemester()]++; - - if (attendance.getBaseType() == TYPE_ABSENT || attendance.getBaseType() == TYPE_ABSENT_EXCUSED) { - subjectAbsent[0]++; - subjectAbsent[attendance.getSemester()]++; - } - - subjectTotalCount.put(attendance.getSubjectId(), subjectTotal); - subjectAbsentCount.put(attendance.getSubjectId(), subjectAbsent); - } - } - - private void updateList() { - if (app == null || activity == null || b == null || !isAdded()) - return; - - int presentCount = 0; - int absentCount = 0; - int absentUnexcusedCount = 0; - int belatedCount = 0; - int releasedCount = 0; - - List filteredList = new ArrayList<>(); - for (AttendanceFull attendance: attendanceList) { - if (displayMode != MODE_YEAR && attendance.getSemester() != displayMode) - continue; - if (subjectIdFilter != -1 && attendance.getSubjectId() != subjectIdFilter) - continue; - if (attendance.getBaseType() != TYPE_PRESENT) - filteredList.add(attendance); - switch (attendance.getBaseType()) { - case TYPE_PRESENT: - presentCount++; - break; - case TYPE_ABSENT: - absentCount++; - absentUnexcusedCount++; - break; - case TYPE_ABSENT_EXCUSED: - absentCount++; - break; - case TYPE_BELATED_EXCUSED: - case TYPE_BELATED: - belatedCount++; - break; - case TYPE_RELEASED: - releasedCount++; - break; - } - } - if (filteredList.size() > 0) { - AttendanceAdapter adapter; - b.attendanceView.setVisibility(View.VISIBLE); - b.attendanceNoData.setVisibility(View.GONE); - if ((adapter = (AttendanceAdapter) b.attendanceView.getAdapter()) != null) { - //adapter.setItems(filteredList); - adapter.notifyDataSetChanged(); - } - else { - //adapter = new AttendanceAdapter(activity, true, null); - //adapter.setItems(filteredList); - b.attendanceView.setAdapter(adapter); - } - } - else { - b.attendanceView.setVisibility(View.GONE); - b.attendanceNoData.setVisibility(View.VISIBLE); - } - - // SUMMARY - if (displayMode == MODE_YEAR) { - b.attendanceSummaryTitle.setText(getString(R.string.attendance_summary_title_year)); - } - else { - b.attendanceSummaryTitle.setText(getString(R.string.attendance_summary_title_semester_format, displayMode)); - } - b.presentCountContainer.setVisibility(presentCount == 0 ? View.GONE : View.VISIBLE); - b.presentCount.setText(String.format(Locale.getDefault(), "%d", presentCount)); - b.absentCount.setText(String.format(Locale.getDefault(), "%d", absentCount)); - b.absentUnexcusedCount.setText(String.format(Locale.getDefault(), "%d", absentUnexcusedCount)); - b.belatedCount.setText(String.format(Locale.getDefault(), "%d", belatedCount)); - b.releasedCount.setText(String.format(Locale.getDefault(), "%d", releasedCount)); - if (absentUnexcusedCount >= 5) { - b.absentUnexcusedCount.setTextColor(Color.RED); - } - else { - b.absentUnexcusedCount.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity)); - } - - float attendancePercentage; - - // in Mobidziennik there are no TYPE_PRESENT records so we cannot calculate the percentage - if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN) { - float allCount = presentCount + absentCount + belatedCount; // do not count releases - float present = allCount - absentCount; - attendancePercentage = present / allCount * 100.0f; - } - else { - float allCount = presentCount + absentCount + belatedCount + releasedCount; - float present = allCount - absentCount; - attendancePercentage = present / allCount * 100.0f; - } - // if it's still 0%, hide the indicator - if (attendancePercentage <= 0.0f) { - b.attendancePercentage.setVisibility(View.GONE); - return; - } - animatePercentageIndicator(attendancePercentage); - } - - private void animatePercentageIndicator(float percentage) { - Animation a = new Animation() { - protected void applyTransformation(float interpolatedTime, Transformation t) { - float progress = percentage *interpolatedTime; - if (interpolatedTime == 1.0f) { - progress = percentage; - } - int color = ColorUtils.blendARGB(Color.RED, Color.GREEN, progress/100.0f); - b.attendancePercentage.setTextColor(color); - b.attendancePercentage.setProgressColor(color); - b.attendancePercentage.setCurrentProgress(progress); - } - public boolean willChangeBounds() { - return true; - } - }; - a.setDuration(1300); - a.setInterpolator(new AccelerateDecelerateInterpolator()); - b.attendancePercentage.postDelayed(() -> b.attendancePercentage.startAnimation(a), 500); - } - - private static final CircularProgressIndicator.ProgressTextAdapter PERCENTAGE_ADAPTER = value -> { - DecimalFormat df = new DecimalFormat("0.##"); - return df.format(value)+"%"; - }; -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt index bf8eab06..0b39b913 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt @@ -12,7 +12,7 @@ import android.widget.Button import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import cat.ereza.customactivityoncrash.CustomActivityOnCrash -import com.afollestad.materialdialogs.MaterialDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.* import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.BuildConfig @@ -69,47 +69,31 @@ class CrashActivity : AppCompatActivity(), CoroutineScope { val restartButton = findViewById