Compare commits

..

31 Commits

Author SHA1 Message Date
1acf1547d5 [3.9.13-dev] 2019-12-02 22:24:55 +01:00
5d3de35c10 [UI/Timetable] Fix non-null cast exception. 2019-12-02 22:20:53 +01:00
8f8d613f6e [UI] Update libraries & NavLib to fix missing badges on most profiles. 2019-12-02 22:10:36 +01:00
6a161b3c97 [UI/Timetable] Add showing event indicators on lessons. 2019-12-02 21:25:18 +01:00
3e97572100 [Dialog/Events] Use new event dialog in homework fragment and event list dialog. 2019-12-02 19:36:19 +01:00
fc3b6fd1e0 [UI/Event] Fix stuff because i'm dumb 2019-12-02 19:07:30 +01:00
9bc7f9ac11 [UI/Event] Make use of default values in event manual dialog. 2019-12-02 19:04:30 +01:00
0a2f252405 [UI/Home] Implement home card swapping and saving. 2019-12-02 18:12:52 +01:00
09bc658f97 [UI] Fix blank screen when loading activity. 2019-12-02 18:11:12 +01:00
7b04202a00 [Config] Add the rest of config. Migrate from AppConfig and remove most values from AppConfig. 2019-12-01 22:35:42 +01:00
acf364166b [Notifications/LessonChange] Fix lesson change notifications text. 2019-12-01 21:54:59 +01:00
4e88efae94 [Home/Grades] Fix padding. 2019-12-01 20:25:50 +01:00
8df24dc1c4 [Dialog/Events] Create adapter outside of the observer. 2019-12-01 20:25:28 +01:00
8482c27689 [Timetable] Add marking lessons as seen in timetable. 2019-12-01 20:23:48 +01:00
d1265dc1f2 [Dialog/Events] Make new event list dialog. 2019-11-30 23:33:20 +01:00
47d395de71 [Home] Add home grades card. 2019-11-29 23:16:50 +01:00
5b443e02a3 [Mobidziennik] Fix getting grades with no category. Add support for comments. 2019-11-29 18:59:47 +01:00
f8a7d52b1d [Mobidziennik] Fix extracting when attachment has no size. 2019-11-29 17:51:16 +01:00
a133a96819 [UI/Messages] Make message list scroll to last opened message. 2019-11-29 17:29:23 +01:00
c71b8f994c [Messages] Implement Mobidziennik attachments. Fix multiplying attachments in UI. 2019-11-28 23:32:10 +01:00
9b02c97926 [UI/Messages] Convert adapter to Kotlin. 2019-11-28 23:12:21 +01:00
ab06efc934 [Librus/Attachment] A huge structure reformat. 2019-11-28 23:00:25 +01:00
928b73f139 [Config] Implement per-profile config. Update timetable card. 2019-11-28 21:45:27 +01:00
a36fb09bc3 [Dialog/Event] Add event adding in the new event manual dialog. 2019-11-28 15:11:23 +01:00
eaed4b76aa [Timetable] Fix showing question marks in lesson changes. 2019-11-26 23:11:14 +01:00
6d8960f089 [Home/Timetable] Add click listener to timetable card. 2019-11-26 22:56:43 +01:00
ca3b6d0705 [APIv2/Idziennik] Fix getting subject and rewrite getting proposed grades. 2019-11-26 22:28:52 +01:00
c2e7931ea6 [Config] Implement (basic) new app config storage. 2019-11-26 21:55:04 +01:00
d1a5d8cba9 [APIv2/Vulcan] Fix start time in events. 2019-11-26 21:44:35 +01:00
c2f91e6867 [APIv2] Move AppError to API directory. 2019-11-26 21:10:31 +01:00
55e32b8d88 [APIv2/Librus] Temporarily fix subject in attendance. 2019-11-26 21:02:33 +01:00
108 changed files with 2809 additions and 1123 deletions

2
.idea/misc.xml generated
View File

@ -50,7 +50,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -24,6 +24,7 @@
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.WidgetTimetable
-keepnames class pl.szczodrzynski.edziennik.notifications.WidgetNotifications

View File

@ -18,7 +18,6 @@ import android.os.Handler;
import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatDelegate;
@ -68,9 +67,9 @@ import me.leolin.shortcutbadger.ShortcutBadger;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;
import pl.szczodrzynski.edziennik.config.Config;
import pl.szczodrzynski.edziennik.data.db.AppDb;
import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog;
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
import pl.szczodrzynski.edziennik.network.NetworkUtils;
@ -83,9 +82,7 @@ import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.AppConfig;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN;
public class App extends androidx.multidex.MultiDexApplication implements Configuration.Provider {
private static final String TAG = "App";
@ -145,6 +142,11 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
//public Register register; // REGISTER for current profile, read from registerStore
public ProfileFull profile;
public Config config;
private static Config mConfig;
public static Config getConfig() {
return mConfig;
}
// other stuff
public Gson gson;
@ -194,6 +196,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
gson = new Gson();
networkUtils = new NetworkUtils(this);
config = new Config(db);
config.migrate(this);
mConfig = config;
Iconics.init(getApplicationContext());
Iconics.registerFont(SzkolnyFont.INSTANCE);
@ -208,7 +214,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
loadConfig();
Themes.INSTANCE.setThemeInt(appConfig.appTheme);
Themes.INSTANCE.setThemeInt(config.getUi().getTheme());
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
@ -227,7 +233,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
if ("f054761fbdb6a238".equals(deviceId) || BuildConfig.DEBUG) {
devMode = true;
}
else if (appConfig.devModePassword != null) {
else if (config.getDevModePassword() != null) {
checkDevModePassword();
}
@ -298,7 +304,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
//profileLoadById(appSharedPrefs.getInt("current_profile_id", 1));
if (appConfig.registerSyncEnabled) {
if (config.getSync().getEnabled()) {
SyncWorker.Companion.scheduleNext(this, false);
}
else {
@ -362,11 +368,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcutTimetable, shortcutAgenda, shortcutGrades, shortcutHomework, shortcutMessages));
}
if (appConfig.appInstalledTime == 0) {
if (config.getAppInstalledTime() == 0) {
try {
appConfig.appInstalledTime = getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime;
appConfig.appRateSnackbarTime = appConfig.appInstalledTime + 7 * 24 * 60 * 60 * 1000;
saveConfig("appInstalledTime", "appRateSnackbarTime");
config.setAppInstalledTime(getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime);
config.setAppRateSnackbarTime(config.getAppInstalledTime() + 7 * 24 * 60 * 60 * 1000);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
@ -434,9 +439,9 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
final long startTime = System.currentTimeMillis();
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for App is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()+". Time is "+(System.currentTimeMillis() - startTime));
appConfig.fcmToken = instanceIdResult.getToken();
config.getSync().setTokenApp(instanceIdResult.getToken());
});
FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
/*FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for Mobidziennik is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
appConfig.fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
});
@ -450,7 +455,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
if (pair == null || pair.first == null || !pair.first.equals(instanceIdResult.getToken())) {
appConfig.fcmTokens.put(LOGIN_TYPE_VULCAN, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
}
});
});*/
FirebaseMessaging.getInstance().subscribeToTopic(getPackageName());
@ -513,7 +518,8 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply();
Log.w(TAG, "Should remove app.appConfig."+fieldName);
//appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply(); TODO migration
}
}
}
@ -585,7 +591,11 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
}
public void profileSave() {
AsyncTask.execute(() -> {
db.profileDao().add(profile);
});
}
public void profileSaveAsync() {
AsyncTask.execute(() -> {
@ -606,14 +616,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
db.profileDao().add(profileFull);
db.loginStoreDao().add(profileFull);
}
public void profileSaveFull(Profile profile, LoginStore loginStore) {
db.profileDao().add(profile);
db.loginStoreDao().add(loginStore);
}
public ProfileFull profileGetOrNull(int id) {
return db.profileDao().getFullByIdNow(id);
}
public void profileLoadById(int id) {
profileLoadById(id, false);
@ -636,6 +638,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
profileId = profile.getId();
appSharedPrefs.edit().putInt("current_profile_id", profile.getId()).apply();
config.setProfile(profileId);
}
else if (!loadedLast) {
profileLoadById(profileLastId(), true);
@ -706,7 +709,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
public void checkDevModePassword() {
try {
devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", appConfig.devModePassword).equals("ok here you go it's enabled now")
devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", config.getDevModePassword()).equals("ok here you go it's enabled now")
|| BuildConfig.DEBUG;
} catch (Exception e) {
e.printStackTrace();

View File

@ -0,0 +1,319 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik
import android.util.Log
import androidx.work.Configuration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScope {
companion object {
var devMode = false
}
//lateinit var db: AppDb
//val config by lazy { Config(db); // TODO migrate }
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun getWorkManagerConfiguration() = Configuration.Builder()
.setMinimumLoggingLevel(Log.VERBOSE)
.build()
/*val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
val notifier by lazy { Notifier(this) }
val permissionChecker by lazy { PermissionChecker(this) }
lateinit var profile: ProfileFull
/* _ _ _______ _______ _____
| | | |__ __|__ __| __ \
| |__| | | | | | | |__) |
| __ | | | | | | ___/
| | | | | | | | | |
|_| |_| |_| |_| |*/
val http: OkHttpClient by lazy {
val builder = OkHttpClient.Builder()
.cache(null)
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cookieJar(cookieJar)
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
builder.installHttpsSupport()
if (devMode || BuildConfig.DEBUG) {
HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this))
val chuckerCollector = ChuckerCollector(this, true, Period.ONE_HOUR)
val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector)
builder.addInterceptor(chuckerInterceptor)
}
builder.build()
}
val httpLazy: OkHttpClient by lazy {
http.newBuilder()
.followRedirects(false)
.followSslRedirects(false)
.build()
}
val cookieJar by lazy { PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(this)) }
/* _____ _ _
/ ____(_) | |
| (___ _ __ _ _ __ __ _| |_ _ _ _ __ ___
\___ \| |/ _` | '_ \ / _` | __| | | | '__/ _ \
____) | | (_| | | | | (_| | |_| |_| | | | __/
|_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___|
__/ |
|__*/
private val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" }
private val signature: String by lazy {
var str = ""
try {
val packageInfo: PackageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
for (signature in packageInfo.signatures) {
val signatureBytes = signature.toByteArray()
val md = MessageDigest.getInstance("SHA")
md.update(signatureBytes)
str = Base64.encodeToString(md.digest(), Base64.DEFAULT)
}
} catch (e: Exception) {
e.printStackTrace()
}
str
}
private var unreadBadgesAvailable = true
/* _____ _
/ ____| | |
___ _ __ | | _ __ ___ __ _| |_ ___
/ _ \| '_ \| | | '__/ _ \/ _` | __/ _ \
| (_) | | | | |____| | | __/ (_| | || __/
\___/|_| |_|\_____|_| \___|\__,_|\__\__*/
override fun onCreate() {
super.onCreate()
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
CaocConfig.Builder.create()
.backgroundMode(CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM)
.enabled(true)
.showErrorDetails(true)
.showRestartButton(true)
.logErrorOnRestart(true)
.trackActivities(true)
.minTimeBetweenCrashesMs(60*1000)
.errorDrawable(R.drawable.ic_rip)
.restartActivity(MainActivity::class.java)
.errorActivity(CrashActivity::class.java)
.apply()
Iconics.init(applicationContext)
Iconics.registerFont(SzkolnyFont)
db = AppDb.getDatabase(this)
Themes.themeInt = config.ui.theme
MHttp.instance().customOkHttpClient(http)
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG
if (config.devModePassword != null)
checkDevModePassword()
launch { async(Dispatchers.Default) {
if (config.sync.enabled) {
scheduleNext(this@App, false)
} else {
cancelNext(this@App)
}
db.metadataDao().countUnseen().observeForever { count: Int ->
if (unreadBadgesAvailable)
unreadBadgesAvailable = ShortcutBadger.applyCount(this@App, count)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
val shortcutManager = getSystemService(ShortcutManager::class.java)
val shortcutTimetable = ShortcutInfo.Builder(this@App, "item_timetable")
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
.build()
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
.build()
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
.build()
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
.build()
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
.build()
shortcutManager.dynamicShortcuts = listOf(
shortcutTimetable,
shortcutAgenda,
shortcutGrades,
shortcutHomework,
shortcutMessages
)
} // shortcuts - end
if (config.appInstalledTime == 0L)
try {
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
config.appRateSnackbarTime = config.appInstalledTime + 7*DAY*MS
} catch (e: NameNotFoundException) {
e.printStackTrace()
}
val pushMobidziennikApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
.build(),
"Mobidziennik2"
)
val pushLibrusApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
.setApplicationId("1:513056078587:android:1e29083b760af544")
.build(),
"Librus"
)
val pushVulcanApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
.build(),
"Vulcan"
)
try {
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
config.sync.tokenApp = token
}
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenMobidziennik) {
config.sync.tokenMobidziennik = token
config.sync.tokenMobidziennikList = listOf()
}
}
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenLibrus) {
config.sync.tokenLibrus = token
config.sync.tokenLibrusList = listOf()
}
}
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenVulcan) {
config.sync.tokenVulcan = token
config.sync.tokenVulcanList = listOf()
}
}
FirebaseMessaging.getInstance().subscribeToTopic(packageName)
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}}
}
private fun profileLoad(profileId: Int) {
db.profileDao().getFullByIdNow(profileId)?.also {
profile = it
} ?: run {
if (!::profile.isInitialized) {
profile = ProfileFull(-1, "", "", -1)
}
}
}
fun profileLoad(profileId: Int, onSuccess: (profile: ProfileFull) -> Unit) {
launch {
val deferred = async(Dispatchers.Default) {
profileLoad(profileId)
}
deferred.await()
onSuccess(profile)
}
}
private fun OkHttpClient.Builder.installHttpsSupport() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
try {
try {
ProviderInstaller.installIfNeeded(this@App)
} catch (e: Exception) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
?: return
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
}
} catch (exc: Exception) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
}
}
}
fun checkDevModePassword() {
devMode = try {
Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", config.devModePassword) == "ok here you go it's enabled now" || BuildConfig.DEBUG
} catch (e: Exception) {
e.printStackTrace()
false
}
}*/
}

View File

@ -5,6 +5,8 @@ import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Resources
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Build
@ -36,6 +38,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.navlib.getColorFromAttr
import pl.szczodrzynski.navlib.getColorFromRes
import java.text.SimpleDateFormat
import java.util.*
@ -200,6 +203,7 @@ const val DAY = 24L*HOUR
const val WEEK = 7L*DAY
const val MONTH = 30L*DAY
const val YEAR = 365L*DAY
const val MS = 1000L
fun <T> LongSparseArray<T>.values(): List<T> {
val result = mutableListOf<T>()
@ -587,3 +591,18 @@ fun Context.timeLeft(time: Int, delimiter: String = " "): String {
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
}
inline fun <reified T> Any?.instanceOfOrNull(): T? {
return when (this) {
is T -> this
else -> null
}
}
fun Drawable.setTintColor(color: Int): Drawable {
colorFilter = PorterDuffColorFilter(
color,
PorterDuff.Mode.SRC_ATOP
)
return this
}

View File

@ -243,7 +243,7 @@ class MainActivity : AppCompatActivity() {
setTheme(Themes.appTheme)
app.appConfig.language?.let {
app.config.ui.language?.let {
setLanguage(it)
}
@ -306,10 +306,10 @@ class MainActivity : AppCompatActivity() {
}
drawer.apply {
setAccountHeaderBackground(app.appConfig.headerBackground)
setAccountHeaderBackground(app.config.ui.headerBackground)
drawerProfileListEmptyListener = {
app.appConfig.loginFinished = false
app.config.loginFinished = false
app.saveConfig("loginFinished")
profileListEmptyListener()
}
@ -334,7 +334,7 @@ class MainActivity : AppCompatActivity() {
drawerProfileSettingClickListener = this@MainActivity.profileSettingClickListener
miniDrawerVisibleLandscape = null
miniDrawerVisiblePortrait = app.appConfig.miniDrawerVisible
miniDrawerVisiblePortrait = app.config.ui.miniMenuVisible
}
}
@ -387,21 +387,23 @@ class MainActivity : AppCompatActivity() {
SyncWorker.scheduleNext(app)
// APP BACKGROUND
if (app.appConfig.appBackground != null) {
if (app.config.ui.appBackground != null) {
try {
var bg = app.appConfig.appBackground
val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg")
if (bgDir.exists()) {
val files = bgDir.listFiles()
val r = Random()
val i = r.nextInt(files.size)
bg = files[i].toString()
}
val linearLayout = b.root
if (bg.endsWith(".gif")) {
linearLayout.background = GifDrawable(bg)
} else {
linearLayout.background = BitmapDrawable.createFromPath(bg)
app.config.ui.appBackground?.let {
var bg = it
val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg")
if (bgDir.exists()) {
val files = bgDir.listFiles()
val r = Random()
val i = r.nextInt(files.size)
bg = files[i].toString()
}
val linearLayout = b.root
if (bg.endsWith(".gif")) {
linearLayout.background = GifDrawable(bg)
} else {
linearLayout.background = BitmapDrawable.createFromPath(bg)
}
}
} catch (e: IOException) {
e.printStackTrace()
@ -409,7 +411,7 @@ class MainActivity : AppCompatActivity() {
}
// WHAT'S NEW DIALOG
if (app.appConfig.lastAppVersion != BuildConfig.VERSION_CODE) {
if (app.config.appVersion < BuildConfig.VERSION_CODE) {
ServerRequest(app, app.requestScheme + APP_URL + "main.php?just_updated", "MainActivity/JU")
.run { e, result ->
Handler(Looper.getMainLooper()).post {
@ -420,17 +422,16 @@ class MainActivity : AppCompatActivity() {
}
}
}
if (app.appConfig.lastAppVersion < 170) {
if (app.config.appVersion < 170) {
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
//startActivity(intent);
} else {
app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE
app.saveConfig("lastAppVersion")
app.config.appVersion = BuildConfig.VERSION_CODE
}
}
// RATE SNACKBAR
if (app.appConfig.appRateSnackbarTime != 0L && app.appConfig.appRateSnackbarTime <= System.currentTimeMillis()) {
if (app.config.appRateSnackbarTime != 0L && app.config.appRateSnackbarTime <= System.currentTimeMillis()) {
navView.coordinator.postDelayed({
CafeBar.builder(this)
.content(R.string.rate_snackbar_text)
@ -444,20 +445,17 @@ class MainActivity : AppCompatActivity() {
.onPositive { cafeBar ->
Utils.openGooglePlay(this)
cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = 0
app.saveConfig("appRateSnackbarTime")
app.config.appRateSnackbarTime = 0
}
.onNegative { cafeBar ->
Toast.makeText(this, "Szkoda, opinie innych pomagają mi rozwijać aplikację.", Toast.LENGTH_LONG).show()
cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = 0
app.saveConfig("appRateSnackbarTime")
app.config.appRateSnackbarTime = 0
}
.onNeutral { cafeBar ->
Toast.makeText(this, "OK", Toast.LENGTH_LONG).show()
cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000
app.saveConfig("appRateSnackbarTime")
app.config.appRateSnackbarTime = System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000
}
.autoDismiss(false)
.swipeToDismiss(true)
@ -689,14 +687,13 @@ class MainActivity : AppCompatActivity() {
}*/
if (navLoading) {
navLoading = false
b.fragment.removeAllViews()
if (intentTargetId == -1)
intentTargetId = HOME_ID
}
when {
app.profile == null -> {
app.profile == null || app.profile.id == -1 -> {
if (intentProfileId == -1)
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
loadProfile(intentProfileId, intentTargetId, extras)
@ -709,13 +706,14 @@ class MainActivity : AppCompatActivity() {
}
intentTargetId != -1 -> {
drawer.currentProfile = app.profile.id
if (navTargetId != intentTargetId)
if (navTargetId != intentTargetId || navLoading)
loadTarget(intentTargetId, extras)
}
else -> {
drawer.currentProfile = app.profile.id
}
}
navLoading = false
}
override fun recreate() {
@ -763,7 +761,7 @@ class MainActivity : AppCompatActivity() {
finish()
}
else {
if (!app.appConfig.loginFinished)
if (!app.config.loginFinished)
finish()
else {
handleIntent(data?.extras)
@ -800,13 +798,16 @@ class MainActivity : AppCompatActivity() {
this.runOnUiThread {
if (app.profile == null) {
LoginActivity.firstCompleted = false
if (app.appConfig.loginFinished) {
if (app.config.loginFinished) {
// this shouldn't run
profileListEmptyListener()
}
} else {
setDrawerItems()
drawer.currentProfile = app.profile.id
// the drawer profile is updated automatically when the drawer item is clicked
// update it manually when switching profiles from other source
//if (drawer.currentProfile != app.profile.id)
drawer.currentProfile = app.profile.id
loadTarget(drawerSelection, arguments)
}
}
@ -973,7 +974,7 @@ class MainActivity : AppCompatActivity() {
val item = DrawerPrimaryItem()
.withIdentifier(target.id.toLong())
.withName(target.name)
.withHiddenInMiniDrawer(!app.appConfig.miniDrawerButtonIds.contains(target.id))
.withHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
.also { if (target.description != null) it.withDescription(target.description!!) }
.also { if (target.icon != null) it.withIcon(target.icon!!) }
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }

View File

@ -93,8 +93,8 @@ public class Notifier {
public boolean shouldBeQuiet() {
long now = Time.getNow().getInMillis();
long start = app.appConfig.quietHoursStart;
long end = app.appConfig.quietHoursEnd;
long start = app.config.getSync().getQuietHoursStart();
long end = app.config.getSync().getQuietHoursEnd();
if (start > end) {
end += 1000 * 60 * 60 * 24;
//Log.d(TAG, "Night passing");
@ -104,7 +104,7 @@ public class Notifier {
//Log.d(TAG, "Now is smaller");
}
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
return app.appConfig.quietHoursStart > 0 && now >= start && now <= end;
return start > 0 && now >= start && now <= end;
}
public int getNotificationDefaults() {
@ -312,7 +312,7 @@ public class Notifier {
| |
|*/
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename, boolean updateDirect) {
if (!app.appConfig.notifyAboutUpdates)
if (!app.config.getSync().getNotifyAboutUpdates())
return;
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
.putExtra("update_version", updateVersion)
@ -340,7 +340,7 @@ public class Notifier {
}
public void notificationUpdatesHide() {
if (!app.appConfig.notifyAboutUpdates)
if (!app.config.getSync().getNotifyAboutUpdates())
return;
notificationManager.cancel(ID_UPDATES);
}

View File

@ -52,9 +52,9 @@ class WidgetTimetable : AppWidgetProvider() {
val app = context.applicationContext as App
var bellSyncDiffMillis: Long = 0
if (app.appConfig.bellSyncDiff != null) {
bellSyncDiffMillis = (app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000).toLong()
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier.toLong()
app.config.timetable.bellSyncDiff?.let {
bellSyncDiffMillis = (it.hour * 60 * 60 * 1000 + it.minute * 60 * 1000 + it.second * 1000).toLong()
bellSyncDiffMillis *= app.config.timetable.bellSyncMultiplier.toLong()
bellSyncDiffMillis *= -1
}

View File

@ -168,6 +168,8 @@ const val EXCEPTION_LIBRUS_API_REQUEST = 904
const val EXCEPTION_LIBRUS_SYNERGIA_REQUEST = 905
const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 906
const val EXCEPTION_VULCAN_API_REQUEST = 907
const val EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST = 908
const val EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST = 909
const val EXCEPTION_NOTIFY_AND_SYNC = 910
const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911
const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912

View File

@ -24,7 +24,7 @@ object Regexes {
"""Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_GRADES_DETAILS by lazy {
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?<small>\((.+?)\)</small>.*?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?(?:<small>\((.+?)\)</small>.*?)?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>.*?(?:Komentarz:.*?<strong>(.+?)</strong>)?</span>""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_EVENT_TYPE by lazy {

View File

@ -5,8 +5,8 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_APP_SERVER_ERROR
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_APP_SERVER_ERROR
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
@ -176,4 +176,4 @@ class ServerSync(val data: Data, val onSuccess: () -> Unit) {
onSuccess()
}}
}
}

View File

@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennik.Mobidziennik
import pl.szczodrzynski.edziennik.api.v2.template.Template
import pl.szczodrzynski.edziennik.api.v2.vulcan.Vulcan
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
@ -24,7 +25,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
fun attachmentGet(profileId: Int, messageId: Long, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(messageId, attachmentId, attachmentName))
fun attachmentGet(profileId: Int, message: Message, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(message, attachmentId, attachmentName))
}
private lateinit var loginStore: LoginStore
@ -74,7 +75,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
is FirstLoginRequest -> edziennikInterface?.firstLogin()
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.messageId, request.attachmentId, request.attachmentName)
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.message, request.attachmentId, request.attachmentName)
}
}
@ -92,5 +93,5 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
data class SyncProfileListRequest(val profileList: List<Int>)
data class MessageGetRequest(val message: MessageFull)
class AnnouncementsReadRequest
data class AttachmentGetRequest(val messageId: Long, val attachmentId: Long, val attachmentName: String)
data class AttachmentGetRequest(val message: Message, val attachmentId: Long, val attachmentName: String)
}

View File

@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
@ -70,7 +71,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
}
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}

View File

@ -59,13 +59,14 @@ class IdziennikWebExams(override val data: DataIdziennik,
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { exam ->
val id = exam.getLong("_recordId") ?: return@forEach
val examDate = Date.fromY_m_d(exam.getString("data") ?: return@forEach)
val subjectId = data.getSubject(exam.getString("przedmiot") ?: return@forEach,
-1, "").id
val teacherId = data.getTeacherByLastFirst(exam.getString("wpisal")
?: return@forEach).id
val subjectName = exam.getString("przedmiot") ?: return@forEach
val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = exam.getString("wpisal") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id
val topic = exam.getString("zakres") ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
val topic = exam.getString("zakres") ?: ""
val eventType = when (exam.getString("rodzaj")) {
"sprawdzian/praca klasowa" -> Event.TYPE_EXAM

View File

@ -50,10 +50,10 @@ class IdziennikWebHomework(override val data: DataIdziennik,
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework ->
val id = homework.getLong("_recordId") ?: return@forEach
val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach)
val subjectId = data.getSubject(homework.getString("przed") ?: return@forEach,
-1, "").id
val teacherId = data.getTeacherByLastFirst(homework.getString("usr")
?: return@forEach).id
val subjectName = homework.getString("przed") ?: return@forEach
val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = homework.getString("usr") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime
val topic = homework.getString("tytul") ?: ""

View File

@ -10,21 +10,24 @@ import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.asJsonObjectList
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue
class IdziennikWebProposedGrades(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) {
val onSuccess: () -> Unit) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebProposedGrades"
}
init {
init { data.profile?.also { profile ->
webApiGet(TAG, IDZIENNIK_WEB_MISSING_GRADES, mapOf(
"idPozDziennika" to data.registerId
)) { result ->
@ -34,17 +37,17 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
return@webApiGet
}
val jSubjects = json.getAsJsonArray("Przedmioty")
for (jSubjectEl in jSubjects) {
val jSubject = jSubjectEl.getAsJsonObject()
// jSubject
val rSubject = data.getSubject(jSubject.get("Przedmiot").getAsString(), -1, jSubject.get("Przedmiot").getAsString())
val semester1Proposed = jSubject.get("OcenaSem1").getAsString()
val semester2Proposed = jSubject.get("OcenaSem2").getAsString()
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { subject ->
val subjectName = subject.getString("Przedmiot") ?: return@forEach
val subjectObject = data.getSubject(subjectName, null, subjectName)
val semester1Proposed = subject.getString("OcenaSem1") ?: ""
val semester1Value = getWordGradeValue(semester1Proposed)
val semester1Id = subjectObject.id * (-100) - 1
val semester2Proposed = subject.getString("OcenaSem2") ?: ""
val semester2Value = getWordGradeValue(semester2Proposed)
val semester1Id = rSubject.id * -100 - 1
val semester2Id = rSubject.id * -100 - 2
val semester2Id = subjectObject.id * (-100) - 2
if (semester1Proposed != "") {
val gradeObject = Grade(
@ -58,17 +61,18 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
0f,
1,
-1,
rSubject.id)
gradeObject.type = TYPE_SEMESTER1_PROPOSED
subjectObject.id
).apply {
type = TYPE_SEMESTER1_PROPOSED
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
gradeObject.id,
profile?.empty ?: false,
profile?.empty ?: false,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
@ -85,17 +89,18 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
0f,
2,
-1,
rSubject.id)
gradeObject.type = TYPE_YEAR_PROPOSED
subjectObject.id
).apply {
type = TYPE_YEAR_PROPOSED
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
gradeObject.id,
profile?.empty ?: false,
profile?.empty ?: false,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
@ -104,5 +109,5 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES, SYNC_ALWAYS)
onSuccess()
}
}
}}
}

View File

@ -5,13 +5,14 @@
package pl.szczodrzynski.edziennik.api.v2.interfaces
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
interface EdziennikInterface {
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
fun getMessage(message: MessageFull)
fun markAllAnnouncementsAsRead()
fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String)
fun getAttachment(message: Message, attachmentId: Long, attachmentName: String)
fun firstLogin()
fun cancel()
}

View File

@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.api.v2.librus.login.*
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
@ -105,12 +106,12 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
}
}
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusLoginMessages(data) {
LibrusMessagesGetAttachment(data, messageId, attachmentId, attachmentName) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}

View File

@ -60,7 +60,7 @@ val LibrusFeatures = listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
data.app.appConfig.fcmTokens[LOGIN_TYPE_LIBRUS]?.second?.contains(data.profileId) == false
!data.app.config.sync.tokenLibrusList.contains(data.profileId)
},

View File

@ -196,7 +196,7 @@ open class LibrusMessages(open val data: DataLibrus) {
try {
onSuccess(file)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST)
.withResponse(response)
.withThrowable(e))
}
@ -206,7 +206,7 @@ open class LibrusMessages(open val data: DataLibrus) {
try {
onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST)
.withThrowable(e))
}
}

View File

@ -36,14 +36,14 @@ class LibrusApiAttendances(override val data: DataLibrus,
val lessonNo = attendance.getInt("LessonNo") ?: return@forEach
val startTime = data.lessonRanges.get(lessonNo).startTime
val lessonDate = Date.fromY_m_d(attendance.getString("Date"))
val subjectId = data.lessonList.singleOrNull {
it.weekDay == lessonDate.weekDay && it.startTime.value == startTime.value
}?.subjectId ?: -1
val semester = attendance.getInt("Semester") ?: return@forEach
val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
val typeObject = data.attendanceTypes.get(type)
val topic = typeObject?.name ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, lessonDate)
val subjectId = lessonList.firstOrNull { it.startTime == startTime }?.subjectId ?: -1
val attendanceObject = Attendance(
profileId,
id,

View File

@ -15,15 +15,16 @@ import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent.Companion.TYP
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
import kotlin.coroutines.CoroutineContext
class LibrusMessagesGetAttachment(override val data: DataLibrus, val messageId: Long, val attachmentId: Long,
val attachmentName: String, val onSuccess: () -> Unit) : LibrusMessages(data), CoroutineScope {
class LibrusMessagesGetAttachment(
override val data: DataLibrus, val message: Message, val attachmentId: Long,
val attachmentName: String, val onSuccess: () -> Unit) : LibrusMessages(data), CoroutineScope {
companion object {
const val TAG = "LibrusMessagesGetAttachment"
}
@ -38,7 +39,7 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus, val messageId:
init {
messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf(
"fileId" to attachmentId,
"msgId" to messageId,
"msgId" to message.id,
"archive" to 0
)) { doc ->
val downloadLink = doc.select("response GetFileDownloadLink downloadLink").text()
@ -55,8 +56,6 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus, val messageId:
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withApiResponse(doc.toString()))
}
onSuccess()
}
}
@ -92,12 +91,11 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus, val messageId:
private fun downloadAttachment(attachmentKey: String) {
val targetFile = File(Utils.getStorageDir(), attachmentName)
sandboxGetFile(TAG, "CSDownload&singleUseKey=$attachmentKey",
targetFile, { file ->
sandboxGetFile(TAG, "CSDownload&singleUseKey=$attachmentKey", targetFile, { file ->
val event = AttachmentGetEvent(
profileId,
messageId,
message.id,
attachmentId,
TYPE_FINISHED,
file.absolutePath
@ -108,10 +106,12 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus, val messageId:
EventBus.getDefault().post(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
messageId,
message.id,
attachmentId,
TYPE_PROGRESS,
bytesWritten = written

View File

@ -13,8 +13,8 @@ import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusPortal
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_LIBRUS_DISCONNECTED
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_SYNERGIA_NOT_ACTIVATED
import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_LIBRUS_DISCONNECTED
import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_SYNERGIA_NOT_ACTIVATED
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
@ -89,4 +89,4 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
}
}
}
}
}

View File

@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGetAttachment
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGetMessage
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.firstlogin.MobidziennikFirstLogin
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin
@ -18,6 +19,7 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
@ -76,8 +78,12 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
}
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
MobidziennikLoginWeb(data) {
MobidziennikWebGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}
}
override fun firstLogin() {

View File

@ -6,12 +6,14 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.File
open class MobidziennikWeb(open val data: DataMobidziennik) {
companion object {
@ -93,4 +95,77 @@ open class MobidziennikWeb(open val data: DataMobidziennik) {
.build()
.enqueue()
}
fun webGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit,
onProgress: (written: Long, total: Long) -> Unit) {
val url = "https://${data.loginServerName}.mobidziennik.pl$action"
d(tag, "Request: Mobidziennik/Web - $url")
if (data.webSessionKey == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SESSION_KEY)
return
}
if (data.webSessionValue == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE)
return
}
if (data.webServerId == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID)
return
}
val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) {
if (file == null) {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withResponse(response))
return
}
try {
onSuccess(file)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST)
.withResponse(response)
.withThrowable(e))
}
}
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
try {
onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST)
.withThrowable(e))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
data.app.cookieJar.saveFromResponse(null, listOf(
Cookie.Builder()
.name(data.webSessionKey!!)
.value(data.webSessionValue!!)
.domain("${data.loginServerName}.mobidziennik.pl")
.secure().httpOnly().build(),
Cookie.Builder()
.name("SERVERID")
.value(data.webServerId!!)
.domain("${data.loginServerName}.mobidziennik.pl")
.secure().httpOnly().build()
))
Request.builder()
.url(url)
.userAgent(MOBIDZIENNIK_USER_AGENT)
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-28.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class MobidziennikWebGetAttachment(
override val data: DataMobidziennik, val message: Message, val attachmentId: Long,
val attachmentName: String, val onSuccess: () -> Unit) : MobidziennikWeb(data) {
companion object {
private const val TAG = "MobidziennikWebGetAttachment"
}
init {
val targetFile = File(Utils.getStorageDir(), attachmentName)
val typeUrl = if (message.type == Message.TYPE_SENT)
"wiadwyslana"
else
"wiadodebrana"
webGetFile(TAG, "/dziennik/$typeUrl/?id=${message.id}&zalacznik=$attachmentId", targetFile, { file ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
onSuccess()
}) { written, _ ->
// TODO make use of bytesTotal
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().post(event)
}
}
}

View File

@ -124,7 +124,7 @@ class MobidziennikWebGetMessage(
val attachmentName = it.ownText()
Regexes.MOBIDZIENNIK_MESSAGE_ATTACHMENT.find(it.outerHtml())?.let { match ->
val attachmentId = match[1].toLong()
var size = match[2].toFloatOrNull() ?: 0f
var size = match[2].toFloatOrNull() ?: -1f
when (match[3]) {
"K" -> size *= 1024f
"M" -> size *= 1024f * 1024f

View File

@ -96,13 +96,17 @@ class MobidziennikWebGrades(override val data: DataMobidziennik,
if (Regexes.MOBIDZIENNIK_GRADES_COUNT_TO_AVG.containsMatchIn(html)) {
Regexes.MOBIDZIENNIK_GRADES_DETAILS.find(html)?.let { match ->
val gradeName = match[1]
val gradeDescription = match[2]
var gradeDescription = match[2]
val gradeValue = match[3].toFloatOrNull() ?: 0.0f
val teacherName = match[4].fixWhiteSpaces()
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1
val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1
if (match[5].isNotEmpty()) {
gradeDescription += "\n"+match[5].replace("<br>", "\n")
}
val gradeObject = Grade(
profileId,
gradeId,

View File

@ -91,7 +91,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
.addParameter("ip", data.app.deviceId)
.addParameter("login", data.loginUsername)
.addParameter("haslo", data.loginPassword)
.addParameter("token", data.app.appConfig.fcmTokens[LOGIN_TYPE_MOBIDZIENNIK]?.first)
.addParameter("token", data.app.config.sync.tokenMobidziennik)
.post()
.callback(callback)
.build()

View File

@ -8,7 +8,6 @@ import android.content.Context
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import pl.szczodrzynski.edziennik.data.api.AppError
class ApiError(val tag: String, val errorCode: Int) {
var profileId: Int? = null
@ -67,4 +66,4 @@ class ApiError(val tag: String, val errorCode: Int) {
}
}
}

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.data.api;
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-26
*/
package pl.szczodrzynski.edziennik.api.v2.models;
import android.content.Context;
import android.os.AsyncTask;

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.DataNotifications
import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_NOTIFY_AND_SYNC
import pl.szczodrzynski.edziennik.api.v2.ServerSync
import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback
import pl.szczodrzynski.edziennik.data.api.AppError.*
import pl.szczodrzynski.edziennik.api.v2.models.AppError.*
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer

View File

@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.template.firstlogin.TemplateFirstLogin
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin
import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
@ -70,7 +71,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
}
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}

View File

@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin
import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi
import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
@ -76,7 +77,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
}
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}

View File

@ -53,10 +53,11 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo
val eventDate = Date.fromY_m_d(event.getString("DataTekst") ?: return@forEach)
val subjectId = event.getLong("IdPrzedmiot") ?: -1
val teacherId = event.getLong("IdPracownik") ?: -1
val startTime = data.lessonList.singleOrNull {
it.weekDay == eventDate.weekDay && it.subjectId == subjectId
}?.startTime
val topic = event.getString("Opis") ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
val type = when (isHomework) {
true -> Event.TYPE_HOMEWORK
else -> when (event.getBoolean("Rodzaj")) {

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config
interface AbstractConfig {
fun set(key: String, value: String?)
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.ConfigMigration
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
companion object {
const val DATA_VERSION = 2
}
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
val values: HashMap<String, String?> = hashMapOf()
val ui by lazy { ConfigUI(this) }
val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }
private var mDataVersion: Int? = null
var dataVersion: Int
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
private var mAppVersion: Int? = null
var appVersion: Int
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
set(value) { set("appVersion", value); mAppVersion = value }
private var mLoginFinished: Boolean? = null
var loginFinished: Boolean
get() { mLoginFinished = mLoginFinished ?: values.get("loginFinished", false); return mLoginFinished ?: false }
set(value) { set("loginFinished", value); mLoginFinished = value }
private var mDevModePassword: String? = null
var devModePassword: String?
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }
set(value) { set("devModePassword", value); mDevModePassword = value }
private var mAppInstalledTime: Long? = null
var appInstalledTime: Long
get() { mAppInstalledTime = mAppInstalledTime ?: values.get("appInstalledTime", 0L); return mAppInstalledTime ?: 0L }
set(value) { set("appInstalledTime", value); mAppInstalledTime = value }
private var mAppRateSnackbarTime: Long? = null
var appRateSnackbarTime: Long
get() { mAppRateSnackbarTime = mAppRateSnackbarTime ?: values.get("appRateSnackbarTime", 0L); return mAppRateSnackbarTime ?: 0L }
set(value) { set("appRateSnackbarTime", value); mAppRateSnackbarTime = value }
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
init {
rawEntries.toHashMap(-1, values)
}
fun migrate(app: App) {
if (dataVersion < DATA_VERSION)
ConfigMigration(app, this)
}
fun getFor(profileId: Int): ProfileConfig {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries)
}
fun setProfile(profileId: Int) {
}
override fun set(key: String, value: String?) {
values[key] = value
launch {
db.configDao().add(ConfigEntry(-1, key, value))
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
class ConfigGrades(private val config: Config) {
companion object {
const val ORDER_BY_DATE_DESC = 0
const val ORDER_BY_SUBJECT_ASC = 1
const val ORDER_BY_DATE_ASC = 2
const val ORDER_BY_SUBJECT_DESC = 3
}
private var mOrderBy: Int? = null
var orderBy: Int
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: 0 }
set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set
class ConfigSync(private val config: Config) {
private var mSyncEnabled: Boolean? = null
var enabled: Boolean
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
set(value) { config.set("syncEnabled", value); mSyncEnabled = value }
private var mSyncOnlyWifi: Boolean? = null
var onlyWifi: Boolean
get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates }
set(value) { config.set("syncOnlyWifi", value); mSyncOnlyWifi = value }
private var mSyncInterval: Int? = null
var interval: Int
get() { mSyncInterval = mSyncInterval ?: config.values.get("syncInterval", 60*60); return mSyncInterval ?: 60*60 }
set(value) { config.set("syncInterval", value); mSyncInterval = value }
private var mNotifyAboutUpdates: Boolean? = null
var notifyAboutUpdates: Boolean
get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true }
set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value }
/* ____ _ _ _
/ __ \ (_) | | | |
| | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___
| | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __|
| |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \
\___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/
private var mQuietHoursStart: Long? = null
var quietHoursStart: Long
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", 0L); return mQuietHoursStart ?: 0L }
set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value }
private var mQuietHoursEnd: Long? = null
var quietHoursEnd: Long
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", 0L); return mQuietHoursEnd ?: 0L }
set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value }
private var mQuietDuringLessons: Boolean? = null
var quietDuringLessons: Boolean
get() { mQuietDuringLessons = mQuietDuringLessons ?: config.values.get("quietDuringLessons", false); return mQuietDuringLessons ?: false }
set(value) { config.set("quietDuringLessons", value); mQuietDuringLessons = value }
/* ______ _____ __ __ _______ _
| ____/ ____| \/ | |__ __| | |
| |__ | | | \ / | | | ___ | | _____ _ __ ___
| __|| | | |\/| | | |/ _ \| |/ / _ \ '_ \/ __|
| | | |____| | | | | | (_) | < __/ | | \__ \
|_| \_____|_| |_| |_|\___/|_|\_\___|_| |_|__*/
private var mTokenApp: String? = null
var tokenApp: String?
get() { mTokenApp = mTokenApp ?: config.values.get("tokenApp", null as String?); return mTokenApp }
set(value) { config.set("tokenApp", value); mTokenApp = value }
private var mTokenMobidziennik: String? = null
var tokenMobidziennik: String?
get() { mTokenMobidziennik = mTokenMobidziennik ?: config.values.get("tokenMobidziennik", null as String?); return mTokenMobidziennik }
set(value) { config.set("tokenMobidziennik", value); mTokenMobidziennik = value }
private var mTokenLibrus: String? = null
var tokenLibrus: String?
get() { mTokenLibrus = mTokenLibrus ?: config.values.get("tokenLibrus", null as String?); return mTokenLibrus }
set(value) { config.set("tokenLibrus", value); mTokenLibrus = value }
private var mTokenVulcan: String? = null
var tokenVulcan: String?
get() { mTokenVulcan = mTokenVulcan ?: config.values.get("tokenVulcan", null as String?); return mTokenVulcan }
set(value) { config.set("tokenVulcan", value); mTokenVulcan = value }
private var mTokenMobidziennikList: List<Int>? = null
var tokenMobidziennikList: List<Int>
get() { mTokenMobidziennikList = mTokenMobidziennikList ?: config.values.getIntList("tokenMobidziennikList", listOf()); return mTokenMobidziennikList ?: listOf() }
set(value) { config.set("tokenMobidziennikList", value); mTokenMobidziennikList = value }
private var mTokenLibrusList: List<Int>? = null
var tokenLibrusList: List<Int>
get() { mTokenLibrusList = mTokenLibrusList ?: config.values.getIntList("tokenLibrusList", listOf()); return mTokenLibrusList ?: listOf() }
set(value) { config.set("tokenLibrusList", value); mTokenLibrusList = value }
private var mTokenVulcanList: List<Int>? = null
var tokenVulcanList: List<Int>
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.utils.models.Time
class ConfigTimetable(private val config: Config) {
private var mBellSyncMultiplier: Int? = null
var bellSyncMultiplier: Int
get() { mBellSyncMultiplier = mBellSyncMultiplier ?: config.values.get("bellSyncMultiplier", 0); return mBellSyncMultiplier ?: 0 }
set(value) { config.set("bellSyncMultiplier", value); mBellSyncMultiplier = value }
private var mBellSyncDiff: Time? = null
var bellSyncDiff: Time?
get() { mBellSyncDiff = mBellSyncDiff ?: config.values.get("bellSyncDiff", null as Time?); return mBellSyncDiff }
set(value) { config.set("bellSyncDiff", value); mBellSyncDiff = value }
private var mCountInSeconds: Boolean? = null
var countInSeconds: Boolean
get() { mCountInSeconds = mCountInSeconds ?: config.values.get("countInSeconds", false); return mCountInSeconds ?: false }
set(value) { config.set("countInSeconds", value); mCountInSeconds = value }
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
class ConfigUI(private val config: Config) {
private var mTheme: Int? = null
var theme: Int
get() { mTheme = mTheme ?: config.values.get("theme", 1); return mTheme ?: 1 }
set(value) { config.set("theme", value); mTheme = value }
private var mLanguage: String? = null
var language: String?
get() { mLanguage = mLanguage ?: config.values.get("language", null as String?); return mLanguage }
set(value) { config.set("language", value); mLanguage = value }
private var mHeaderBackground: String? = null
var headerBackground: String?
get() { mHeaderBackground = mHeaderBackground ?: config.values.get("headerBackground", null as String?); return mHeaderBackground }
set(value) { config.set("headerBg", value); mHeaderBackground = value }
private var mAppBackground: String? = null
var appBackground: String?
get() { mAppBackground = mAppBackground ?: config.values.get("appBackground", null as String?); return mAppBackground }
set(value) { config.set("appBg", value); mAppBackground = value }
private var mMiniMenuVisible: Boolean? = null
var miniMenuVisible: Boolean
get() { mMiniMenuVisible = mMiniMenuVisible ?: config.values.get("miniMenuVisible", false); return mMiniMenuVisible ?: false }
set(value) { config.set("miniMenuVisible", value); mMiniMenuVisible = value }
private var mMiniMenuButtons: List<Int>? = null
var miniMenuButtons: List<Int>
get() { mMiniMenuButtons = mMiniMenuButtons ?: config.values.getIntList("miniMenuButtons", listOf()); return mMiniMenuButtons ?: listOf() }
set(value) { config.set("miniMenuButtons", value); mMiniMenuButtons = value }
private var mAgendaViewType: Int? = null
var agendaViewType: Int
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: 0 }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
private var mHomeCards: List<HomeCardModel>? = null
var homeCards: List<HomeCardModel>
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
set(value) { config.set("homeCards", value); mHomeCards = value }
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext
class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
companion object {
const val DATA_VERSION = 1
}
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
val values: HashMap<String, String?> = hashMapOf()
val grades by lazy { ProfileConfigGrades(this) }
/*
val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }*/
private var mDataVersion: Int? = null
var dataVersion: Int
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
init {
rawEntries.toHashMap(profileId, values)
/*if (dataVersion < DATA_VERSION)
ProfileConfigMigration(this)*/
}
override fun set(key: String, value: String?) {
values[key] = value
launch {
db.configDao().add(ConfigEntry(profileId, key, value))
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.YEAR_ALL_GRADES
class ProfileConfigGrades(private val config: ProfileConfig) {
private var mColorMode: Int? = null
var colorMode: Int
get() { mColorMode = mColorMode ?: config.values.get("gradesColorMode", COLOR_MODE_WEIGHTED); return mColorMode ?: COLOR_MODE_WEIGHTED }
set(value) { config.set("gradesColorMode", value); mColorMode = value }
private var mYearAverageMode: Int? = null
var yearAverageMode: Int
get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
private var mCountZeroToAvg: Boolean? = null
var countZeroToAvg: Boolean
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config.db
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface ConfigDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun add(entry: ConfigEntry)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun addAll(list: List<ConfigEntry>)
@Query("SELECT * FROM config WHERE profileId = -1")
fun getAllNow(): List<ConfigEntry>
@Query("SELECT * FROM config WHERE profileId = :profileId")
fun getAllNow(profileId: Int): List<ConfigEntry>
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config.db
import androidx.room.Entity
@Entity(tableName = "config", primaryKeys = ["profileId", "key"])
data class ConfigEntry(
val profileId: Int = -1,
val key: String,
val value: String?
)

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config.utils
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.config.AbstractConfig
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
private val gson = Gson()
fun AbstractConfig.set(key: String, value: Int) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Boolean) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Long) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Float) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Date?) {
set(key, value?.stringY_m_d)
}
fun AbstractConfig.set(key: String, value: Time?) {
set(key, value?.stringValue)
}
fun AbstractConfig.set(key: String, value: JsonElement?) {
set(key, value?.toString())
}
fun AbstractConfig.set(key: String, value: List<Any>?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.setStringList(key: String, value: List<String>?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.setIntList(key: String, value: List<Int>?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.setLongList(key: String, value: List<Long>?) {
set(key, value?.let { gson.toJson(it) })
}
fun HashMap<String, String?>.get(key: String, default: String?): String? {
return this[key] ?: default
}
fun HashMap<String, String?>.get(key: String, default: Boolean): Boolean {
return this[key]?.toBoolean() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Int): Int {
return this[key]?.toIntOrNull() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Long): Long {
return this[key]?.toLongOrNull() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Float): Float {
return this[key]?.toFloatOrNull() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Date?): Date? {
return this[key]?.let { Date.fromY_m_d(it) } ?: default
}
fun HashMap<String, String?>.get(key: String, default: Time?): Time? {
return this[key]?.let { Time.fromHms(it) } ?: default
}
fun HashMap<String, String?>.get(key: String, default: JsonObject?): JsonObject? {
return this[key]?.let { JsonParser().parse(it)?.asJsonObject } ?: default
}
fun HashMap<String, String?>.get(key: String, default: JsonArray?): JsonArray? {
return this[key]?.let { JsonParser().parse(it)?.asJsonArray } ?: default
}
/* !!! cannot use mutable list here - modifying it will not update the DB */
fun <T> HashMap<String, String?>.get(key: String, default: List<T>?, classOfT: Class<T>): List<T>? {
return this[key]?.let { ConfigGsonUtils().deserializeList<T>(gson, it, classOfT) } ?: default
}
fun HashMap<String, String?>.getStringList(key: String, default: List<String>?): List<String>? {
return this[key]?.let { gson.fromJson<List<String>>(it, object: TypeToken<List<String>>(){}.type) } ?: default
}
fun HashMap<String, String?>.getIntList(key: String, default: List<Int>?): List<Int>? {
return this[key]?.let { gson.fromJson<List<Int>>(it, object: TypeToken<List<Int>>(){}.type) } ?: default
}
fun HashMap<String, String?>.getLongList(key: String, default: List<Long>?): List<Long>? {
return this[key]?.let { gson.fromJson<List<Long>>(it, object: TypeToken<List<Long>>(){}.type) } ?: default
}
fun List<ConfigEntry>.toHashMap(profileId: Int, map: HashMap<String, String?>) {
map.clear()
forEach {
if (it.profileId == profileId)
map[it.key] = it.value
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-2.
*/
package pl.szczodrzynski.edziennik.config.utils
import com.google.gson.Gson
import com.google.gson.JsonParser
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
import pl.szczodrzynski.edziennik.utils.models.Time
class ConfigGsonUtils {
fun <T> deserializeList(gson: Gson, str: String?, classOfT: Class<T>): List<T> {
val json = JsonParser().parse(str)
val list: MutableList<T> = mutableListOf()
if (!json.isJsonArray)
return list
json.asJsonArray.forEach { e ->
when (classOfT) {
String::class.java -> {
list += e.asString as T
}
HomeCardModel::class.java -> {
val o = e.asJsonObject
list += HomeCardModel(
o.getInt("profileId", 0),
o.getInt("cardId", 0)
) as T
}
Time::class.java -> {
val o = e.asJsonObject
list += Time(
o.getInt("hour", 0),
o.getInt("minute", 0),
o.getInt("second", 0)
) as T
}
}
}
return list
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config.utils
import android.content.Context
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_VULCAN
import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.utils.models.Time
class ConfigMigration(app: App, config: Config) {
init { config.apply {
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
val s = "app.appConfig"
if (dataVersion < 1) {
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true
sync.interval = p.getString("$s.registerSyncEnabled", null)?.toIntOrNull() ?: 3600
val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str ->
str.replace("[\\[\\]]*".toRegex(), "")
.split(",\\s?".toRegex())
.mapNotNull { it.toIntOrNull() }
}
ui.miniMenuButtons = oldButtons ?: listOf(
MainActivity.DRAWER_ITEM_HOME,
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES,
MainActivity.DRAWER_ITEM_MESSAGES,
MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_SETTINGS
)
dataVersion = 1
}
if (dataVersion < 2) {
devModePassword = p.getString("$s.devModePassword", null).fix()
sync.tokenApp = p.getString("$s.fcmToken", null).fix()
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0
sync.quietHoursEnd = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
ui.appBackground = p.getString("$s.appBackground", null).fix()
ui.language = p.getString("$s.language", null).fix()
appVersion = p.getString("$s.lastAppVersion", null)?.toIntOrNull() ?: BuildConfig.VERSION_CODE
appInstalledTime = p.getString("$s.appInstalledTime", null)?.toLongOrNull() ?: 0
grades.orderBy = p.getString("$s.gradesOrderBy", null)?.toIntOrNull() ?: 0
sync.quietDuringLessons = p.getString("$s.quietDuringLessons", null)?.toBoolean() ?: false
ui.miniMenuVisible = p.getString("$s.miniDrawerVisible", null)?.toBoolean() ?: false
loginFinished = p.getString("$s.loginFinished", null)?.toBoolean() ?: false
sync.onlyWifi = p.getString("$s.registerSyncOnlyWifi", null)?.toBoolean() ?: false
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
sync.tokenMobidziennikList = listOf()
sync.tokenVulcanList = listOf()
sync.tokenLibrusList = listOf()
val tokens = p.getString("$s.fcmTokens", null)?.let { Gson().fromJson<Map<Int, Pair<String, List<Int>>>>(it, object: TypeToken<Map<Int, Pair<String, List<Int>>>>(){}.type) }
tokens?.forEach {
val token = it.value.first
when (it.key) {
LOGIN_TYPE_MOBIDZIENNIK -> sync.tokenMobidziennik = token
LOGIN_TYPE_VULCAN -> sync.tokenVulcan = token
LOGIN_TYPE_LIBRUS -> sync.tokenLibrus = token
}
}
dataVersion = 2
}
}}
private fun String?.fix(): String? {
return this?.replace("\"", "")?.let { if (it == "null") null else it }
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-1.
*/
package pl.szczodrzynski.edziennik.config.utils
import android.content.Context
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.config.Config
class ProfileConfigMigration(app: App, config: Config) {
init { config.apply {
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
val s = "app.appConfig"
if (dataVersion < 1) {
//dataVersion = 1
}
if (dataVersion < 2) {
//gradesColorMode do profilu !
//agendaViewType do profilu !
// app.appConfig.dontCountZeroToAverage do profilu !
}
}}
}

View File

@ -10,6 +10,8 @@ import androidx.room.TypeConverters;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import pl.szczodrzynski.edziennik.config.db.ConfigDao;
import pl.szczodrzynski.edziennik.config.db.ConfigEntry;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterDateInt;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterJsonObject;
@ -105,7 +107,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date;
NoticeType.class,
AttendanceType.class,
pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class,
Metadata.class}, version = 64)
ConfigEntry.class,
Metadata.class}, version = 66)
@TypeConverters({
ConverterTime.class,
ConverterDate.class,
@ -144,6 +147,7 @@ public abstract class AppDb extends RoomDatabase {
public abstract NoticeTypeDao noticeTypeDao();
public abstract AttendanceTypeDao attendanceTypeDao();
public abstract TimetableDao timetableDao();
public abstract ConfigDao configDao();
public abstract MetadataDao metadataDao();
private static volatile AppDb INSTANCE;
@ -763,6 +767,27 @@ public abstract class AppDb extends RoomDatabase {
database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);");
}
};
private static final Migration MIGRATION_64_65 = new Migration(64, 65) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE config (" +
"profileId INTEGER NOT NULL DEFAULT -1," +
"`key` TEXT NOT NULL," +
"value TEXT NOT NULL," +
"PRIMARY KEY(profileId, `key`));");
}
};
private static final Migration MIGRATION_65_66 = new Migration(65, 66) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("DROP TABLE config;");
database.execSQL("CREATE TABLE config (" +
"profileId INTEGER NOT NULL DEFAULT -1," +
"`key` TEXT NOT NULL," +
"value TEXT," +
"PRIMARY KEY(profileId, `key`));");
}
};
public static AppDb getDatabase(final Context context) {
@ -824,7 +849,9 @@ public abstract class AppDb extends RoomDatabase {
MIGRATION_60_61,
MIGRATION_61_62,
MIGRATION_62_63,
MIGRATION_63_64
MIGRATION_63_64,
MIGRATION_64_65,
MIGRATION_65_66
)
.allowMainThreadQueries()
//.fallbackToDestructiveMigration()

View File

@ -1,18 +1,16 @@
package pl.szczodrzynski.edziennik.data.db.modules.grades;
import android.util.LongSparseArray;
import androidx.lifecycle.LiveData;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.RawQuery;
import androidx.room.Transaction;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List;
@ -133,4 +131,8 @@ public abstract class GradeDao {
}
}
}
public LiveData<List<GradeFull>> getAllFromDate(int profileId, int semester, long date) {
return getAllWhere(profileId, "gradeSemester = " + semester + " AND addedDate > " + date);
}
}

View File

@ -9,7 +9,6 @@ import androidx.room.Transaction;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice;
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement;
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance;
import pl.szczodrzynski.edziennik.data.db.modules.events.Event;
@ -17,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message;
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice;
import pl.szczodrzynski.edziennik.utils.models.UnreadCounter;
import static pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.TYPE_ANNOUNCEMENT;
@ -85,6 +85,11 @@ public abstract class MetadataDao {
updateSeen(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).changeId, seen);
}
}
if (o instanceof pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) {
if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), seen);
}
}
if (o instanceof Announcement) {
if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen);
@ -129,6 +134,11 @@ public abstract class MetadataDao {
updateNotified(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).changeId, notified);
}
}
if (o instanceof pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) {
if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), notified);
}
}
if (o instanceof Announcement) {
if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, notified);

View File

@ -1,5 +1,6 @@
package pl.szczodrzynski.edziennik.data.db.modules.profiles;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
@ -24,6 +25,7 @@ public interface ProfileDao {
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId")
LiveData<ProfileFull> getById(int profileId);
@Nullable
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId")
ProfileFull getFullByIdNow(int profileId);

View File

@ -2,6 +2,7 @@ package pl.szczodrzynski.edziennik.data.db.modules.profiles
import android.content.Context
import androidx.room.ColumnInfo
import androidx.room.Ignore
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
@ -123,6 +124,9 @@ class ProfileFull : Profile {
constructor(context: Context) : super(context)
@Ignore
constructor(id: Int, name: String, subname: String, loginStoreId: Int) : super(id, name, subname, loginStoreId)
fun canChangeLoginPassword(): Boolean {
return loginStoreType == LOGIN_TYPE_MOBIDZIENNIK || loginStoreType == LOGIN_TYPE_LIBRUS || loginStoreType == LOGIN_TYPE_IUCZNIOWIE
}

View File

@ -84,24 +84,44 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
})
}
val changeSubjectName: String
get() {
val first = when (type) {
TYPE_CHANGE, TYPE_CANCELLED, TYPE_SHIFTED_SOURCE -> oldSubjectName
else -> subjectName
}
val second = when (type) {
TYPE_CHANGE -> subjectName
else -> null
}
return when (second) {
null -> first ?: ""
else -> "$first -> $second"
}
private fun changeText(actual: String?, old: String?): String {
val first = when (type) {
TYPE_CHANGE, TYPE_CANCELLED, TYPE_SHIFTED_SOURCE -> old
else -> actual
}
val second = when (type) {
TYPE_CHANGE -> actual
else -> null
}
return when (second) {
null -> first.orEmpty()
first -> second
else -> if (first != null) "$first -> $second" else second.orEmpty()
}
}
val changeSubjectName: String
get() = changeText(subjectName, oldSubjectName)
val isSubjectNameChanged: Boolean
get() = type == TYPE_CHANGE && subjectName != oldSubjectName
val changeTeacherName: String
get() = changeText(teacherName, oldTeacherName)
val isTeacherNameChanged: Boolean
get() = type == TYPE_CHANGE && teacherName != oldTeacherName
val changeClassroom: String
get() = changeText(classroom, oldClassroom)
val isClassroomChanged: Boolean
get() = type == TYPE_CHANGE && classroom != oldClassroom
// metadata
var seen: Boolean = false
var notified: Boolean = false

View File

@ -90,12 +90,12 @@ public class ServerRequest {
.addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID))
.addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.addParameter("device_os_version", Build.VERSION.RELEASE)
.addParameter("fcm_token", app.appConfig.fcmToken)
.addParameter("fcm_token", app.config.getSync().getTokenApp())
.addParameter("signature", sign(app.signature, timestamp))
.addParameter("signature_timestamp", timestamp)
.addParameter("package_name", "pl.szczodrzynski.edziennik")
.addParameter("source", source)
.addParameter("update_frequency", app.appConfig.registerSyncEnabled ? app.appConfig.registerSyncInterval : -1)
.addParameter("update_frequency", app.config.getSync().getEnabled() ? app.config.getSync().getInterval() : -1)
.post()
.callback(new JsonCallbackHandler() {
@Override
@ -127,12 +127,12 @@ public class ServerRequest {
.addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID))
.addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.addParameter("device_os_version", Build.VERSION.RELEASE)
.addParameter("fcm_token", app.appConfig.fcmToken)
.addParameter("fcm_token", app.config.getSync().getTokenApp())
.addParameter("signature", sign(app.signature, timestamp))
.addParameter("signature_timestamp", timestamp)
.addParameter("package_name", "pl.szczodrzynski.edziennik")
.addParameter("source", source)
.addParameter("update_frequency", app.appConfig.registerSyncEnabled ? app.appConfig.registerSyncInterval : -1)
.addParameter("update_frequency", app.config.getSync().getEnabled() ? app.config.getSync().getInterval() : -1)
.post()
.build()
.execute();

View File

@ -47,9 +47,8 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
Log.d(TAG, "New token: "+s);
App app = (App)getApplicationContext();
if (app.appConfig.fcmToken == null || !app.appConfig.fcmToken.equals(s)) {
app.appConfig.fcmToken = s;
app.saveConfig();
if (app.config.getSync().getTokenApp() == null || !app.config.getSync().getTokenApp().equals(s)) {
app.config.getSync().setTokenApp(s);
}
}
@ -198,8 +197,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
else {
feedbackMessage.sentTime = Long.parseLong(remoteMessage.getData().get("sent_time"));
if (feedbackMessage.text.startsWith("devmode")) {
app.appConfig.devModePassword = feedbackMessage.text.replace("devmode", "");
app.saveConfig("devModePassword");
app.config.setDevModePassword(feedbackMessage.text.replace("devmode", ""));
app.checkDevModePassword();
feedbackMessage.text = "devmode "+(App.devMode ? "allowed" : "disallowed");
}

View File

@ -57,16 +57,16 @@ class SyncWorker(val context: Context, val params: WorkerParameters) : Worker(co
/**
* Cancel any existing sync jobs and schedule a new one.
*
* If [registerSyncEnabled] is not true, just cancel every job.
* If [ConfigSync.enabled] is not true, just cancel every job.
*/
fun rescheduleNext(app: App) {
cancelNext(app)
val enableSync = app.appConfig.registerSyncEnabled
val enableSync = app.config.sync.enabled
if (!enableSync) {
return
}
val onlyWifi = app.appConfig.registerSyncOnlyWifi
val syncInterval = app.appConfig.registerSyncInterval.toLong()
val onlyWifi = app.config.sync.onlyWifi
val syncInterval = app.config.sync.interval.toLong()
val syncAt = System.currentTimeMillis() + syncInterval*1000
d(TAG, "Scheduling work at ${syncAt.formatDate()}")

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-30
*/
package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
import pl.szczodrzynski.edziennik.databinding.RowDialogEventListItemBinding
import pl.szczodrzynski.edziennik.utils.Utils.bs
import pl.szczodrzynski.edziennik.utils.models.Date
class EventListAdapter(
val context: Context,
val parentDialog: EventListDialog
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() {
private val app by lazy { context.applicationContext as App }
val eventList = mutableListOf<EventFull>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view: RowDialogEventListItemBinding = DataBindingUtil.inflate(inflater, R.layout.row_dialog_event_list_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val event = eventList[position]
holder.apply {
b.eventListItemRoot.background.colorFilter = when (event.type) {
Event.TYPE_HOMEWORK -> PorterDuffColorFilter((0xffffffff).toInt(), PorterDuff.Mode.CLEAR)
else -> PorterDuffColorFilter(event.color, PorterDuff.Mode.MULTIPLY)
}
b.eventListItemStartTime.text = if (event.startTime == null) app.getString(R.string.event_all_day) else event.startTime?.stringHM
b.eventListItemTeamName.text = bs(event.teamName)
b.eventListItemTeacherName.text = app.getString(R.string.concat_2_strings, bs(null, event.teacherFullName, "\n"), bs(event.subjectLongName))
b.eventListItemAddedDate.text = Date.fromMillis(event.addedDate).formattedStringShort
b.eventListItemType.text = event.typeName
b.eventListItemTopic.text = event.topic
b.eventListItemHomework.visibility = if (event.type == Event.TYPE_HOMEWORK) View.VISIBLE else View.GONE
b.eventListItemSharedBy.text = app.getString(R.string.event_shared_by_format, if (event.sharedBy == "self") app.getString(R.string.event_shared_by_self) else event.sharedByName)
b.eventListItemSharedBy.visibility = if (event.sharedByName.isNullOrBlank()) View.GONE else View.VISIBLE
b.eventListItemEdit.visibility = if (event.addedManually) View.VISIBLE else View.GONE
b.eventListItemEdit.setOnClickListener {
parentDialog.dismiss()
EventManualV2Dialog(
context as MainActivity,
event.profileId,
editingEvent = event,
onShowListener = parentDialog.onShowListener,
onDismissListener = parentDialog.onDismissListener
)
}
}
}
override fun getItemCount(): Int = eventList.size
class ViewHolder(val b: RowDialogEventListItemBinding) : RecyclerView.ViewHolder(b.root)
}

View File

@ -3,14 +3,15 @@ package pl.szczodrzynski.edziennik.ui.dialogs.event;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.mikepenz.iconics.view.IconicsImageView;
import com.mikepenz.iconics.view.IconicsTextView;
@ -20,21 +21,21 @@ import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.events.Event;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
private static final String TAG = "EventListAdapter";
public class EventListAdapterOld extends RecyclerView.Adapter<EventListAdapterOld.ViewHolder> {
private static final String TAG = "EventListAdapterOld";
private Context context;
private List<EventFull> examList;
private EventListDialog parentDialog;
private EventListDialogOld parentDialog;
//getting the context and product list with constructor
public EventListAdapter(Context mCtx, List<EventFull> examList, EventListDialog parentDialog) {
public EventListAdapterOld(Context mCtx, List<EventFull> examList, EventListDialogOld parentDialog) {
this.context = mCtx;
this.examList = examList;
this.parentDialog = parentDialog;
@ -42,15 +43,15 @@ public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.View
@NonNull
@Override
public EventListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public EventListAdapterOld.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.row_dialog_event_list_item, parent, false);
return new EventListAdapter.ViewHolder(view);
return new EventListAdapterOld.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull EventListAdapter.ViewHolder holder, int position) {
public void onBindViewHolder(@NonNull EventListAdapterOld.ViewHolder holder, int position) {
App app = (App) context.getApplicationContext();
EventFull event = examList.get(position);

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-30
*/
package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.graphics.Typeface
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventListBinding
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class EventListDialog(
val activity: AppCompatActivity,
val profileId: Int,
val date: Date,
val time: Time? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
const val TAG = "EventListDialog"
}
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val app by lazy { activity.application as App }
private lateinit var b: DialogEventListBinding
private lateinit var dialog: AlertDialog
private lateinit var adapter: EventListAdapter
private var lesson: LessonFull? = null
init {
run {
if (activity.isFinishing)
return@run
job = Job()
onShowListener?.invoke(TAG)
b = DialogEventListBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(date.formattedString + (time?.let { ", " + it.stringHM } ?: ""))
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ -> dialog.dismiss() }
.setNeutralButton(R.string.add) { _, _ ->
EventManualV2Dialog(
activity,
lesson?.profileId ?: profileId,
lesson,
date,
time,
onShowListener = onShowListener,
onDismissListener = onDismissListener
)
}
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
app.db.timetableDao().getForDate(profileId, date).observe(activity, Observer { lessons ->
lesson = lessons.firstOrNull { it.displayStartTime == time }
update()
})
}
}
fun dismiss() = dialog.dismiss()
private fun update() {
b.eventListLessonDetails.visibility = if (lesson == null) View.GONE else View.VISIBLE
if (lesson != null) {
dialog.setTitle(if (time == null) date.formattedString else (lesson?.displaySubjectName
?: date.formattedString) + ", " + time.stringHM)
b.eventListLessonDate.text = app.getString(R.string.date_time_format, date.formattedString, "")
if (lesson?.type == Lesson.TYPE_CANCELLED) {
b.eventListLessonChange.text = app.getString(R.string.lesson_cancelled)
b.eventListLessonChange.setTypeface(null, Typeface.BOLD_ITALIC)
b.eventListTeacher.visibility = View.GONE
b.eventListClassroom.visibility = View.GONE
} else {
b.eventListLessonChange.text = lesson?.changeSubjectName
b.eventListLessonChange.setTypeface(null, Typeface.ITALIC)
b.eventListLessonChange.visibility = if (lesson?.isSubjectNameChanged == true) View.VISIBLE else View.GONE
b.eventListTeacher.text = lesson?.changeTeacherName
b.eventListTeacher.setTypeface(null, if (lesson?.isTeacherNameChanged == true) Typeface.ITALIC else Typeface.NORMAL)
b.eventListClassroom.text = lesson?.changeClassroom
b.eventListClassroom.setTypeface(null, if (lesson?.isClassroomChanged == true) Typeface.ITALIC else Typeface.NORMAL)
}
}
b.eventListView.apply {
setHasFixedSize(false)
isNestedScrollingEnabled = true
layoutManager = LinearLayoutManager(activity)
}
adapter = EventListAdapter(activity, this@EventListDialog)
b.eventListView.adapter = adapter
app.db.eventDao().getAllByDateTime(profileId, date, time).observe(activity, Observer { events ->
if (events.isNullOrEmpty()) {
b.eventListView.visibility = View.GONE
b.textNoEvents.visibility = View.VISIBLE
} else {
adapter.run {
eventList.apply {
clear()
addAll(events)
}
notifyDataSetChanged()
}
}
})
}
}

View File

@ -8,14 +8,14 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.afollestad.materialdialogs.MaterialDialog;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange;
@ -26,16 +26,16 @@ import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
public class EventListDialog {
public class EventListDialogOld {
private App app;
private Context context;
private int profileId;
public EventListDialog(Context context) {
public EventListDialogOld(Context context) {
this.context = context;
this.profileId = App.profileId;
}
public EventListDialog(Context context, int profileId) {
public EventListDialogOld(Context context, int profileId) {
this.context = context;
this.profileId = profileId;
}
@ -47,7 +47,7 @@ public class EventListDialog {
public boolean callDismissListener = true;
private LessonFull lesson;
public EventListDialog withDismissListener(DialogInterface.OnDismissListener dismissListener) {
public EventListDialogOld withDismissListener(DialogInterface.OnDismissListener dismissListener) {
this.dismissListener = dismissListener;
return this;
}
@ -222,7 +222,7 @@ public class EventListDialog {
dialogView.findViewById(R.id.textNoEvents).setVisibility(View.VISIBLE);
}
else {
EventListAdapter adapter = new EventListAdapter(context, events, this);
EventListAdapterOld adapter = new EventListAdapterOld(context, events, this);
examsView.setAdapter(adapter);
}
});

View File

@ -7,7 +7,9 @@ package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import com.google.android.material.datepicker.MaterialDatePicker
@ -18,7 +20,9 @@ import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
@ -68,25 +72,27 @@ class EventManualV2Dialog(
.setTitle(R.string.dialog_event_manual_title)
.setView(b.root)
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.setPositiveButton(R.string.save) { _, _ -> saveEvent() }
.setPositiveButton(R.string.save, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
.create()
.apply {
setOnShowListener { dialog ->
val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE)
positiveButton.setOnClickListener {
saveEvent()
}
}
show()
}
event = editingEvent?.clone() ?: Event().also { event ->
event.profileId = profileId
/*defaultDate?.let {
event.eventDate = it
b.date = it
}
defaultTime?.let {
event.startTime = it
b.time = it
}
defaultType?.let {
event.type = it
}*/
}
b.shareSwitch.isChecked = event.sharedBy != null
}
@ -180,6 +186,8 @@ class EventManualV2Dialog(
b.teamDropdown.select(it.teamId)
b.subjectDropdown.select(it.subjectId)
b.teacherDropdown.select(it.teacherId)
b.topic.setText(it.topic)
b.shareSwitch.isChecked = true
b.typeDropdown.select(it.type)?.let { item ->
customColor = (item.tag as EventType).color
}
@ -288,6 +296,16 @@ class EventManualV2Dialog(
val dates = deferred.await()
b.dateDropdown.clear().append(dates)
defaultDate?.let {
event.eventDate = it
if (b.dateDropdown.select(it) == null)
b.dateDropdown.select(TextInputDropDown.Item(
it.value.toLong(),
it.formattedString,
tag = it
))
}
editingEvent?.eventDate?.let {
b.dateDropdown.select(TextInputDropDown.Item(
it.value.toLong(),
@ -425,6 +443,16 @@ class EventManualV2Dialog(
b.teacherDropdown.deselect()
}
else {
defaultTime?.let {
event.startTime = it
if (b.timeDropdown.select(it) == null)
b.timeDropdown.select(TextInputDropDown.Item(
it.value.toLong(),
it.stringHM,
tag = it
))
}
editingEvent?.let {
b.timeDropdown.select(it.startTime?.value?.toLong())
}
@ -442,17 +470,16 @@ class EventManualV2Dialog(
// attach a listener to time dropdown
b.timeDropdown.setOnChangeListener { item ->
when {
when (item.id) {
// no lessons this day
item.id == -2L -> {
-2L -> {
b.timeDropdown.deselect()
return@setOnChangeListener false
}
// custom start hour
item.id == -1L -> {
return@setOnChangeListener false
}
// custom start hour
-1L -> return@setOnChangeListener false
// selected a specific lesson
else -> {
if (item.tag is LessonFull) {
@ -461,7 +488,7 @@ class EventManualV2Dialog(
b.teamDropdown.deselect()
b.subjectDropdown.deselect()
b.teacherDropdown.deselect()
item.tag.displayTeamId?.let {
item.tag.displayTeamId?.let {
b.teamDropdown.select(it)
}
item.tag.displaySubjectId?.let {
@ -479,6 +506,80 @@ class EventManualV2Dialog(
}
private fun saveEvent() {
val date = b.dateDropdown.selected?.tag.instanceOfOrNull<Date>()
val lesson = b.timeDropdown.selected?.tag.instanceOfOrNull<LessonFull>()
val team = b.teamDropdown.selected?.tag.instanceOfOrNull<Team>()
val share = b.shareSwitch.isChecked
val type = b.typeDropdown.selected?.tag.instanceOfOrNull<EventType>()
val topic = b.topic.text?.toString()
val subject = b.subjectDropdown.selected?.tag.instanceOfOrNull<Subject>()
val teacher = b.teacherDropdown.selected?.tag.instanceOfOrNull<Teacher>()
b.teamDropdown.error = null
b.typeDropdown.error = null
b.topic.error = null
var isError = false
if (share && team == null) {
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
isError = true
}
if (type == null) {
b.typeDropdown.error = app.getString(R.string.dialog_event_manual_type_choose)
isError = true
}
if (topic.isNullOrBlank()) {
b.topic.error = app.getString(R.string.dialog_event_manual_topic_choose)
isError = true
}
if (isError) return
val id = System.currentTimeMillis()
val eventObject = Event(
profileId,
id,
date,
lesson?.displayStartTime,
topic,
customColor ?: -1,
type?.id?.toInt() ?: Event.TYPE_DEFAULT,
true,
teacher?.id ?: -1,
subject?.id ?: -1,
team?.id ?: -1
)
val metadataObject = Metadata(
profileId,
when (type?.id?.toInt()) {
Event.TYPE_HOMEWORK -> Metadata.TYPE_HOMEWORK
else -> Metadata.TYPE_EVENT
},
eventObject.id,
true,
true,
System.currentTimeMillis()
)
finishAdding(eventObject, metadataObject)
}
private fun finishAdding(eventObject: Event, metadataObject: Metadata) {
launch {
val deferred = async(Dispatchers.Default) {
app.db.eventDao().add(eventObject)
app.db.metadataDao().add(metadataObject)
}
deferred.await()
}
dialog.dismiss()
Toast.makeText(app, R.string.saved, Toast.LENGTH_SHORT).show()
}
}

View File

@ -42,7 +42,7 @@ class LessonDetailsDialog(
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
}
.setNeutralButton(R.string.add) { dialog, _ ->
.setNeutralButton(R.string.add) { _, _ ->
EventManualV2Dialog(
activity,
lesson.profileId,

View File

@ -84,7 +84,7 @@ public class AgendaFragment extends Fragment {
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false);
// activity, context and profile is valid
viewType = app.profile.getAgendaViewType();
viewType = app.config.getUi().getAgendaViewType();
if (viewType == AGENDA_DEFAULT) {
b_default = DataBindingUtil.inflate(inflater, R.layout.fragment_agenda_default, container, false);
return b_default.getRoot();
@ -128,8 +128,7 @@ public class AgendaFragment extends Fragment {
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT;
app.profile.setAgendaViewType(viewType);
app.profileSaveAsync();
app.config.getUi().setAgendaViewType(viewType);
activity.reloadTarget();
}),
new BottomSheetSeparatorItem(true),
@ -311,7 +310,7 @@ public class AgendaFragment extends Fragment {
int scrolledDate = Date.fromCalendar(calendar).getValue();
if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove(unreadEventDates.indexOf(scrolledDate));
unreadEventDates.remove((Integer) scrolledDate);
}
}
@ -319,9 +318,23 @@ public class AgendaFragment extends Fragment {
public void onEventSelected(CalendarEvent calendarEvent) {
if (calendarEvent instanceof BaseCalendarEvent) {
if (!calendarEvent.isPlaceholder() && !calendarEvent.isAllDay()) {
new EventListDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()), Time.fromMillis(calendarEvent.getStartTime().getTimeInMillis()), true);
// new EventListDialogOld(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()), Time.fromMillis(calendarEvent.getStartTime().getTimeInMillis()), true);
new EventListDialog(
activity,
App.profileId,
Date.fromCalendar(calendarEvent.getInstanceDay()),
Time.fromMillis(calendarEvent.getStartTime().getTimeInMillis()),
null,
null);
} else {
new EventListDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
// new EventListDialogOld(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
new EventListDialog(
activity,
App.profileId,
Date.fromCalendar(calendarEvent.getInstanceDay()),
null,
null,
null);
}
} else if (calendarEvent instanceof LessonChangeEvent) {
new LessonChangeDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
@ -403,10 +416,18 @@ public class AgendaFragment extends Fragment {
int scrolledDate = dayDate.getValue();
if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove(unreadEventDates.indexOf(scrolledDate));
unreadEventDates.remove((Integer) scrolledDate);
}
new EventListDialog(getContext()).show(app, dayDate);
// new EventListDialogOld(getContext()).show(app, dayDate);
new EventListDialog(
activity,
App.profileId,
dayDate,
null,
null,
null
);
});
b_calendar.progressBar.setVisibility(View.GONE);
});

View File

@ -23,6 +23,7 @@ import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.config.ProfileConfigGrades;
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade;
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull;
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject;
@ -32,15 +33,15 @@ import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_DATE_ASC;
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_DATE_DESC;
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_SUBJECT_ASC;
import static pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.TYPE_GRADE;
import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.YEAR_1_AVG_2_AVG;
import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.YEAR_1_AVG_2_SEM;
import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.YEAR_1_SEM_2_AVG;
import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.YEAR_1_SEM_2_SEM;
import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.YEAR_ALL_GRADES;
import static pl.szczodrzynski.edziennik.utils.models.AppConfig.ORDER_BY_DATE_ASC;
import static pl.szczodrzynski.edziennik.utils.models.AppConfig.ORDER_BY_DATE_DESC;
import static pl.szczodrzynski.edziennik.utils.models.AppConfig.ORDER_BY_SUBJECT_ASC;
public class GradesFragment extends Fragment {
@ -135,12 +136,12 @@ public class GradesFragment extends Fragment {
.withIcon(CommunityMaterial.Icon2.cmd_palette_outline)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
ProfileConfigGrades config = app.config.getFor(app.profileId).getGrades();
new MaterialDialog.Builder(activity)
.title(R.string.dialog_grades_color_mode_title)
.items(R.array.dialog_grades_color_modes)
.itemsCallbackSingleChoice(app.profile.getGradeColorMode(), (dialog, view1, which, text) -> {
app.profile.setGradeColorMode(which);
app.profileSaveAsync();
.itemsCallbackSingleChoice(config.getColorMode(), (dialog, view1, which, text) -> {
config.setColorMode(which);
activity.reloadTarget();
return true;
})
@ -154,9 +155,8 @@ public class GradesFragment extends Fragment {
new MaterialDialog.Builder(activity)
.title(R.string.dialog_grades_sort_title)
.items(R.array.dialog_grades_sort_modes)
.itemsCallbackSingleChoice(app.appConfig.gradesOrderBy, (dialog, view1, which, text) -> {
app.appConfig.gradesOrderBy = which;
app.saveConfig("gradesOrderBy");
.itemsCallbackSingleChoice(app.config.getGrades().getOrderBy(), (dialog, view1, which, text) -> {
app.config.getGrades().setOrderBy(which);
activity.reloadTarget();
return true;
})
@ -228,13 +228,13 @@ public class GradesFragment extends Fragment {
long finalExpandSubjectId = expandSubjectId;
String orderBy;
if (app.appConfig.gradesOrderBy == ORDER_BY_SUBJECT_ASC) {
if (app.config.getGrades().getOrderBy() == ORDER_BY_SUBJECT_ASC) {
orderBy = "subjectLongName ASC, addedDate DESC";
}
else if (app.appConfig.gradesOrderBy == ORDER_BY_DATE_DESC) {
else if (app.config.getGrades().getOrderBy() == ORDER_BY_DATE_DESC) {
orderBy = "addedDate DESC";
}
else if (app.appConfig.gradesOrderBy == ORDER_BY_DATE_ASC) {
else if (app.config.getGrades().getOrderBy() == ORDER_BY_DATE_ASC) {
orderBy = "addedDate ASC";
}
else {
@ -247,11 +247,13 @@ public class GradesFragment extends Fragment {
subjectList = new ArrayList<>();
ProfileConfigGrades config = app.config.getFor(app.profileId).getGrades();
// now we have all grades from the newest to the oldest
for (GradeFull grade: grades) {
ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
if (model == null) {
model = new ItemGradesSubjectModel(app.profile, new Subject(App.profileId, grade.subjectId, grade.subjectLongName, grade.subjectShortName), new ArrayList<>(), new ArrayList<>());//ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
model = new ItemGradesSubjectModel(app.profile, new Subject(app.profileId, grade.subjectId, grade.subjectLongName, grade.subjectShortName), new ArrayList<>(), new ArrayList<>());//ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
subjectList.add(model);
if (model.subject != null && model.subject.id == finalExpandSubjectId) {
model.expandView = true;
@ -303,7 +305,7 @@ public class GradesFragment extends Fragment {
// do not show *normal* grades with negative weight - these are historical grades - Iuczniowie
continue;
}
if (app.appConfig.dontCountZeroToAverage && grade.name.equals("0")) {
if (!config.getCountZeroToAvg() && grade.name.equals("0")) {
weight = 0;
}
float valueWeighted = grade.value * weight;

View File

@ -28,15 +28,15 @@ import java.text.DecimalFormat;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.AppDb;
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull;
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@ -219,7 +219,7 @@ public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> i
layoutParams.setMargins(0, 0, _5dp, 0);
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.appConfig.miniDrawerVisible ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/);
int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.config.getUi().getMiniMenuVisible() ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/);
int totalWidthPx = 0;
boolean ellipsized = false;

View File

@ -13,12 +13,12 @@ import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.YEAR_1_AVG_2_AVG
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.YEAR_1_AVG_2_SEM
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.YEAR_ALL_GRADES
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Themes
import java.text.DecimalFormat
@ -34,6 +34,8 @@ class GradesEditorFragment : Fragment() {
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
*/
private val config by lazy { app.config.getFor(App.profileId).grades }
private var subjectId: Long = -1
private var semester: Int = 1
@ -108,7 +110,7 @@ class GradesEditorFragment : Fragment() {
continue
}
var weight = editorGrade.weight
if (app.appConfig.dontCountZeroToAverage && editorGrade.name == "0") {
if (!config.countZeroToAvg && editorGrade.name == "0") {
weight = 0f
}
val value = editorGrade.value * weight
@ -173,7 +175,7 @@ class GradesEditorFragment : Fragment() {
averageSemester = 0f
for (editorGrade in editorGrades) {
var weight = editorGrade.weight
if (app.appConfig.dontCountZeroToAverage && editorGrade.name == "0") {
if (!config.countZeroToAvg && editorGrade.name == "0") {
weight = 0f
}
val value = editorGrade.value * weight
@ -216,7 +218,7 @@ class GradesEditorFragment : Fragment() {
continue
}
var weight = grade.weight
if (app.appConfig.dontCountZeroToAverage && grade.name == "0") {
if (!config.countZeroToAvg && grade.name == "0") {
weight = 0f
}
val value = grade.value * weight

View File

@ -49,17 +49,17 @@ public class CounterActivity extends AppCompatActivity {
Time now = Time.getNow();
Time syncedNow = now;
//Time updateDiff = null;
if (app.appConfig.bellSyncDiff != null) {
if (app.appConfig.bellSyncMultiplier < 0) {
if (app.config.getTimetable().getBellSyncDiff() != null) {
if (app.config.getTimetable().getBellSyncMultiplier() < 0) {
// the bell is too fast, need to step further to go with it
// add some time
syncedNow = Time.sum(now, app.appConfig.bellSyncDiff);
syncedNow = Time.sum(now, app.config.getTimetable().getBellSyncDiff());
//Toast.makeText(c, "Bell sync diff is "+app.appConfig.bellSyncDiff.getStringHMS()+"\n\n Synced now is "+syncedNow.getStringHMS(), Toast.LENGTH_LONG).show();
}
if (app.appConfig.bellSyncMultiplier > 0) {
if (app.config.getTimetable().getBellSyncMultiplier() > 0) {
// the bell is delayed, need to roll the "now" time back
// subtract some time
syncedNow = Time.diff(now, app.appConfig.bellSyncDiff);
syncedNow = Time.diff(now, app.config.getTimetable().getBellSyncDiff());
}
}
@ -153,7 +153,7 @@ public class CounterActivity extends AppCompatActivity {
private short counterType = TIME_LEFT;
private long updateCounter(Time syncedNow) {
Time diff = Time.diff(counterTarget, syncedNow);
b.timeLeft.setText(counterType == TIME_TILL ? HomeFragment.timeTill(app, diff, app.appConfig.countInSeconds, "\n") : HomeFragment.timeLeft(app, diff, app.appConfig.countInSeconds, "\n"));
b.timeLeft.setText(counterType == TIME_TILL ? HomeFragment.timeTill(app, diff, app.config.getTimetable().getCountInSeconds(), "\n") : HomeFragment.timeLeft(app, diff, app.config.getTimetable().getCountInSeconds(), "\n"));
return updateInterval(app, diff);
}

View File

@ -5,6 +5,29 @@
package pl.szczodrzynski.edziennik.ui.modules.home
interface HomeCard {
companion object {
/**
* A card is visible on every profile.
* On a unified home fragment, shows
* summary data from every profile.
* Not every card may work like this.
*/
const val PROFILE_UNIFIED = -1
/**
* A card is visible on every profile, but does
* not show every profile, just the currently
* loaded one.
*/
const val PROFILE_ALL = 0
const val CARD_LUCKY_NUMBER = 1
const val CARD_TIMETABLE = 2
const val CARD_GRADES = 3
const val CARD_EVENTS = 4
}
val id: Int
fun bind(position: Int, holder: HomeCardAdapter.ViewHolder)
fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder)
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-2.
*/
package pl.szczodrzynski.edziennik.ui.modules.home
import java.io.Serializable
data class HomeCardModel(
val profileId: Int,
val cardId: Int
) : Serializable

View File

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.startCoroutineTimer
import kotlin.coroutines.CoroutineContext
class HomeDummyCard(val id: Int) : HomeCard, CoroutineScope {
class HomeDummyCard(override val id: Int) : HomeCard, CoroutineScope {
companion object {
private const val TAG = "HomeDummyCard"
}

View File

@ -310,7 +310,7 @@ public class HomeFragment extends Fragment {
public static long updateInterval(App app, Time diff) {
//Log.d(TAG, "Millis is "+System.currentTimeMillis() % 1000+", update in "+(1000-(System.currentTimeMillis() % 1000)));
if (app.appConfig.countInSeconds) {
if (app.config.getTimetable().getCountInSeconds()) {
return 1000-(System.currentTimeMillis() % 1000);
}
if (diff.minute > 10) {
@ -587,7 +587,7 @@ public class HomeFragment extends Fragment {
private void configCardGrades(Context c, LayoutInflater layoutInflater, Activity a, ViewGroup insertPoint) {
View root = layoutInflater.inflate(R.layout.card_grades, null);
DisplayMetrics displayMetrics = c.getResources().getDisplayMetrics();
updateCardGrades(c, a, root, displayMetrics.widthPixels - Utils.dpToPx((app.appConfig.miniDrawerVisible ? 72 : 0)/*miniDrawer size*/ + 24 + 24/*left and right offsets*/ + 16/*ellipsize width*/));
updateCardGrades(c, a, root, displayMetrics.widthPixels - Utils.dpToPx((app.config.getUi().getMiniMenuVisible() ? 72 : 0)/*miniDrawer size*/ + 24 + 24/*left and right offsets*/ + 16/*ellipsize width*/));
insertPoint.addView(root, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}

View File

@ -28,6 +28,7 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.FragmentHomeV2Binding
import pl.szczodrzynski.edziennik.ui.dialogs.home.StudentNumberDialog
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeGradesCard
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeLuckyNumberCard
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeTimetableCard
import pl.szczodrzynski.edziennik.utils.Themes
@ -40,6 +41,12 @@ class HomeFragmentV2 : Fragment(), CoroutineScope {
private const val TAG = "HomeFragment"
fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter) {
val homeCards = App.getConfig().ui.homeCards.toMutableList()
val fromPair = homeCards[fromPosition]
homeCards[fromPosition] = homeCards[toPosition]
homeCards[toPosition] = fromPair
App.getConfig().ui.homeCards = homeCards
val fromCard = cardAdapter.items[fromPosition]
cardAdapter.items[fromPosition] = cardAdapter.items[toPosition]
cardAdapter.items[toPosition] = fromCard
@ -78,7 +85,7 @@ class HomeFragmentV2 : Fragment(), CoroutineScope {
.withOnClickListener(OnClickListener {
activity.bottomSheet.close()
StudentNumberDialog(activity, app.profile) {
app.profileSaveAsync()
app.profileSave()
}
}),
BottomSheetSeparatorItem(true),
@ -92,10 +99,28 @@ class HomeFragmentV2 : Fragment(), CoroutineScope {
})
)
val items = mutableListOf<HomeCard>(
HomeLuckyNumberCard(0, app, activity, this, app.profile),
HomeTimetableCard(1, app, activity, this, app.profile)
)
val showUnified = false
val cards = app.config.ui.homeCards.filter { it.profileId == app.profile.id }.toMutableList()
if (cards.isEmpty()) {
cards += listOf(
HomeCardModel(app.profile.id, HomeCard.CARD_LUCKY_NUMBER),
HomeCardModel(app.profile.id, HomeCard.CARD_TIMETABLE),
/*HomeCardModel(app.profile.id, HomeCard.CARD_EVENTS),*/
HomeCardModel(app.profile.id, HomeCard.CARD_GRADES)
)
app.config.ui.homeCards = app.config.ui.homeCards.toMutableList().also { it.addAll(cards) }
}
val items = mutableListOf<HomeCard>()
cards.mapNotNullTo(items) {
when (it.cardId) {
HomeCard.CARD_LUCKY_NUMBER -> HomeLuckyNumberCard(it.cardId, app, activity, this, app.profile)
HomeCard.CARD_TIMETABLE -> HomeTimetableCard(it.cardId, app, activity, this, app.profile)
HomeCard.CARD_GRADES -> HomeGradesCard(it.cardId, app, activity, this, app.profile)
else -> null
}
}
val adapter = HomeCardAdapter(items)
val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter, b.refreshLayout))
@ -138,4 +163,4 @@ class HomeFragmentV2 : Fragment(), CoroutineScope {
})
itemTouchHelper.attachToRecyclerView(b.list)
}
}
}

View File

@ -74,8 +74,8 @@ class HomeTimetableCard(
.title(R.string.bell_sync_title)
.content(app.getString(R.string.bell_sync_howto, bellSyncTime!!.stringHM).toString() +
when {
app.appConfig.bellSyncDiff != null -> app.getString(R.string.bell_sync_current_dialog,
(if (app.appConfig.bellSyncMultiplier == -1) "-" else "+") + app.appConfig.bellSyncDiff.stringHMS)
app.config.timetable.bellSyncDiff != null -> app.getString(R.string.bell_sync_current_dialog,
(if (app.config.timetable.bellSyncMultiplier == -1) "-" else "+") + app.config.timetable.bellSyncDiff?.stringHMS)
else -> ""
})
.positiveText(R.string.ok)
@ -83,9 +83,8 @@ class HomeTimetableCard(
.neutralText(R.string.reset)
.onPositive { _, _: DialogAction? ->
val bellDiff = Time.diff(Time.getNow(), bellSyncTime)
app.appConfig.bellSyncDiff = bellDiff
app.appConfig.bellSyncMultiplier = if (bellSyncTime!!.value > Time.getNow().value) -1 else 1
app.saveConfig("bellSyncDiff", "bellSyncMultiplier")
app.config.timetable.bellSyncDiff = bellDiff
app.config.timetable.bellSyncMultiplier = if (bellSyncTime!!.value > Time.getNow().value) -1 else 1
MaterialDialog.Builder(activity)
.title(R.string.bell_sync_title)
@ -100,8 +99,8 @@ class HomeTimetableCard(
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive { _, _ ->
app.appConfig.bellSyncDiff = null
app.appConfig.bellSyncMultiplier = 0
app.config.timetable.bellSyncDiff = null
app.config.timetable.bellSyncMultiplier = 0
app.saveConfig("bellSyncDiff", "bellSyncMultiplier")
}
.show()
@ -129,10 +128,10 @@ class HomeTimetableCard(
val now = Time.getNow()
val syncedNow: Time = when (app.appConfig.bellSyncDiff != null) {
val syncedNow: Time = when (app.config.timetable.bellSyncDiff != null) {
true -> when {
app.appConfig.bellSyncMultiplier < 0 -> Time.sum(now, app.appConfig.bellSyncDiff)
app.appConfig.bellSyncMultiplier > 0 -> Time.diff(now, app.appConfig.bellSyncDiff)
app.config.timetable.bellSyncMultiplier < 0 -> Time.sum(now, app.config.timetable.bellSyncDiff)
app.config.timetable.bellSyncMultiplier > 0 -> Time.diff(now, app.config.timetable.bellSyncDiff)
else -> now
}
else -> now
@ -148,8 +147,8 @@ class HomeTimetableCard(
private fun updateCounter(syncedNow: Time): Long {
val diff = Time.diff(counterTarget, syncedNow)
b.cardTimetableTimeLeft.text = when (counterType) {
TIME_TILL -> HomeFragment.timeTill(app, diff, app.appConfig.countInSeconds)
else -> HomeFragment.timeLeft(app, diff, app.appConfig.countInSeconds)
TIME_TILL -> HomeFragment.timeTill(app, diff, app.config.timetable.countInSeconds)
else -> HomeFragment.timeLeft(app, diff, app.config.timetable.countInSeconds)
}
bellSyncTime = counterTarget.clone()
b.cardTimetableFullscreenCounter.visibility = View.VISIBLE

View File

@ -77,17 +77,16 @@ public class HomeTimetableCardOld {
new MaterialDialog.Builder(a)
.title(R.string.bell_sync_title)
.content(app.getString(R.string.bell_sync_howto, bellSyncTime.getStringHM())+
(app.appConfig.bellSyncDiff != null ?
""+app.getString(R.string.bell_sync_current_dialog, (app.appConfig.bellSyncMultiplier == -1 ? "-" : "+")+app.appConfig.bellSyncDiff.getStringHMS())
(app.config.getTimetable().getBellSyncDiff() != null ?
""+app.getString(R.string.bell_sync_current_dialog, (app.config.getTimetable().getBellSyncMultiplier() == -1 ? "-" : "+")+app.config.getTimetable().getBellSyncDiff().getStringHMS())
: ""))
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.neutralText(R.string.reset)
.onPositive((dialog, which) -> {
Time bellDiff = Time.diff(Time.getNow(), bellSyncTime);
app.appConfig.bellSyncDiff = bellDiff;
app.appConfig.bellSyncMultiplier = (bellSyncTime.getValue() > Time.getNow().getValue() ? -1 : 1);
app.saveConfig("bellSyncDiff", "bellSyncMultiplier");
app.config.getTimetable().setBellSyncDiff(bellDiff);
app.config.getTimetable().setBellSyncMultiplier(bellSyncTime.getValue() > Time.getNow().getValue() ? -1 : 1);
new MaterialDialog.Builder(a)
.title(R.string.bell_sync_title)
.content(app.getString(R.string.bell_sync_results, (bellSyncTime.getValue() > Time.getNow().getValue() ? "-" : "+"), bellDiff.getStringHMS()))
@ -101,9 +100,8 @@ public class HomeTimetableCardOld {
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog1, which1) -> {
app.appConfig.bellSyncDiff = null;
app.appConfig.bellSyncMultiplier = 0;
app.saveConfig("bellSyncDiff", "bellSyncMultiplier");
app.config.getTimetable().setBellSyncDiff(null);
app.config.getTimetable().setBellSyncMultiplier(0);
}))
.show();
})
@ -125,17 +123,17 @@ public class HomeTimetableCardOld {
Time now = Time.getNow();
Time syncedNow = now;
//Time updateDiff = null;
if (app.appConfig.bellSyncDiff != null) {
if (app.appConfig.bellSyncMultiplier < 0) {
if (app.config.getTimetable().getBellSyncDiff() != null) {
if (app.config.getTimetable().getBellSyncMultiplier() < 0) {
// the bell is too fast, need to step further to go with it
// add some time
syncedNow = Time.sum(now, app.appConfig.bellSyncDiff);
syncedNow = Time.sum(now, app.config.getTimetable().getBellSyncDiff());
//Toast.makeText(c, "Bell sync diff is "+app.appConfig.bellSyncDiff.getStringHMS()+"\n\n Synced now is "+syncedNow.getStringHMS(), Toast.LENGTH_LONG).show();
}
if (app.appConfig.bellSyncMultiplier > 0) {
if (app.config.getTimetable().getBellSyncMultiplier() > 0) {
// the bell is delayed, need to roll the "now" time back
// subtract some time
syncedNow = Time.diff(now, app.appConfig.bellSyncDiff);
syncedNow = Time.diff(now, app.config.getTimetable().getBellSyncDiff());
}
}
@ -230,7 +228,7 @@ public class HomeTimetableCardOld {
private short counterType = TIME_TILL;
private long updateCounter(Time syncedNow) {
Time diff = Time.diff(counterTarget, syncedNow);
b.cardTimetableTimeLeft.setText(counterType == TIME_TILL ? HomeFragment.timeTill(app, diff, app.appConfig.countInSeconds) : HomeFragment.timeLeft(app, diff, app.appConfig.countInSeconds));
b.cardTimetableTimeLeft.setText(counterType == TIME_TILL ? HomeFragment.timeTill(app, diff, app.config.getTimetable().getCountInSeconds()) : HomeFragment.timeLeft(app, diff, app.config.getTimetable().getCountInSeconds()));
bellSyncTime = counterTarget;
b.cardTimetableFullscreenCounter.setVisibility(View.VISIBLE);
return updateInterval(app, diff);

View File

@ -0,0 +1,191 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-29
*/
package pl.szczodrzynski.edziennik.ui.modules.home.cards
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
import android.os.Build
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.LinearLayout.HORIZONTAL
import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
import android.widget.TextView
import androidx.core.graphics.ColorUtils
import androidx.core.view.plusAssign
import androidx.core.view.setMargins
import androidx.lifecycle.Observer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.*
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
import pl.szczodrzynski.edziennik.databinding.CardHomeGradesBinding
import pl.szczodrzynski.edziennik.dp
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel
import kotlin.coroutines.CoroutineContext
class HomeGradesCard(
override val id: Int,
val app: App,
val activity: MainActivity,
val fragment: HomeFragmentV2,
val profile: Profile
) : HomeCard, CoroutineScope {
private var job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private lateinit var b: CardHomeGradesBinding
private val grades = mutableListOf<GradeFull>()
private val subjects = mutableListOf<ItemGradesSubjectModel>()
override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) {
holder.root.removeAllViews()
b = CardHomeGradesBinding.inflate(LayoutInflater.from(holder.root.context))
b.root.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
setMargins(8.dp)
}
holder.root += b.root
val sevenDaysAgo = System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1000
app.db.gradeDao().getAllFromDate(profile.id, profile.currentSemester, sevenDaysAgo).observe(fragment, Observer {
grades.apply {
clear()
addAll(it)
}
update()
})
b.root.setOnClickListener {
activity.loadTarget(MainActivity.DRAWER_ITEM_GRADES)
}
}
private fun update() {
subjects.clear()
grades.forEach { grade ->
val model = ItemGradesSubjectModel.searchModelBySubjectId(subjects, grade.subjectId)
?: run {
subjects.add(ItemGradesSubjectModel(
profile,
Subject(profile.id, grade.subjectId, grade.subjectLongName, grade.subjectShortName),
mutableListOf(),
mutableListOf()
))
ItemGradesSubjectModel.searchModelBySubjectId(subjects, grade.subjectId)
}
model?.grades1?.add(grade)
}
b.gradeList.removeAllViews()
val textLayoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(0, 0, 5.dp, 0)
}
val linearLayoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(2.dp, 0, 2.dp, 5.dp)
}
subjects.forEach { subject ->
val gradeItem = LinearLayout(b.gradeList.context)
gradeItem.orientation = HORIZONTAL
var totalWidth = 0
val maxWidth = (app.resources.displayMetrics.widthPixels -
Utils.dpToPx((if (app.config.ui.miniMenuVisible) 72 else 0) /*miniDrawer size*/ +
24 + 24 /*left and right offsets*/ +
16 /*ellipsize width*/)) / 1.5f
subject.grades1.onEach { grade ->
val gradeColor = when (app.profile.gradeColorMode) {
Profile.COLOR_MODE_DEFAULT -> grade.color
else -> Colors.gradeToColor(grade)
}
val gradeName = TextView(gradeItem.context).apply {
text = when (grade.type) {
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format, grade.name)
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format, grade.name)
TYPE_YEAR_PROPOSED -> app.getString(R.string.grade_year_proposed_format, grade.name)
TYPE_YEAR_FINAL -> app.getString(R.string.grade_year_final_format, grade.name)
else -> grade.name
}
setTextColor(when (ColorUtils.calculateLuminance(gradeColor) > 0.25) {
true -> 0xff000000
else -> 0xffffffff
}.toInt())
setTypeface(null, Typeface.BOLD)
setBackgroundResource(R.drawable.bg_rounded_4dp)
background.colorFilter = PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY)
setPadding(5.dp, 0, 5.dp, 0)
measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
totalWidth += gradeName.measuredWidth + 5.dp
if (totalWidth >= maxWidth) {
val ellipsisText = TextView(gradeItem.context).apply {
text = app.getString(R.string.ellipsis)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
setTextAppearance(context, R.style.NavView_TextView)
} else {
setTextAppearance(R.style.NavView_TextView)
}
setTypeface(null, Typeface.BOLD)
setPadding(0, 0, 0, 0)
}
gradeItem.addView(ellipsisText, textLayoutParams)
return@forEach
} else {
gradeItem.addView(gradeName, textLayoutParams)
}
}
val subjectName = TextView(gradeItem.context).apply {
text = app.getString(R.string.grade_subject_format, subject.subject?.longName
?: "")
ellipsize = TextUtils.TruncateAt.END
setSingleLine()
measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
gradeItem.addView(subjectName, textLayoutParams)
b.gradeList.addView(gradeItem, linearLayoutParams)
}
b.noData.visibility = if (subjects.isEmpty()) VISIBLE else GONE
b.gradeList.visibility = if (subjects.isEmpty()) GONE else VISIBLE
}
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
}

View File

@ -26,7 +26,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class HomeLuckyNumberCard(
val id: Int,
override val id: Int,
val app: App,
val activity: MainActivity,
val fragment: HomeFragmentV2,

View File

@ -4,6 +4,7 @@
package pl.szczodrzynski.edziennik.ui.modules.home.cards
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -31,7 +32,7 @@ import pl.szczodrzynski.navlib.colorAttr
import kotlin.coroutines.CoroutineContext
class HomeTimetableCard(
val id: Int,
override val id: Int,
val app: App,
val activity: MainActivity,
val fragment: HomeFragmentV2,
@ -48,6 +49,7 @@ class HomeTimetableCard(
private lateinit var b: CardHomeTimetableBinding
private val today = Date.getToday()
private var timetableDate: Date = Date.getToday()
private val searchEnd = today.clone().stepForward(0, 0, 7)
private var allLessons = listOf<LessonFull>()
@ -78,9 +80,9 @@ class HomeTimetableCard(
.sizeDp(20))
// get current bell-sync params
if (app.appConfig.bellSyncDiff != null) {
bellSyncDiffMillis = (app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000).toLong()
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier.toLong()
app.config.timetable.bellSyncDiff?.let {
bellSyncDiffMillis = (it.hour * 60 * 60 * 1000 + it.minute * 60 * 1000 + it.second * 1000).toLong()
bellSyncDiffMillis *= app.config.timetable.bellSyncMultiplier.toLong()
bellSyncDiffMillis *= -1
}
@ -89,6 +91,12 @@ class HomeTimetableCard(
allLessons = it
update()
})
b.root.setOnClickListener {
activity.loadTarget(MainActivity.DRAWER_ITEM_TIMETABLE, Bundle().apply {
putString("timetableDate", timetableDate.stringY_m_d)
})
}
}
private fun update() { launch {
@ -122,7 +130,8 @@ class HomeTimetableCard(
}
timetableDate
}
val timetableDate = deferred.await()
timetableDate = deferred.await()
val isToday = today == timetableDate
@ -136,6 +145,11 @@ class HomeTimetableCard(
if (isToday) {
// today
b.dayInfo.setText(R.string.home_timetable_today)
b.lessonInfo.setText(
R.string.home_timetable_lessons_remaining,
lessons.size,
lastLesson?.displayEndTime?.stringHM ?: "?"
)
counterStart = firstLesson?.displayStartTime
counterEnd = firstLesson?.displayEndTime
val isOngoing = counterStart <= now && now <= counterEnd
@ -210,8 +224,8 @@ class HomeTimetableCard(
private val LessonFull?.subjectSpannable: CharSequence
get() = if (this == null) "?" else when {
isCancelled -> displaySubjectName.asStrikethroughSpannable()
isChange -> displaySubjectName.asItalicSpannable()
isCancelled -> displaySubjectName?.asStrikethroughSpannable() ?: "?"
isChange -> displaySubjectName?.asItalicSpannable() ?: "?"
else -> displaySubjectName ?: "?"
}
@ -266,4 +280,4 @@ class HomeTimetableCard(
}
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
}
}

View File

@ -4,21 +4,23 @@ import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualV2Dialog;
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment;
import pl.szczodrzynski.edziennik.utils.models.Date;
@ -92,7 +94,16 @@ public class HomeworkAdapter extends RecyclerView.Adapter<HomeworkAdapter.ViewHo
holder.homeworkItemEdit.setVisibility((homework.addedManually ? View.VISIBLE : View.GONE));
holder.homeworkItemEdit.setOnClickListener(v -> {
new EventManualDialog(context).show(app, homework, null, null, EventManualDialog.DIALOG_HOMEWORK);
new EventManualV2Dialog(
(MainActivity) context,
homework.profileId,
null,
null,
null,
null,
homework,
null,
null);
});
if (homework.sharedBy == null) {

View File

@ -13,9 +13,10 @@ import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.databinding.FragmentHomeworkBinding
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualV2Dialog
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
@ -56,7 +57,7 @@ class HomeworkFragment : Fragment() {
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
EventManualDialog(activity).show(app, null, null, null, EventManualDialog.DIALOG_HOMEWORK)
EventManualV2Dialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK)
}),
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
@ -102,7 +103,7 @@ class HomeworkFragment : Fragment() {
activity.navView.bottomBar.fabExtendedText = getString(R.string.add)
activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon2.cmd_plus
activity.navView.setFabOnClickListener(View.OnClickListener {
EventManualDialog(activity).show(app, null, null, null, EventManualDialog.DIALOG_HOMEWORK)
EventManualV2Dialog(activity, App.profileId)
})
activity.gainAttention()

View File

@ -78,7 +78,6 @@ public class ChangelogIntroActivity extends IntroActivity {
.build());
}*/
app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE;
app.appConfig.savePending = true;
app.config.setAppVersion(BuildConfig.VERSION_CODE);
}
}

View File

@ -91,9 +91,8 @@ public class LoginActivity extends AppCompatActivity {
app = (App) getApplication();
if (!app.appConfig.loginFinished) {
app.appConfig.miniDrawerVisible = getResources().getConfiguration().smallestScreenWidthDp > 480;
app.saveConfig("miniDrawerVisible");
if (!app.config.getLoginFinished()) {
app.config.getUi().setMiniMenuVisible(getResources().getConfiguration().smallestScreenWidthDp > 480);
}
/*b.getRoot().addOnLayoutChangeListener(((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {

View File

@ -59,7 +59,7 @@ public class LoginChooserFragment extends Fragment {
b.cancelButton.setVisibility(View.VISIBLE);
b.cancelButton.setOnClickListener((v -> nav.navigateUp()));
}
else if (app.appConfig.loginFinished) {
else if (app.config.getLoginFinished()) {
// we are navigated here from AppDrawer
b.cancelButton.setVisibility(View.VISIBLE);
b.cancelButton.setOnClickListener((v -> {

View File

@ -19,8 +19,8 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginIuczniowieBinding;
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_INVALID_SCHOOL_NAME;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_INVALID_SCHOOL_NAME;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_IUCZNIOWIE;
public class LoginIuczniowieFragment extends Fragment {

View File

@ -19,10 +19,10 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginMobidziennikBinding;
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_ARCHIVED;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_INVALID_SERVER_ADDRESS;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_OLD_PASSWORD;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_ARCHIVED;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_INVALID_SERVER_ADDRESS;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_OLD_PASSWORD;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
public class LoginMobidziennikFragment extends Fragment {

View File

@ -101,12 +101,11 @@ class LoginSyncFragment : Fragment() {
d(TAG, loginStore.toString())
}*/
if (app.appConfig.loginFinished) {
if (app.config.loginFinished) {
LoginFinishFragment.firstRun = false
} else {
LoginFinishFragment.firstRun = true
app.appConfig.loginFinished = true
app.saveConfig("loginFinished")
app.config.loginFinished = true
}
LoginFinishFragment.firstProfileId = firstProfileId
@ -140,7 +139,8 @@ class LoginSyncFragment : Fragment() {
EventType(profileId, TYPE_INFORMATION.toLong(), getString(R.string.event_information), COLOR_INFORMATION)
)
app.db.eventTypeDao().addAll(typeList)
app.profileSaveFull(profile, loginStore)
app.db.profileDao().add(profile)
app.db.loginStoreDao().add(loginStore)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@ -39,10 +39,10 @@ import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
import pl.szczodrzynski.edziennik.ui.modules.webpush.QrScannerActivity;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_EXPIRED_TOKEN;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_INVALID_PIN;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_INVALID_SYMBOL;
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_INVALID_TOKEN;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_EXPIRED_TOKEN;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_INVALID_PIN;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_INVALID_SYMBOL;
import static pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_INVALID_TOKEN;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN;
public class LoginVulcanFragment extends Fragment {

View File

@ -125,6 +125,7 @@ class MessageFragment : Fragment(), CoroutineScope {
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onMessageGetEvent(event: MessageGetEvent) {
EventBus.getDefault().removeStickyEvent(event)
// TODO remove this: message = event.message
showMessage()
}
@ -197,6 +198,7 @@ class MessageFragment : Fragment(), CoroutineScope {
private fun showAttachments() {
if (message.attachmentIds != null) {
val insertPoint = b.attachments
insertPoint.removeAllViews()
val chipLayoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
chipLayoutParams.setMargins(0, Utils.dpToPx(8), 0, Utils.dpToPx(8))
@ -285,7 +287,7 @@ class MessageFragment : Fragment(), CoroutineScope {
EdziennikTask.attachmentGet(
App.profileId,
attachment.messageId,
message,
attachment.attachmentId,
attachment.attachmentName
).enqueue(activity)

View File

@ -1,176 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.graphics.Typeface;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.MessagesItemBinding;
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_DRAFT;
import static pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT;
public class MessagesAdapter extends RecyclerView.Adapter<MessagesAdapter.ViewHolder> {
private App app;
public List<MessageFull> messageList = new ArrayList<>();
private AdapterView.OnItemClickListener onItemClickListener;
public MessagesAdapter(App app, AdapterView.OnItemClickListener onItemClickListener) {
this.app = app;
this.onItemClickListener = onItemClickListener;
}
void setData(List<MessageFull> messageList) {
this.messageList = messageList;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(DataBindingUtil.inflate(inflater, R.layout.messages_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
MessagesItemBinding b = holder.b;
MessageFull message = messageList.get(position);
b.getRoot().setOnClickListener((v -> {
onItemClickListener.onItemClick(null, v, position, position);
}));
ViewCompat.setTransitionName(b.getRoot(), String.valueOf(message.id));
/*if (message.type == TYPE_RECEIVED) {
b.messageSender.setText(message.senderFullName);
}
else if (message.type == TYPE_SENT && message.recipients != null && message.recipients.size() > 0) {
StringBuilder senderText = new StringBuilder();
boolean first = true;
for (MessageRecipientFull recipient: message.recipients) {
if (!first) {
senderText.append(", ");
}
first = false;
senderText.append(recipient.fullName);
}
b.messageSender.setText(senderText.toString());
}*/
b.messageSubject.setText(message.subject);
b.messageDate.setText(Date.fromMillis(message.addedDate).getFormattedStringShort());
b.messageAttachmentImage.setVisibility(message.hasAttachments() ? View.VISIBLE : View.GONE);
try {
b.messageBody.setText(
Html.fromHtml(
message.body == null ? "" : message
.body
.substring(0, Math.min(message.body.length(), 200))
.replaceAll("\\[META:[A-z0-9]+;[0-9-]+]", "")
)
);
}
catch (Exception e) {
// TODO ???
}
if (message.type == TYPE_SENT || message.type == TYPE_DRAFT || message.seen) {
b.messageSender.setTextAppearance(b.messageSender.getContext(), R.style.NavView_TextView_Small);
b.messageSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
b.messageSubject.setTextAppearance(b.messageSubject.getContext(), R.style.NavView_TextView_Small);
b.messageSubject.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
b.messageDate.setTextAppearance(b.messageDate.getContext(), R.style.NavView_TextView_Small);
b.messageDate.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
}
else {
b.messageSender.setTextAppearance(b.messageSender.getContext(), R.style.NavView_TextView);
b.messageSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
b.messageSubject.setTextAppearance(b.messageSubject.getContext(), R.style.NavView_TextView);
b.messageSubject.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
b.messageDate.setTextAppearance(b.messageDate.getContext(), R.style.NavView_TextView);
b.messageDate.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
}
/*if (message.type == TYPE_RECEIVED) {
if (app.appConfig.teacherImages != null && app.appConfig.teacherImages.size() > 0 && app.appConfig.teacherImages.containsKey(message.senderId)) {
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(app.getFilesDir().getAbsolutePath()+"/teacher_"+message.senderId+".jpg");
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
}
else {
b.messageProfileImage.setImageDrawable(null);
int color = Colors.stringToMaterialColor(message.senderFullName);
b.messageProfileBackground.getDrawable().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
b.messageProfileName.setTextColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
if (message.senderFullName == null) {
b.messageProfileName.setText("N");
} else {
String[] nameParts = message.senderFullName.split(" ");
b.messageProfileName.setText(nameParts[0].toUpperCase().charAt(0) + "" + nameParts[1].toUpperCase().charAt(0));
}
}
}
else if (message.type == TYPE_SENT && message.recipients != null && message.recipients.size() > 0) {
MessageRecipientFull recipient = message.recipients.get(0);
if (message.recipients.size() == 1 && app.appConfig.teacherImages != null && app.appConfig.teacherImages.size() > 0 && app.appConfig.teacherImages.containsKey(recipient.id)) {
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(app.getFilesDir().getAbsolutePath()+"/teacher_"+recipient.id+".jpg");
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
}
else {
b.messageProfileImage.setImageDrawable(null);
int color = Colors.stringToMaterialColor(recipient.fullName);
b.messageProfileBackground.getDrawable().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
b.messageProfileName.setTextColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
if (recipient.fullName == null) {
b.messageProfileName.setText("N");
} else {
String[] nameParts = recipient.fullName.split(" ");
b.messageProfileName.setText(nameParts[0].toUpperCase().charAt(0) + "" + nameParts[1].toUpperCase().charAt(0));
}
}
}*/
MessagesUtils.MessageInfo messageInfo = MessagesUtils.getMessageInfo(app, message, 48, 24, 18, 12);
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage);
b.messageSender.setText(messageInfo.profileName);
}
@Override
public int getItemCount() {
return messageList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
MessagesItemBinding b;
public ViewHolder(MessagesItemBinding b) {
super(b.getRoot());
this.b = b;
}
}
}

View File

@ -0,0 +1,81 @@
package pl.szczodrzynski.edziennik.ui.modules.messages
import android.graphics.Typeface
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView.OnItemClickListener
import androidx.core.view.ViewCompat
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Adapter
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessagesItemBinding
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter.ViewHolder
import pl.szczodrzynski.edziennik.utils.models.Date
class MessagesAdapter(private val app: App, private val onItemClickListener: OnItemClickListener) : Adapter<ViewHolder>() {
var messageList: List<MessageFull> = ArrayList()
fun setData(messageList: List<MessageFull>) {
this.messageList = messageList
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(DataBindingUtil.inflate(inflater, R.layout.messages_item, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val b = holder.b
val message = messageList[position]
b.root.setOnClickListener { v: View? -> onItemClickListener.onItemClick(null, v, position, position.toLong()) }
ViewCompat.setTransitionName(b.root, message.id.toString())
b.messageSubject.text = message.subject
b.messageDate.text = Date.fromMillis(message.addedDate).formattedStringShort
b.messageAttachmentImage.visibility = if (message.hasAttachments()) View.VISIBLE else View.GONE
try {
b.messageBody.text = Html.fromHtml(
if (message.body == null) "" else message.body!!
.substring(0, message.body!!.length.coerceAtMost(200))
.replace("\\[META:[A-z0-9]+;[0-9-]+]".toRegex(), "")
)
} catch (e: Exception) {
// TODO ???
}
if (message.type == Message.TYPE_SENT || message.type == Message.TYPE_DRAFT || message.seen) {
b.messageSender.setTextAppearance(b.messageSender.context, R.style.NavView_TextView_Small)
b.messageSender.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
b.messageSender.textSize = 16f
b.messageSubject.setTextAppearance(b.messageSubject.context, R.style.NavView_TextView_Small)
b.messageSubject.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
b.messageDate.setTextAppearance(b.messageDate.context, R.style.NavView_TextView_Small)
b.messageDate.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
} else {
b.messageSender.setTextAppearance(b.messageSender.context, R.style.NavView_TextView_Normal)
b.messageSender.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
b.messageSender.textSize = 16f
b.messageSubject.setTextAppearance(b.messageSubject.context, R.style.NavView_TextView_Normal)
b.messageSubject.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
b.messageDate.setTextAppearance(b.messageDate.context, R.style.NavView_TextView_Normal)
b.messageDate.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
}
val messageInfo = MessagesUtils.getMessageInfo(app, message, 48, 24, 18, 12)
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
b.messageSender.text = messageInfo.profileName
}
override fun getItemCount(): Int {
return messageList.size
}
inner class ViewHolder(var b: MessagesItemBinding) : RecyclerView.ViewHolder(b.root)
}

View File

@ -1,10 +1,5 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.LayoutInflater;
@ -12,12 +7,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
@ -27,17 +19,13 @@ import com.mikepenz.iconics.IconicsColor;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.IconicsSize;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import com.theartofdev.edmodo.cropper.CropImage;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
@ -49,8 +37,6 @@ import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDialog;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import static android.app.Activity.RESULT_OK;
import static pl.szczodrzynski.edziennik.utils.Utils.getResizedBitmap;
import static pl.szczodrzynski.edziennik.utils.Utils.getStringFromFile;
import static pl.szczodrzynski.edziennik.utils.Utils.readableFileSize;
@ -523,69 +509,6 @@ public class MessagesDetailsFragment extends Fragment {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
CropImage.ActivityResult result = CropImage.getActivityResult(data);
Uri uri = result.getUri();
File photoFile = new File(uri.getPath());
File destFile = new File(getContext().getFilesDir(),"teacher_"+message.senderId+".jpg");
if (destFile.exists()) {
destFile.delete();
destFile = new File(getContext().getFilesDir(),"teacher_"+message.senderId+".jpg");
}
if (result.getCropRect().width() > 512) {
Bitmap bigImage = BitmapFactory.decodeFile(uri.getPath());
Bitmap smallImage = getResizedBitmap(bigImage, 512, 512);
try (FileOutputStream out = new FileOutputStream(destFile)) {
smallImage.compress(Bitmap.CompressFormat.JPEG, 80, out); // bmp is your Bitmap instance
// PNG is a lossless format, the compression factor (100) is ignored
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(destFile.getAbsolutePath());
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
if (app.appConfig.teacherImages == null) {
app.appConfig.teacherImages = new HashMap<>();
}
app.appConfig.teacherImages.put(message.senderId, true);
app.saveConfig("teacherImages");
if (photoFile.exists()) {
photoFile.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
}
else {
if (photoFile.renameTo(destFile)) {
// success
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(destFile.getAbsolutePath());
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
if (app.appConfig.teacherImages == null) {
app.appConfig.teacherImages = new HashMap<>();
}
app.appConfig.teacherImages.put(message.senderId, true);
app.saveConfig("teacherImages");
}
else {
// not this time
Toast.makeText(app, R.string.error_occured, Toast.LENGTH_LONG).show();
}
}
}
}
}
/*if (message.type == TYPE_RECEIVED) {

View File

@ -46,9 +46,9 @@ public class MessagesListFragment extends Fragment {
static final long TRANSITION_DURATION = 300L;
static final String TAP_POSITION = "tap_position";
private static int tapPosition = NO_POSITION;
private static int topPosition = NO_POSITION;
private static int bottomPosition = NO_POSITION;
private static int[] tapPositions = {NO_POSITION, NO_POSITION};
private static int[] topPositions = {NO_POSITION, NO_POSITION};
private static int[] bottomPositions = {NO_POSITION, NO_POSITION};
private int messageType = Message.TYPE_RECEIVED;
@ -93,11 +93,11 @@ public class MessagesListFragment extends Fragment {
messagesAdapter = new MessagesAdapter(app, ((parent, view1, position, id) -> {
// TODO ANIMATION
/*tapPosition = position;
topPosition = ((LinearLayoutManager) b.emailList.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
bottomPosition = ((LinearLayoutManager) b.emailList.getLayoutManager()).findLastCompletelyVisibleItemPosition();
tapPositions[messageType] = position;
topPositions[messageType] = ((LinearLayoutManager) b.emailList.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
bottomPositions[messageType] = ((LinearLayoutManager) b.emailList.getLayoutManager()).findLastCompletelyVisibleItemPosition();
view1.getGlobalVisibleRect(viewRect);
/*view1.getGlobalVisibleRect(viewRect);
((Transition) MessagesListFragment.this.getExitTransition()).setEpicenterCallback(new Transition.EpicenterCallback() {
@Override
public Rect onGetEpicenter(@NonNull Transition transition) {
@ -106,7 +106,7 @@ public class MessagesListFragment extends Fragment {
});*/
Bundle args = new Bundle();
args.putLong("messageId", messagesAdapter.messageList.get(position).id);
args.putLong("messageId", messagesAdapter.getMessageList().get(position).id);
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args);
// KOD W WERSJI 2.7
@ -224,17 +224,36 @@ public class MessagesListFragment extends Fragment {
b.progressBar.setVisibility(View.GONE);
b.emailList.setVisibility(View.VISIBLE);
messagesAdapter.setData(messageFulls);
LinearLayoutManager layoutManager = (LinearLayoutManager) b.emailList.getLayoutManager();
if (tapPositions[messageType] != NO_POSITION && layoutManager != null) {
//d("MessageList", "Scrolling");
if (topPositions[messageType] > layoutManager.findLastCompletelyVisibleItemPosition()) {
b.emailList.scrollToPosition(topPositions[messageType]);
}
else if (bottomPositions[messageType] < layoutManager.findFirstCompletelyVisibleItemPosition()) {
b.emailList.scrollToPosition(bottomPositions[messageType]);
}
else {
b.emailList.scrollToPosition(tapPositions[messageType]);
}
//tapPositions[messageType] = NO_POSITION;
//topPositions[messageType] = NO_POSITION;
//bottomPositions[messageType] = NO_POSITION;
}
// TODO ANIMATION
/*final ViewTreeObserver observer = viewParent.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
viewParent.getViewTreeObserver().removeOnPreDrawListener(this);
*//*viewParent.getViewTreeObserver().removeOnPreDrawListener(this);
if (getExitTransition() == null) {
setExitTransition(new SlideExplode().setDuration(TRANSITION_DURATION).setInterpolator(transitionInterpolator));
}
LinearLayoutManager layoutManager = (LinearLayoutManager) b.emailList.getLayoutManager();
View view2 = layoutManager != null ? layoutManager.findViewByPosition(tapPosition) : null;
if (view2 != null) {
view2.getGlobalVisibleRect(viewRect);
@ -248,24 +267,8 @@ public class MessagesListFragment extends Fragment {
d("MessagesList", "topPosition "+topPosition);
d("MessagesList", "tapPosition "+tapPosition);
d("MessagesList", "bottomPosition "+bottomPosition);
if (tapPosition != NO_POSITION && layoutManager != null) {
d("MessageList", "Scrolling");
d("MessagesList", "bottomPosition "+bottomPosition);*//*
if (bottomPosition > layoutManager.findLastCompletelyVisibleItemPosition()) {
b.emailList.scrollToPosition(bottomPosition);
}
else if (topPosition < layoutManager.findFirstCompletelyVisibleItemPosition()) {
b.emailList.scrollToPosition(topPosition);
}
else {
b.emailList.scrollToPosition(tapPosition);
}
tapPosition = NO_POSITION;
topPosition = NO_POSITION;
bottomPosition = NO_POSITION;
}
startPostponedEnterTransition();
return true;
@ -276,7 +279,7 @@ public class MessagesListFragment extends Fragment {
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
d("MessagesList", "onSaveInstanceState position "+tapPosition);
outState.putInt(TAP_POSITION, tapPosition);
d("MessagesList", "onSaveInstanceState position "+tapPositions[messageType]);
outState.putInt(TAP_POSITION, tapPositions[messageType]);
}
}

View File

@ -296,12 +296,11 @@ public class SettingsNewFragment extends MaterialAboutFragment {
new MaterialDialog.Builder(activity)
.title(R.string.settings_theme_theme_text)
.items(Themes.INSTANCE.getThemeNames(activity))
.itemsCallbackSingleChoice(app.appConfig.appTheme, (dialog, itemView, which, text) -> {
if (app.appConfig.appTheme == which)
.itemsCallbackSingleChoice(app.config.getUi().getTheme(), (dialog, itemView, which, text) -> {
if (app.config.getUi().getTheme() == which)
return true;
app.appConfig.appTheme = which;
Themes.INSTANCE.setThemeInt(app.appConfig.appTheme);
app.saveConfig("appTheme");
app.config.getUi().setTheme(which);
Themes.INSTANCE.setThemeInt(app.config.getUi().getTheme());
activity.recreate();
return true;
})
@ -318,14 +317,13 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setChecked(app.appConfig.miniDrawerVisible)
.setChecked(app.config.getUi().getMiniMenuVisible())
.setOnChangeAction((isChecked, tag) -> {
// 0,1 1
// 0,0 0
// 1,1 0
// 1,0 1
app.appConfig.miniDrawerVisible = isChecked;
app.saveConfig("miniDrawerVisible");
app.config.getUi().setMiniMenuVisible(isChecked);
activity.getNavView().drawer.setMiniDrawerVisiblePortrait(isChecked);
return true;
})
@ -372,7 +370,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
buttonCaptions.add(getString(R.string.menu_settings));
//buttonCaptions.add(getString(R.string.title_debugging));
List<Integer> selectedIds = new ArrayList<>();
for (int id: app.appConfig.miniDrawerButtonIds) {
for (int id: app.config.getUi().getMiniMenuButtons()) {
selectedIds.add(buttonIds.indexOf(id));
}
new MaterialDialog.Builder(activity)
@ -380,16 +378,16 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.content(getString(R.string.settings_theme_mini_drawer_buttons_dialog_text))
.items(buttonCaptions)
.itemsCallbackMultiChoice(selectedIds.toArray(new Integer[0]), (dialog, which, text) -> {
app.appConfig.miniDrawerButtonIds.clear();
List<Integer> list = new ArrayList<>();
for (int index: which) {
if (index == -1)
continue;
// wtf
int id = buttonIds.get(index);
app.appConfig.miniDrawerButtonIds.add(id);
list.add(id);
}
app.saveConfig("miniDrawerButtonIds");
app.config.getUi().setMiniMenuButtons(list);
activity.setDrawerItems();
activity.getDrawer().updateBadges();
return true;
@ -409,7 +407,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.color(IconicsColor.colorInt(iconColor))
)
.setOnClickAction(() -> {
if (app.appConfig.headerBackground != null) {
if (app.config.getUi().getHeaderBackground() != null) {
new MaterialDialog.Builder(activity)
.title(R.string.what_do_you_want_to_do)
.items(getString(R.string.settings_theme_drawer_header_dialog_set), getString(R.string.settings_theme_drawer_header_dialog_restore))
@ -418,12 +416,11 @@ public class SettingsNewFragment extends MaterialAboutFragment {
startActivityForResult(CropImage.getPickImageChooserIntent(activity), 22);
} else {
MainActivity ac = (MainActivity) getActivity();
app.appConfig.headerBackground = null;
app.config.getUi().setHeaderBackground(null);
if (ac != null) {
ac.getDrawer().setAccountHeaderBackground(null);
ac.getDrawer().open();
}
app.saveConfig("headerBackground");
}
})
.show();
@ -444,7 +441,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.color(IconicsColor.colorInt(iconColor))
)
.setOnClickAction(() -> {
if (app.appConfig.appBackground != null) {
if (app.config.getUi().getAppBackground() != null) {
new MaterialDialog.Builder(activity)
.title(R.string.what_do_you_want_to_do)
.items(getString(R.string.settings_theme_app_background_dialog_set), getString(R.string.settings_theme_app_background_dialog_restore))
@ -452,8 +449,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
if (position == 0) {
startActivityForResult(CropImage.getPickImageChooserIntent(activity), 23);
} else {
app.appConfig.appBackground = null;
app.saveConfig("appBackground");
app.config.getUi().setAppBackground(null);
activity.recreate();
}
})
@ -479,25 +475,25 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|__*/
private String getSyncCardIntervalSubText() {
if (app.appConfig.registerSyncInterval < 60 * 60)
if (app.config.getSync().getInterval() < 60 * 60)
return getString(
R.string.settings_sync_sync_interval_subtext_format,
HomeFragment.plural(activity, R.plurals.time_till_minutes, app.appConfig.registerSyncInterval / 60)
HomeFragment.plural(activity, R.plurals.time_till_minutes, app.config.getSync().getInterval() / 60)
);
return getString(
R.string.settings_sync_sync_interval_subtext_format,
HomeFragment.plural(activity, R.plurals.time_till_hours, app.appConfig.registerSyncInterval / 60 / 60) +
(app.appConfig.registerSyncInterval / 60 % 60 == 0 ?
HomeFragment.plural(activity, R.plurals.time_till_hours, app.config.getSync().getInterval() / 60 / 60) +
(app.config.getSync().getInterval() / 60 % 60 == 0 ?
"" :
" " + HomeFragment.plural(activity, R.plurals.time_till_minutes, app.appConfig.registerSyncInterval / 60 % 60)
" " + HomeFragment.plural(activity, R.plurals.time_till_minutes, app.config.getSync().getInterval() / 60 % 60)
)
);
}
private String getSyncCardQuietHoursSubText() {
return getString(
app.appConfig.quietHoursStart >= app.appConfig.quietHoursEnd ? R.string.settings_sync_quiet_hours_subtext_next_day_format : R.string.settings_sync_quiet_hours_subtext_format,
Time.fromMillis(Math.abs(app.appConfig.quietHoursStart)).getStringHM(),
Time.fromMillis(app.appConfig.quietHoursEnd).getStringHM()
app.config.getSync().getQuietHoursStart() >= app.config.getSync().getQuietHoursEnd() ? R.string.settings_sync_quiet_hours_subtext_next_day_format : R.string.settings_sync_quiet_hours_subtext_format,
Time.fromMillis(Math.abs(app.config.getSync().getQuietHoursStart())).getStringHM(),
Time.fromMillis(app.config.getSync().getQuietHoursEnd()).getStringHM()
);
}
private MaterialAboutItem getSyncCardWifiItem() {
@ -509,10 +505,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setChecked(app.appConfig.registerSyncOnlyWifi)
.setChecked(app.config.getSync().getOnlyWifi())
.setOnChangeAction((isChecked, tag) -> {
app.appConfig.registerSyncOnlyWifi = isChecked;
app.saveConfig("registerSyncOnlyWifi");
app.config.getSync().setOnlyWifi(isChecked);
SyncWorker.Companion.rescheduleNext(app);
return true;
});
@ -532,7 +527,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.color(IconicsColor.colorInt(iconColor))
);
syncCardIntervalItem.setSubTextChecked(getSyncCardIntervalSubText());
syncCardIntervalItem.setChecked(app.appConfig.registerSyncEnabled);
syncCardIntervalItem.setChecked(app.config.getSync().getEnabled());
syncCardIntervalItem.setOnClickAction(() -> {
List<CharSequence> intervalNames = new ArrayList<>();
if (App.devMode && false) {
@ -562,39 +557,37 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.title(getString(R.string.settings_sync_sync_interval_dialog_title))
.content(getString(R.string.settings_sync_sync_interval_dialog_text))
.items(intervalNames)
.itemsCallbackSingleChoice(intervals.indexOf(app.appConfig.registerSyncInterval), (dialog, itemView, which, text) -> {
app.appConfig.registerSyncInterval = intervals.get(which);
.itemsCallbackSingleChoice(intervals.indexOf(app.config.getSync().getInterval()), (dialog, itemView, which, text) -> {
app.config.getSync().setInterval(intervals.get(which));
syncCardIntervalItem.setSubTextChecked(getSyncCardIntervalSubText());
syncCardIntervalItem.setChecked(true);
if (!app.appConfig.registerSyncEnabled) {
if (!app.config.getSync().getEnabled()) {
addCardItem(CARD_SYNC, 1, getSyncCardWifiItem());
}
else {
refreshMaterialAboutList();
}
app.appConfig.registerSyncEnabled = true;
app.config.getSync().setEnabled(true);
// app.appConfig modifications have to surround syncCardIntervalItem and those ifs
app.saveConfig("registerSyncInterval", "registerSyncEnabled");
SyncWorker.Companion.rescheduleNext(app);
return true;
})
.show();
});
syncCardIntervalItem.setOnChangeAction((isChecked, tag) -> {
if (isChecked && !app.appConfig.registerSyncEnabled) {
if (isChecked && !app.config.getSync().getEnabled()) {
addCardItem(CARD_SYNC, 1, getSyncCardWifiItem());
}
else if (!isChecked) {
removeCardItem(CARD_SYNC, 1);
}
app.appConfig.registerSyncEnabled = isChecked;
app.saveConfig("registerSyncEnabled");
app.config.getSync().setEnabled(isChecked);
SyncWorker.Companion.rescheduleNext(app);
return true;
});
items.add(syncCardIntervalItem);
if (app.appConfig.registerSyncEnabled) {
if (app.config.getSync().getEnabled()) {
items.add(getSyncCardWifiItem());
}
@ -624,7 +617,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
);
syncCardQuietHoursItem.setChecked(app.appConfig.quietHoursStart > 0);
syncCardQuietHoursItem.setChecked(app.config.getSync().getQuietHoursStart() > 0);
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
syncCardQuietHoursItem.setOnClickAction(() -> {
new MaterialDialog.Builder(activity)
@ -636,11 +629,10 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.itemsCallback((dialog, itemView, position, text) -> {
if (position == 0) {
// set beginning
Time time = Time.fromMillis(Math.abs(app.appConfig.quietHoursStart));
Time time = Time.fromMillis(Math.abs(app.config.getSync().getQuietHoursStart()));
TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> {
// if it's disabled, it'll be enabled automatically
app.appConfig.quietHoursStart = new Time(hourOfDay, minute, second).getInMillis();
app.saveConfig("quietHoursStart");
app.config.getSync().setQuietHoursStart(new Time(hourOfDay, minute, second).getInMillis());
syncCardQuietHoursItem.setChecked(true);
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
refreshMaterialAboutList();
@ -648,15 +640,13 @@ public class SettingsNewFragment extends MaterialAboutFragment {
}
else {
// set end
Time time = Time.fromMillis(app.appConfig.quietHoursEnd);
Time time = Time.fromMillis(app.config.getSync().getQuietHoursEnd());
TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> {
if (app.appConfig.quietHoursStart < 0) {
if (app.config.getSync().getQuietHoursStart() < 0) {
// if it's disabled, enable
app.appConfig.quietHoursStart *= -1;
app.saveConfig("quietHoursStart");
app.config.getSync().setQuietHoursStart(-1 * app.config.getSync().getQuietHoursStart());
}
app.appConfig.quietHoursEnd = new Time(hourOfDay, minute, second).getInMillis();
app.saveConfig("quietHoursEnd");
app.config.getSync().setQuietHoursEnd(new Time(hourOfDay, minute, second).getInMillis());
syncCardQuietHoursItem.setChecked(true);
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
refreshMaterialAboutList();
@ -666,20 +656,17 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.show();
});
syncCardQuietHoursItem.setOnChangeAction((isChecked, tag) -> {
if (isChecked && app.appConfig.quietHoursStart < 0) {
app.appConfig.quietHoursStart *= -1;
app.saveConfig("quietHoursStart");
if (isChecked && app.config.getSync().getQuietHoursStart() < 0) {
app.config.getSync().setQuietHoursStart(app.config.getSync().getQuietHoursStart() * -1);
}
else if (!isChecked && app.appConfig.quietHoursStart > 0) {
app.appConfig.quietHoursStart *= -1;
app.saveConfig("quietHoursStart");
else if (!isChecked && app.config.getSync().getQuietHoursStart() > 0) {
app.config.getSync().setQuietHoursStart(app.config.getSync().getQuietHoursStart() * -1);
}
else if (isChecked && app.appConfig.quietHoursStart == 0) {
app.appConfig.quietHoursStart = new Time(22, 30, 0).getInMillis();
app.appConfig.quietHoursEnd = new Time(5, 30, 0).getInMillis();
else if (isChecked && app.config.getSync().getQuietHoursStart() == 0) {
app.config.getSync().setQuietHoursStart(new Time(22, 30, 0).getInMillis());
app.config.getSync().setQuietHoursEnd(new Time(5, 30, 0).getInMillis());
syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText());
refreshMaterialAboutList();
app.saveConfig("quietHoursStart", "quietHoursEnd");
}
return true;
});
@ -727,10 +714,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setChecked(app.appConfig.notifyAboutUpdates)
.setChecked(app.config.getSync().getNotifyAboutUpdates())
.setOnChangeAction((isChecked, tag) -> {
app.appConfig.notifyAboutUpdates = isChecked;
app.saveConfig("notifyAboutUpdates");
app.config.getSync().setNotifyAboutUpdates(isChecked);
return true;
})
);
@ -809,11 +795,11 @@ public class SettingsNewFragment extends MaterialAboutFragment {
}
}
private String getRegisterCardBellSyncSubText() {
if (app.appConfig.bellSyncDiff == null)
if (app.config.getTimetable().getBellSyncDiff() == null)
return getString(R.string.settings_register_bell_sync_subtext_disabled);
return getString(
R.string.settings_register_bell_sync_subtext_format,
(app.appConfig.bellSyncMultiplier == -1 ? "-" : "+") + app.appConfig.bellSyncDiff.getStringHMS()
(app.config.getTimetable().getBellSyncMultiplier() == -1 ? "-" : "+") + app.config.getTimetable().getBellSyncDiff().getStringHMS()
);
}
private MaterialAboutItem getRegisterCardSharedEventsItem() {
@ -972,8 +958,8 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.neutralText(R.string.reset)
.inputRangeRes(8, 8, R.color.md_red_500)
.input("±H:MM:SS",
(app.appConfig.bellSyncDiff != null
? (app.appConfig.bellSyncMultiplier == -1 ? "-" : "+") + app.appConfig.bellSyncDiff.getStringHMS()
(app.config.getTimetable().getBellSyncDiff() != null
? (app.config.getTimetable().getBellSyncMultiplier() == -1 ? "-" : "+") + app.config.getTimetable().getBellSyncDiff().getStringHMS()
: ""), (dialog, input) -> {
if (input == null)
return;
@ -986,10 +972,10 @@ public class SettingsNewFragment extends MaterialAboutFragment {
return;
}
if (input.charAt(0) == '+') {
app.appConfig.bellSyncMultiplier = 1;
app.config.getTimetable().setBellSyncMultiplier(1);
}
else if (input.charAt(0) == '-') {
app.appConfig.bellSyncMultiplier = -1;
app.config.getTimetable().setBellSyncMultiplier(-1);
}
else {
Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show();
@ -1010,15 +996,13 @@ public class SettingsNewFragment extends MaterialAboutFragment {
return;
}
app.appConfig.bellSyncDiff = new Time(hour, minute, second);
app.saveConfig("bellSyncDiff", "bellSyncMultiplier");
app.config.getTimetable().setBellSyncDiff(new Time(hour, minute, second));
registerCardBellSyncItem.setSubText(getRegisterCardBellSyncSubText());
refreshMaterialAboutList();
})
.onNeutral(((dialog, which) -> {
app.appConfig.bellSyncDiff = null;
app.appConfig.bellSyncMultiplier = 0;
app.saveConfig("bellSyncDiff", "bellSyncMultiplier");
app.config.getTimetable().setBellSyncDiff(null);
app.config.getTimetable().setBellSyncMultiplier(0);
registerCardBellSyncItem.setSubText(getRegisterCardBellSyncSubText());
refreshMaterialAboutList();
}))
@ -1035,9 +1019,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setChecked(app.appConfig.dontCountZeroToAverage)
.setChecked(!app.config.getFor(app.profileId).getGrades().getCountZeroToAvg())
.setOnChangeAction((isChecked, tag) -> {
app.appConfig.dontCountZeroToAverage = isChecked;
app.config.getFor(app.profileId).getGrades().setCountZeroToAvg(!isChecked);
app.saveConfig("dontCountZeroToAverage");
return true;
})
@ -1052,10 +1036,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.size(IconicsSize.dp(iconSizeDp))
.color(IconicsColor.colorInt(iconColor))
)
.setChecked(app.appConfig.countInSeconds)
.setChecked(app.config.getTimetable().getCountInSeconds())
.setOnChangeAction((isChecked, tag) -> {
app.appConfig.countInSeconds = isChecked;
app.saveConfig("countInSeconds");
app.config.getTimetable().setCountInSeconds(isChecked);
return true;
})
);
@ -1166,17 +1149,17 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.title(getString(R.string.settings_about_language_dialog_title))
.content(getString(R.string.settings_about_language_dialog_text))
.items(getString(R.string.language_system), getString(R.string.language_polish), getString(R.string.language_english))
.itemsCallbackSingleChoice(app.appConfig.language == null ? 0 : app.appConfig.language.equals("pl") ? 1 : 2, (dialog, itemView, which, text) -> {
.itemsCallbackSingleChoice(app.config.getUi().getLanguage() == null ? 0 : app.config.getUi().getLanguage().equals("pl") ? 1 : 2, (dialog, itemView, which, text) -> {
switch (which) {
case 0:
app.appConfig.language = null;
app.config.getUi().setLanguage(null);
initDefaultLocale();
break;
case 1:
app.appConfig.language = "pl";
app.config.getUi().setLanguage("pl");
break;
case 2:
app.appConfig.language = "en";
app.config.getUi().setLanguage("en");
break;
}
app.saveConfig("language");
@ -1397,10 +1380,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
else if (requestCode == 22 && resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
if (uri != null) {
app.appConfig.headerBackground = getRealPathFromURI(getContext(), uri);
app.saveConfig("headerBackground");
app.config.getUi().setHeaderBackground(getRealPathFromURI(getContext(), uri));
if (activity != null) {
activity.getDrawer().setAccountHeaderBackground(app.appConfig.headerBackground);
activity.getDrawer().setAccountHeaderBackground(app.config.getUi().getHeaderBackground());
activity.getDrawer().open();
}
}
@ -1408,8 +1390,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
else if (requestCode == 23 && resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
if (uri != null) {
app.appConfig.appBackground = getRealPathFromURI(getContext(), uri);
app.saveConfig("appBackground");
app.config.getUi().setAppBackground(getRealPathFromURI(getContext(), uri));
if (activity != null) {
activity.recreate();
}

View File

@ -5,16 +5,17 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.mikepenz.iconics.IconicsColor;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.IconicsSize;
@ -27,11 +28,11 @@ import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialogOld;
import pl.szczodrzynski.edziennik.utils.SpannableHtmlTagHandler;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK;
@ -159,7 +160,7 @@ public class TimetableAdapter extends RecyclerView.Adapter<TimetableAdapter.View
}
}
holder.timetableItemCard.setOnClickListener(v -> new EventListDialog(context).show(app, lessonDate, lesson.startTime));
holder.timetableItemCard.setOnClickListener(v -> new EventListDialogOld(context).show(app, lessonDate, lesson.startTime));
}
@Override

View File

@ -149,10 +149,10 @@ public class TimetableFragment extends Fragment {
Date today = Date.getToday();
Date date = Date.getToday();
int weekBeginning = app.appConfig.timetableDisplayDaysBackward - date.getWeekDay();
int weekBeginning = 2 - date.getWeekDay();
int weekEnd = weekBeginning + 6;
date.stepForward(0, 0, 0 - (app.appConfig.timetableDisplayDaysBackward));
for (int i = 0; i < app.appConfig.timetableDisplayDaysForward + app.appConfig.timetableDisplayDaysBackward + 1; i++) {
date.stepForward(0, 0, 0 - (2));
for (int i = 0; i < 7 + 2 + 1; i++) {
Bundle args = new Bundle();
args.putLong("date", date.getValue());
TimetableDayFragment timetableDayFragment = new TimetableDayFragment();
@ -176,7 +176,7 @@ public class TimetableFragment extends Fragment {
Log.d(TAG, "Got date "+getArguments().getLong("timetableDate", 0));
pageSelection = app.appConfig.timetableDisplayDaysBackward + Date.diffDays(gotDate, today);
pageSelection = 2 + Date.diffDays(gotDate, today);
displayingDate = gotDate;
}
else if (pageSelection == -1) {
@ -186,7 +186,7 @@ public class TimetableFragment extends Fragment {
List<LessonFull> lessons = app.db.lessonDao().getAllWeekNow(App.profileId, today.getWeekStart(), today);
displayingDate = HomeFragment.findDateWithLessons(App.profileId, lessons);
pageSelection = app.appConfig.timetableDisplayDaysBackward + Date.diffDays(displayingDate, today); // DEFAULT HERE
pageSelection = 2 + Date.diffDays(displayingDate, today); // DEFAULT HERE
activity.runOnUiThread(() -> {
viewPager.setCurrentItem(pageSelection, false);

View File

@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.viewpager.widget.ViewPager
import com.google.android.material.datepicker.MaterialDatePicker
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
@ -21,7 +22,9 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding
import pl.szczodrzynski.edziennik.observeOnce
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
@ -152,6 +155,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
activity.gainAttentionFAB()
fabShown = true
}
markLessonsAsSeen()
}
})
@ -196,4 +200,15 @@ class TimetableFragment : Fragment(), CoroutineScope {
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, true)
})
}}
private fun markLessonsAsSeen() = pageSelection?.let { date ->
app.db.timetableDao().getForDate(App.profileId, date).observeOnce(this@TimetableFragment, Observer { lessons ->
lessons.forEach { lesson ->
if (lesson.type != Lesson.TYPE_NORMAL && lesson.type != Lesson.TYPE_NO_LESSONS
&& !lesson.seen) {
app.db.metadataDao().setSeen(lesson.profileId, lesson, true)
}
}
})
}
}

View File

@ -15,13 +15,12 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import com.linkedin.android.tachyon.DayView
import com.linkedin.android.tachyon.DayViewConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
@ -52,7 +51,7 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
private lateinit var date: Date
private var startHour = DEFAULT_START_HOUR
private var endHour = DEFAULT_END_HOUR
private var firstEventMinute = 24*60
private var firstEventMinute = 24 * 60
// find SwipeRefreshLayout in the hierarchy
private val refreshLayout by lazy { view?.findParentById(R.id.refreshLayout) }
@ -108,15 +107,20 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
Log.d(TAG, "onViewCreated, date=$date")
// observe lesson database
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer<List<LessonFull>> { lessons ->
processLessonList(lessons)
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer { lessons ->
launch {
val events = withContext(Dispatchers.Default) {
app.db.eventDao().getAllByDateNow(App.profileId, date)
}
processLessonList(lessons, events)
}
})
}
private fun processLessonList(lessons: List<LessonFull>) {
private fun processLessonList(lessons: List<LessonFull>, events: List<EventFull>) {
// no lessons - timetable not downloaded yet
if (lessons.isEmpty()) {
inflater.inflate(R.layout.timetable_no_timetable, view as FrameLayout) { view, _, parent ->
inflater.inflate(R.layout.timetable_no_timetable, view as FrameLayout?) { view, _, parent ->
parent?.removeAllViews()
parent?.addView(view)
val b = TimetableNoTimetableBinding.bind(view)
@ -139,7 +143,7 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
}
// one lesson indicating a day without lessons
if (lessons.size == 1 && lessons[0].type == Lesson.TYPE_NO_LESSONS) {
inflater.inflate(R.layout.timetable_no_lessons, view as FrameLayout) { view, _, parent ->
inflater.inflate(R.layout.timetable_no_lessons, view as FrameLayout?) { view, _, parent ->
parent?.removeAllViews()
parent?.addView(view)
}
@ -166,10 +170,10 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
}
dayView.setHourLabelViews(hourLabelViews)
buildLessonViews(lessons)
buildLessonViews(lessons, events)
}
private fun buildLessonViews(lessons: List<LessonFull>) {
private fun buildLessonViews(lessons: List<LessonFull>, events: List<EventFull>) {
if (!isAdded)
return
@ -189,7 +193,7 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
val startTime = lesson.displayStartTime ?: continue
val endTime = lesson.displayEndTime ?: continue
firstEventMinute = min(firstEventMinute, startTime.hour*60 + startTime.minute)
firstEventMinute = min(firstEventMinute, startTime.hour * 60 + startTime.minute)
// Try to recycle an existing event view if there are enough left, otherwise inflate
// a new one
@ -206,6 +210,26 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
LessonDetailsDialog(activity, it.tag as LessonFull)
}
val eventList = events.filter { it.startTime != null && it.startTime == lesson.displayStartTime }.take(3)
eventList.getOrNull(0).let {
lb.event1.visibility = if (it == null) View.GONE else View.VISIBLE
lb.event1.background = it?.let {
R.drawable.bg_circle.resolveDrawable(activity).setTintColor(it.getColor())
}
}
eventList.getOrNull(1).let {
lb.event2.visibility = if (it == null) View.GONE else View.VISIBLE
lb.event2.background = it?.let {
R.drawable.bg_circle.resolveDrawable(activity).setTintColor(it.getColor())
}
}
eventList.getOrNull(2).let {
lb.event3.visibility = if (it == null) View.GONE else View.VISIBLE
lb.event3.background = it?.let {
R.drawable.bg_circle.resolveDrawable(activity).setTintColor(it.getColor())
}
}
val timeRange = "${startTime.stringHM} - ${endTime.stringHM}".asColoredSpannable(colorSecondary)
@ -265,26 +289,26 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
}
Lesson.TYPE_CHANGE -> {
lb.annotationVisible = true
if (lesson.subjectId != lesson.oldSubjectId && lesson.teacherId != lesson.oldTeacherId) {
lb.annotation.setText(
R.string.timetable_lesson_change_format,
"${lesson.oldSubjectName ?: "?"}, ${lesson.oldTeacherName ?: "?"}"
)
}
else if (lesson.subjectId != lesson.oldSubjectId) {
lb.annotation.setText(
R.string.timetable_lesson_change_format,
lesson.oldSubjectName ?: "?"
)
}
else if (lesson.teacherId != lesson.oldTeacherId) {
lb.annotation.setText(
R.string.timetable_lesson_change_format,
lesson.oldTeacherName ?: "?"
)
}
else {
lb.annotation.setText(R.string.timetable_lesson_change)
when {
lesson.subjectId != lesson.oldSubjectId && lesson.teacherId != lesson.oldTeacherId
&& lesson.oldSubjectName != null && lesson.oldTeacherName != null ->
lb.annotation.setText(
R.string.timetable_lesson_change_format,
"${lesson.oldSubjectName ?: "?"}, ${lesson.oldTeacherName ?: "?"}"
)
lesson.subjectId != lesson.oldSubjectId && lesson.oldSubjectName != null ->
lb.annotation.setText(
R.string.timetable_lesson_change_format,
lesson.oldSubjectName ?: "?"
)
lesson.teacherId != lesson.oldTeacherId && lesson.oldTeacherName != null ->
lb.annotation.setText(
R.string.timetable_lesson_change_format,
lesson.oldTeacherName ?: "?"
)
else -> lb.annotation.setText(R.string.timetable_lesson_change)
}
lb.annotation.background.colorFilter = PorterDuffColorFilter(
@ -295,35 +319,40 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
Lesson.TYPE_SHIFTED_SOURCE -> {
lb.annotationVisible = true
when {
lesson.date != lesson.oldDate -> lb.annotation.setText(
R.string.timetable_lesson_shifted_other_day,
lesson.date?.stringY_m_d ?: "?",
lesson.startTime?.stringHM ?: "?"
)
lesson.startTime != lesson.oldStartTime -> lb.annotation.setText(
R.string.timetable_lesson_shifted_same_day,
lesson.startTime?.stringHM ?: "?"
)
lesson.date != lesson.oldDate && lesson.date != null ->
lb.annotation.setText(
R.string.timetable_lesson_shifted_other_day,
lesson.date?.stringY_m_d ?: "?",
lesson.startTime?.stringHM ?: ""
)
lesson.startTime != lesson.oldStartTime && lesson.startTime != null ->
lb.annotation.setText(
R.string.timetable_lesson_shifted_same_day,
lesson.startTime?.stringHM ?: "?"
)
else -> lb.annotation.setText(R.string.timetable_lesson_shifted)
}
lb.annotation.background.colorFilter = PorterDuffColorFilter(
getColorFromAttr(activity, R.attr.timetable_lesson_shifted_source_color),
PorterDuff.Mode.SRC_ATOP
)
lb.annotation.background.setTintColor(R.attr.timetable_lesson_shifted_source_color.resolveAttr(activity))
}
Lesson.TYPE_SHIFTED_TARGET -> {
lb.annotationVisible = true
when {
lesson.date != lesson.oldDate -> lb.annotation.setText(
R.string.timetable_lesson_shifted_from_other_day,
lesson.oldDate?.stringY_m_d ?: "?",
lesson.oldStartTime?.stringHM ?: "?"
)
lesson.startTime != lesson.oldStartTime -> lb.annotation.setText(
R.string.timetable_lesson_shifted_from_same_day,
lesson.oldStartTime?.stringHM ?: "?"
)
lesson.date != lesson.oldDate && lesson.oldDate != null ->
lb.annotation.setText(
R.string.timetable_lesson_shifted_from_other_day,
lesson.oldDate?.stringY_m_d ?: "?",
lesson.oldStartTime?.stringHM ?: ""
)
lesson.startTime != lesson.oldStartTime && lesson.oldStartTime != null ->
lb.annotation.setText(
R.string.timetable_lesson_shifted_from_same_day,
lesson.oldStartTime?.stringHM ?: "?"
)
else -> lb.annotation.setText(R.string.timetable_lesson_shifted_from)
}
@ -337,7 +366,8 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
// The day view needs the event time ranges in the start minute/end minute format,
// so calculate those here
val startMinute = 60 * (lesson.displayStartTime?.hour ?: 0) + (lesson.displayStartTime?.minute ?: 0)
val startMinute = 60 * (lesson.displayStartTime?.hour
?: 0) + (lesson.displayStartTime?.minute ?: 0)
val endMinute = startMinute + 45
eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute))
}

View File

@ -1,9 +1,6 @@
package pl.szczodrzynski.edziennik.utils.models;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -11,86 +8,21 @@ import java.util.Map;
import java.util.TreeMap;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.widgets.WidgetConfig;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_IUCZNIOWIE;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN;
public class AppConfig {
private static final String TAG = "AppConfig";
public int appTheme = 1;
public List<Notification> notifications;
public long lastDeleteUnused = System.currentTimeMillis();
public Map<Long, Boolean> teacherImages = null;
public boolean dontCountZeroToAverage = false;
public AppConfig(App _app) {
notifications = new ArrayList<>();
miniDrawerButtonIds = new ArrayList<>();
miniDrawerButtonIds.add(MainActivity.DRAWER_ITEM_HOME);
miniDrawerButtonIds.add(MainActivity.DRAWER_ITEM_TIMETABLE);
miniDrawerButtonIds.add(MainActivity.DRAWER_ITEM_AGENDA);
miniDrawerButtonIds.add(MainActivity.DRAWER_ITEM_GRADES);
miniDrawerButtonIds.add(MainActivity.DRAWER_ITEM_MESSAGES);
miniDrawerButtonIds.add(MainActivity.DRAWER_ITEM_HOMEWORK);
miniDrawerButtonIds.add(MainActivity.DRAWER_ITEM_SETTINGS);
fcmToken = "";
fcmTokens = new TreeMap<>();
fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>("", new ArrayList<>()));
fcmTokens.put(LOGIN_TYPE_LIBRUS, new Pair<>("", new ArrayList<>()));
fcmTokens.put(LOGIN_TYPE_IUCZNIOWIE, new Pair<>("", new ArrayList<>()));
fcmTokens.put(LOGIN_TYPE_VULCAN, new Pair<>("", new ArrayList<>()));
}
public Map<Integer, WidgetConfig> widgetTimetableConfigs = new TreeMap<>();
public static final int ORDER_BY_DATE_DESC = 0;
public static final int ORDER_BY_SUBJECT_ASC = 1;
public static final int ORDER_BY_DATE_ASC = 2;
public static final int ORDER_BY_SUBJECT_DESC = 3;
public int gradesOrderBy = ORDER_BY_DATE_DESC;
public String headerBackground = null;
public String appBackground = null;
public int lastAppVersion = BuildConfig.VERSION_CODE;
public boolean registerSyncEnabled = true;
public boolean registerSyncOnlyWifi = false;
public int registerSyncInterval = 60 * 60; // seconds
public int timetableDisplayDaysForward = 7;
public int timetableDisplayDaysBackward = 2;
//public boolean syncNotificationsEnabled = true;
public String fcmToken;
public Map<Integer, Pair<String, List<Integer>>> fcmTokens;
public boolean miniDrawerVisible = false;
//public boolean autoRegistrationAllowed = false;
public boolean loginFinished = false;
public Time bellSyncDiff = null;
public int bellSyncMultiplier = 0;
public boolean savePending = false;
public List<Integer> miniDrawerButtonIds;
public boolean notifyAboutUpdates = true;
public String updateVersion = "";
public String updateUrl = "";
public String updateFilename = "";
@ -99,25 +31,9 @@ public class AppConfig {
public boolean webPushEnabled = false;
public boolean tapTargetSetAsRead = false;
public boolean tapTargetSwitchProfile = false;
public boolean countInSeconds = false;
public long quietHoursStart = 0;
public long quietHoursEnd = 0;
public boolean quietDuringLessons = true;
public String devModePassword = null;
public long appInstalledTime = 0;
public long appRateSnackbarTime = 0;
public int mobidziennikOldMessages = -1;
@Nullable
public String language = null;
@NonNull
public boolean dontShowAppManagerDialog = false;
}

View File

@ -1,5 +1,7 @@
package pl.szczodrzynski.edziennik.utils.models;
import androidx.annotation.Nullable;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull;
@ -43,8 +45,9 @@ public class ItemGradesSubjectModel {
public boolean expandView = false;
public static ItemGradesSubjectModel searchModelBySubjectId(List<ItemGradesSubjectModel> subjectList, long id) {
for (ItemGradesSubjectModel subjectModel: subjectList) {
public static @Nullable
ItemGradesSubjectModel searchModelBySubjectId(List<ItemGradesSubjectModel> subjectList, long id) {
for (ItemGradesSubjectModel subjectModel : subjectList) {
if (subjectModel.subject != null && subjectModel.subject.id == id) {
return subjectModel;
}

Some files were not shown because too many files have changed in this diff Show More