From cf7c7afe616ac62f97f4aad7797262da73140ff5 Mon Sep 17 00:00:00 2001 From: Sebastian Seedorf Date: Mon, 8 Nov 2021 00:07:48 +0100 Subject: [PATCH] Updated dependencies --- app/build.gradle | 53 ++++---- app/src/main/AndroidManifest.xml | 12 +- .../java/de/sebse/fuplanner2/MainActivity.kt | 36 ++--- .../de/sebse/fuplanner2/auth/FUAuthModule.kt | 6 +- .../sebse/fuplanner2/blackboard/Blackboard.kt | 18 +-- .../sebse/fuplanner2/blackboard/ExtEvents.kt | 2 +- .../sebse/fuplanner2/network/CustomRequest.kt | 2 +- .../fuplanner2/ui/courses/CoursesFragment.kt | 13 +- .../fuplanner2/ui/details/DetailsFragment.kt | 10 +- .../fuplanner2/ui/details/DetailsViewModel.kt | 2 +- .../AnnouncementsFragment.kt | 11 +- .../AnnouncementsViewModel.kt | 2 +- .../DescriptionFragment.kt | 7 +- .../ui/details_events/EventsFragment.kt | 9 +- .../ui/details_events/EventsViewModel.kt | 2 +- .../ui/notification/NotificationFragment.kt | 6 +- .../ui/schedule/ScheduleFragment.kt | 127 +++++++++++------- .../ui/schedule/ScheduleViewModel.kt | 2 +- .../sebse/fuplanner2/whiteboard/Whiteboard.kt | 12 +- app/src/main/res/layout/activity_main.xml | 1 + app/src/main/res/layout/content_main.xml | 2 +- app/src/main/res/layout/fragment_schedule.xml | 7 +- build.gradle | 11 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 24 files changed, 204 insertions(+), 153 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4edd8dc..db1ca7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,17 +1,16 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' apply plugin: 'androidx.navigation.safeargs.kotlin' android { - compileSdkVersion 29 - buildToolsVersion "29.0.3" + compileSdkVersion 31 + buildToolsVersion "30.0.2" defaultConfig { applicationId "de.sebse.fuplanner2" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 1 versionName "1.0" @@ -31,6 +30,10 @@ android { } } + buildFeatures { + viewBinding true + } + dataBinding { enabled = true } @@ -52,28 +55,26 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.2.0' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'com.google.android.material:material:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1' - implementation 'androidx.navigation:navigation-ui-ktx:2.2.1' - implementation 'androidx.fragment:fragment:1.2.4' - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" - implementation 'com.android.volley:volley:1.1.1' - implementation 'androidx.room:room-runtime:2.2.5' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' - kapt 'androidx.room:room-compiler:2.2.5' - implementation 'androidx.room:room-ktx:2.2.5' - implementation 'com.beust:klaxon:5.0.1' - implementation 'androidx.work:work-runtime-ktx:2.3.4' - implementation 'androidx.paging:paging-runtime:2.1.2' - implementation 'com.github.thellmund.android-week-view:core:4.1.5' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - - debugImplementation 'com.amitshekhar.android:debug-db:1.0.6' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.5' + implementation 'androidx.fragment:fragment-ktx:1.3.6' + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" + implementation 'com.android.volley:volley:1.2.1' + implementation 'androidx.room:room-runtime:2.3.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0' + kapt 'androidx.room:room-compiler:2.3.0' + implementation 'androidx.room:room-ktx:2.3.0' + implementation 'com.beust:klaxon:5.5' + implementation 'androidx.work:work-runtime-ktx:2.7.0' + implementation 'androidx.paging:paging-runtime-ktx:3.0.1' + implementation 'com.github.thellmund:android-week-view:5.3.2' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3a63c98..3e747e2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,8 +15,10 @@ android:supportsRtl="true" android:theme="@style/FUTheme" android:name=".CustomApplication"> - + @@ -29,8 +31,10 @@ - + diff --git a/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt b/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt index e8b6f02..4c2c09c 100644 --- a/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt +++ b/app/src/main/java/de/sebse/fuplanner2/MainActivity.kt @@ -12,6 +12,7 @@ import androidx.core.os.bundleOf import androidx.lifecycle.* import androidx.navigation.NavOptions import androidx.navigation.findNavController +import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.navigateUp import androidx.navigation.ui.setupActionBarWithNavController @@ -20,9 +21,8 @@ import de.sebse.fuplanner2.auth.AppAccounts import de.sebse.fuplanner2.database.AppDatabase import de.sebse.fuplanner2.database.Course import de.sebse.fuplanner2.database.User -import de.sebse.fuplanner2.utils.console -import kotlinx.android.synthetic.main.activity_main.* -import kotlinx.android.synthetic.main.app_bar_main.* +import de.sebse.fuplanner2.databinding.ActivityMainBinding +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -31,30 +31,33 @@ class MainActivity() : AppCompatActivity() { private lateinit var appBarConfiguration: AppBarConfiguration private lateinit var activityViewModel: MainActivityViewModel + private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - setSupportActionBar(toolbar) + val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment + val navController = navHostFragment.navController + binding = ActivityMainBinding.inflate(layoutInflater) + setSupportActionBar(binding.appBarMain.toolbar) - val navController = findNavController(R.id.nav_host_fragment) activityViewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java) activityViewModel.user.observe(this) { - nav_view.getHeaderView(0).run { + binding.navView.getHeaderView(0).run { findViewById(R.id.nav_header_title).text = getString(R.string.full_name, it?.firstName, it?.lastName) findViewById(R.id.nav_header_subtitle).text = it?.email } } activityViewModel.notificationCnt.observe(this) { - nav_view.menu.findItem(R.id.nav_notifications).apply { + binding.navView.menu.findItem(R.id.nav_notifications).apply { icon = ContextCompat.getDrawable( this@MainActivity, if (it == 0) R.drawable.ic_menu_notifications_none else R.drawable.ic_menu_notifications ) actionView = if (it == 0) null - else layoutInflater.inflate(R.layout.nav_action_view_counter, nav_view, false).apply { + else layoutInflater.inflate(R.layout.nav_action_view_counter, binding.navView, false).apply { findViewById(R.id.counterText).text = when (it) { in 0..99 -> it.toString() else -> "99+" @@ -63,23 +66,23 @@ class MainActivity() : AppCompatActivity() { } } activityViewModel.latestSemester.observe(this) { - val courseOrder = nav_view.menu.findItem(R.id.nav_courses).order - var i = nav_view.menu.size() - 1 + val courseOrder = binding.navView.menu.findItem(R.id.nav_courses).order + var i = binding.navView.menu.size() - 1 while(i >= 0) { - val menuItem: MenuItem = nav_view.menu.getItem(i--) + val menuItem: MenuItem = binding.navView.menu.getItem(i--) if (menuItem.order / 100 == courseOrder / 100 && menuItem.order != courseOrder) { - nav_view.menu.removeItem(menuItem.itemId) + binding.navView.menu.removeItem(menuItem.itemId) } } it.mapIndexed { index, course -> val itemOrder = courseOrder / 100 * 100 + index + 2 - nav_view.menu.add(0, itemOrder, itemOrder, course.title).setOnMenuItemClickListener { + binding.navView.menu.add(0, itemOrder, itemOrder, course.title).setOnMenuItemClickListener { navController.navigate( R.id.course_details, bundleOf("courseId" to course.uid, "title" to course.title), NavOptions.Builder().setPopUpTo(R.id.nav_courses, false).build() ) - drawer_layout.closeDrawers() + binding.drawerLayout.closeDrawers() false } } @@ -87,10 +90,10 @@ class MainActivity() : AppCompatActivity() { appBarConfiguration = AppBarConfiguration( setOf(R.id.nav_courses, R.id.nav_canteen, R.id.nav_schedule, R.id.nav_notifications), - drawer_layout + binding.drawerLayout ) setupActionBarWithNavController(navController, appBarConfiguration) - nav_view.setupWithNavController(navController) + binding.navView.setupWithNavController(navController) if (intent.getBooleanExtra(EXTRA_OPEN_NOTIFICATIONS, false)) { navController.navigate(R.id.nav_notifications) @@ -138,6 +141,7 @@ class MainActivityViewModel : ViewModel() { val notificationCnt: LiveData = database.notificationDao().getUnreadRowCount() val latestSemester: LiveData> = database.courseDao().getLatestSemester() + @DelicateCoroutinesApi fun updateSelectedUser(account: Account) { GlobalScope.launch { user.postValue(database.userDao().findByUsername(account.name)) diff --git a/app/src/main/java/de/sebse/fuplanner2/auth/FUAuthModule.kt b/app/src/main/java/de/sebse/fuplanner2/auth/FUAuthModule.kt index d89f7c0..5a133e9 100644 --- a/app/src/main/java/de/sebse/fuplanner2/auth/FUAuthModule.kt +++ b/app/src/main/java/de/sebse/fuplanner2/auth/FUAuthModule.kt @@ -29,7 +29,7 @@ abstract class FUAuthModule { if (response.networkResponse.statusCode == 200) { return parseResponse(response.body) } else { - val relLocation = response.headers["Location"] + val relLocation = response.headers?.get("Location") ?: throw invalidResponse(100110, "No IDP form location!") val formUri = URI(samlUrl).resolve(relLocation).toString() requester.head(formUri, getCookies(user)) @@ -72,8 +72,8 @@ abstract class FUAuthModule { } private fun updateCookies(user: User, response: NetData) { - val setCookies = parseCookies(response.networkResponse.allHeaders) - setCookies["JSESSIONID"]?.let { + val setCookies = response.networkResponse.allHeaders?.let { parseCookies(it) } + setCookies?.get("JSESSIONID")?.let { user.cookies.idpJsessionId = it } } diff --git a/app/src/main/java/de/sebse/fuplanner2/blackboard/Blackboard.kt b/app/src/main/java/de/sebse/fuplanner2/blackboard/Blackboard.kt index fb4d0fb..f8f4c49 100644 --- a/app/src/main/java/de/sebse/fuplanner2/blackboard/Blackboard.kt +++ b/app/src/main/java/de/sebse/fuplanner2/blackboard/Blackboard.kt @@ -42,7 +42,7 @@ object Blackboard: FUAuthModule() { override suspend fun login(ctx: Context, name: String, password: String, user: User) { val requester = Requester(ctx) var response = requester.head(LOGIN_URL, cookies = getCookies(user, shib = true)) - val samlUri = response.headers["Location"] + val samlUri = response.headers?.get("Location") ?: throw invalidResponse(101100, "Location header not set!") if (!samlUri.startsWith(RESTORE_SESSION_URI)) { @@ -56,13 +56,13 @@ object Blackboard: FUAuthModule() { updateCookies(user, response) // Finish BB response = requester.get( - response.networkResponse.headers["Location"] ?: throw invalidResponse(101101, "No Location header to finish Blackboard"), + response.networkResponse.headers?.get("Location") ?: throw invalidResponse(101101, "No Location header to finish Blackboard"), getCookies(user, shib = true) ) } // Start Session response = requester.get( - response.networkResponse.headers["Location"] ?: throw invalidResponse(101102, "No Location header to start Blackboard session"), + response.networkResponse.headers?.get("Location") ?: throw invalidResponse(101102, "No Location header to start Blackboard session"), getCookies(user, shib = true) ) @@ -88,19 +88,19 @@ object Blackboard: FUAuthModule() { } private fun updateCookies(user: User, response: NetData) { - val setCookies = parseCookies(response.networkResponse.allHeaders) - setCookies["JSESSIONID"]?.let { + val setCookies = response.networkResponse.allHeaders?.let { parseCookies(it) } + setCookies?.get("JSESSIONID")?.let { user.cookies.bbJsessionId = it } - setCookies["session_id"]?.let { + setCookies?.get("session_id")?.let { user.cookies.bbSessionId = it } - setCookies["s_session_id"]?.let { + setCookies?.get("s_session_id")?.let { user.cookies.bbSSessionId = it } setCookies - .filter{ (key, _) -> key.startsWith("_shibsession_") } - .forEach { (key, value) -> + ?.filter{ (key, _) -> key.startsWith("_shibsession_") } + ?.forEach { (key, value) -> user.cookies.bbShibKey = key user.cookies.bbShibValue = value return@forEach diff --git a/app/src/main/java/de/sebse/fuplanner2/blackboard/ExtEvents.kt b/app/src/main/java/de/sebse/fuplanner2/blackboard/ExtEvents.kt index bc4aff8..0f84953 100644 --- a/app/src/main/java/de/sebse/fuplanner2/blackboard/ExtEvents.kt +++ b/app/src/main/java/de/sebse/fuplanner2/blackboard/ExtEvents.kt @@ -31,7 +31,7 @@ suspend fun Blackboard.getEvents(ctx: Context, database: AppDatabase, user: User null ).let { Regex("lv/([0-9]+)\\?") - .find(it.headers["Location"] ?: "") + .find(it.headers?.get("Location") ?: "") ?.groups ?.get(1) ?.value diff --git a/app/src/main/java/de/sebse/fuplanner2/network/CustomRequest.kt b/app/src/main/java/de/sebse/fuplanner2/network/CustomRequest.kt index 83b73e6..73acef1 100644 --- a/app/src/main/java/de/sebse/fuplanner2/network/CustomRequest.kt +++ b/app/src/main/java/de/sebse/fuplanner2/network/CustomRequest.kt @@ -72,7 +72,7 @@ class CustomRequest: Request { } override fun parseNetworkResponse(response: NetworkResponse): Response? { - val parsed: String = parse(response.data, response.headers) ?: "" + val parsed: String = response.headers?.let { parse(response.data, it) } ?: "" return Response.success(NetData(parsed, response), HttpHeaderParser.parseCacheHeaders(response)) } diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesFragment.kt index 637a76b..0c72a45 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesFragment.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/courses/CoursesFragment.kt @@ -16,14 +16,16 @@ import androidx.work.workDataOf import de.sebse.fuplanner2.R import de.sebse.fuplanner2.auth.AppAccounts import de.sebse.fuplanner2.database.Course +import de.sebse.fuplanner2.databinding.ActivityMainBinding +import de.sebse.fuplanner2.databinding.FragmentRefreshRecyclerBinding import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_ACCOUNT_NAME import de.sebse.fuplanner2.worker.CourseWorker -import kotlinx.android.synthetic.main.fragment_refresh_recycler.view.* class CoursesFragment : Fragment() { private lateinit var coursesViewModel: CoursesViewModel private lateinit var navController: NavController + private lateinit var binding: FragmentRefreshRecyclerBinding override fun onCreateView( @@ -34,13 +36,14 @@ class CoursesFragment : Fragment() { val viewManager = LinearLayoutManager(context) val viewAdapter = CoursesAdapter(this::onClick) coursesViewModel = ViewModelProvider(this).get(CoursesViewModel::class.java) + binding = FragmentRefreshRecyclerBinding.inflate(inflater, container, false) return inflater.inflate(R.layout.fragment_refresh_recycler, container, false).apply { - recycler_view.apply { + binding.recyclerView.apply { //setHasFixedSize(true) layoutManager = viewManager adapter = viewAdapter } - swipe_refresh_layout.setOnRefreshListener { + binding.swipeRefreshLayout.setOnRefreshListener { val work = OneTimeWorkRequestBuilder() .setInputData(workDataOf( KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name @@ -50,9 +53,9 @@ class CoursesFragment : Fragment() { .enqueue(work) WorkManager.getInstance(context.applicationContext) .getWorkInfoByIdLiveData(work.id) - .observe(viewLifecycleOwner, Observer { + .observe(viewLifecycleOwner, { if (it.state.isFinished) - swipe_refresh_layout.isRefreshing = false + binding.swipeRefreshLayout.isRefreshing = false }) } coursesViewModel.text.observe(viewLifecycleOwner, Observer { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsFragment.kt index e02da1d..4f097ee 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsFragment.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsFragment.kt @@ -17,12 +17,11 @@ import androidx.work.workDataOf import de.sebse.fuplanner2.R import de.sebse.fuplanner2.auth.AppAccounts import de.sebse.fuplanner2.database.Lecturer -import de.sebse.fuplanner2.utils.console +import de.sebse.fuplanner2.databinding.FragmentRefreshRecyclerBinding import de.sebse.fuplanner2.utils.enqueueOneTimeWork import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_ACCOUNT_NAME import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_COURSE_ID import de.sebse.fuplanner2.worker.CourseWorker -import kotlinx.android.synthetic.main.fragment_refresh_recycler.view.* class DetailsFragment : Fragment() { @@ -31,6 +30,7 @@ class DetailsFragment : Fragment() { private val args: DetailsFragmentArgs by navArgs() private val detailsViewModel: DetailsViewModel by viewModels { DetailsViewModelFactory(args.courseId) } private lateinit var navController: NavController + private lateinit var binding: FragmentRefreshRecyclerBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -39,13 +39,13 @@ class DetailsFragment : Fragment() { val viewManager = LinearLayoutManager(context) val viewAdapter = DetailsAdapter(this::launchFragment, this::sendMail) return inflater.inflate(R.layout.fragment_refresh_recycler, container, false).apply { - recycler_view.apply { + binding.recyclerView.apply { setHasFixedSize(true) layoutManager = viewManager adapter = viewAdapter } - swipe_refresh_layout.setOnRefreshListener { + binding.swipeRefreshLayout.setOnRefreshListener { enqueueOneTimeWork(context.applicationContext) { it.setInputData(workDataOf( KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name, @@ -53,7 +53,7 @@ class DetailsFragment : Fragment() { )) }.observe(viewLifecycleOwner, Observer { if (it.state.isFinished) - swipe_refresh_layout.isRefreshing = false + binding.swipeRefreshLayout.isRefreshing = false }) } detailsViewModel.course.observe(viewLifecycleOwner, Observer { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsViewModel.kt index c624c83..59e4a8c 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details/DetailsViewModel.kt @@ -8,7 +8,7 @@ import de.sebse.fuplanner2.database.Course class DetailsViewModelFactory(private val courseId: Long): ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = DetailsViewModel(courseId) as T + override fun create(modelClass: Class): T = DetailsViewModel(courseId) as T } class DetailsViewModel(courseId: Long) : ViewModel() { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsFragment.kt index c04318b..69bda28 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsFragment.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsFragment.kt @@ -14,12 +14,12 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.work.workDataOf import de.sebse.fuplanner2.R import de.sebse.fuplanner2.auth.AppAccounts +import de.sebse.fuplanner2.databinding.FragmentRefreshRecyclerBinding import de.sebse.fuplanner2.utils.console import de.sebse.fuplanner2.utils.enqueueOneTimeWork import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_ACCOUNT_NAME import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_COURSE_ID import de.sebse.fuplanner2.worker.AnnouncementWorker -import kotlinx.android.synthetic.main.fragment_refresh_recycler.view.* class AnnouncementsFragment : Fragment() { @@ -27,6 +27,7 @@ class AnnouncementsFragment : Fragment() { private val args: AnnouncementsFragmentArgs by navArgs() private val announcementsViewModel: AnnouncementsViewModel by viewModels { AnnouncementsViewModelFactory(args.courseId) } private lateinit var navController: NavController + private lateinit var binding: FragmentRefreshRecyclerBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -35,14 +36,14 @@ class AnnouncementsFragment : Fragment() { val viewManager = LinearLayoutManager(context) val viewAdapter = AnnouncementsAdapter() return inflater.inflate(R.layout.fragment_refresh_recycler, container, false).apply { - console.log(findViewById(R.id.recycler_view), recycler_view) - recycler_view.apply { + console.log(findViewById(R.id.recycler_view), binding.recyclerView) + binding.recyclerView.apply { setHasFixedSize(true) layoutManager = viewManager adapter = viewAdapter } - swipe_refresh_layout.setOnRefreshListener { + binding.swipeRefreshLayout.setOnRefreshListener { enqueueOneTimeWork(context.applicationContext) { it.setInputData(workDataOf( KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name, @@ -50,7 +51,7 @@ class AnnouncementsFragment : Fragment() { )) }.observe(viewLifecycleOwner, Observer { if (it.state.isFinished) - swipe_refresh_layout.isRefreshing = false + binding.swipeRefreshLayout.isRefreshing = false }) } announcementsViewModel.events.observe(viewLifecycleOwner, Observer { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt index 68e8a8a..f0058f5 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_announcements/AnnouncementsViewModel.kt @@ -9,7 +9,7 @@ import de.sebse.fuplanner2.database.Announcement import de.sebse.fuplanner2.database.AppDatabase class AnnouncementsViewModelFactory(private val courseId: Long): ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = AnnouncementsViewModel(courseId) as T + override fun create(modelClass: Class): T = AnnouncementsViewModel(courseId) as T } class AnnouncementsViewModel(courseId: Long) : ViewModel() { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_description/DescriptionFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_description/DescriptionFragment.kt index 3968935..92c427b 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details_description/DescriptionFragment.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_description/DescriptionFragment.kt @@ -14,9 +14,9 @@ import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import de.sebse.fuplanner2.R +import de.sebse.fuplanner2.databinding.FragmentDescriptionBinding import de.sebse.fuplanner2.ui.details.DetailsViewModel import de.sebse.fuplanner2.ui.details.DetailsViewModelFactory -import kotlinx.android.synthetic.main.fragment_description.view.* class DescriptionFragment : Fragment() { @@ -25,6 +25,7 @@ class DescriptionFragment : Fragment() { private val args: DescriptionFragmentArgs by navArgs() private val detailsViewModel: DetailsViewModel by viewModels { DetailsViewModelFactory(args.courseId) } private lateinit var navController: NavController + private lateinit var binding: FragmentDescriptionBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -33,10 +34,10 @@ class DescriptionFragment : Fragment() { return inflater.inflate(R.layout.fragment_description, container, false).apply { val nightModeFlags = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - description.settings.forceDark = WebSettings.FORCE_DARK_ON + binding.description.settings.forceDark = WebSettings.FORCE_DARK_ON } detailsViewModel.course.observe(viewLifecycleOwner, Observer { - description.loadDataWithBaseURL("", it.description, "text/html", "UTF-8", "") + binding.description.loadDataWithBaseURL("", it.description, "text/html", "UTF-8", "") }) navController = findNavController() } diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsFragment.kt index ae63d50..8c95762 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsFragment.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsFragment.kt @@ -14,12 +14,12 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.work.workDataOf import de.sebse.fuplanner2.R import de.sebse.fuplanner2.auth.AppAccounts +import de.sebse.fuplanner2.databinding.FragmentRefreshRecyclerBinding import de.sebse.fuplanner2.utils.enqueueOneTimeWork import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_ACCOUNT_NAME import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_COURSE_ID import de.sebse.fuplanner2.worker.CourseWorker import de.sebse.fuplanner2.worker.EventWorker -import kotlinx.android.synthetic.main.fragment_refresh_recycler.view.* class EventsFragment : Fragment() { @@ -28,6 +28,7 @@ class EventsFragment : Fragment() { private val args: EventsFragmentArgs by navArgs() private val eventsViewModel: EventsViewModel by viewModels { EventsViewModelFactory(args.courseId) } private lateinit var navController: NavController + private lateinit var binding: FragmentRefreshRecyclerBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -36,13 +37,13 @@ class EventsFragment : Fragment() { val viewManager = LinearLayoutManager(context) val viewAdapter = EventsAdapter() return inflater.inflate(R.layout.fragment_refresh_recycler, container, false).apply { - recycler_view.apply { + binding.recyclerView.apply { setHasFixedSize(true) layoutManager = viewManager adapter = viewAdapter } - swipe_refresh_layout.setOnRefreshListener { + binding.swipeRefreshLayout.setOnRefreshListener { enqueueOneTimeWork(context.applicationContext) { it.setInputData(workDataOf( KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name, @@ -50,7 +51,7 @@ class EventsFragment : Fragment() { )) }.observe(viewLifecycleOwner, Observer { if (it.state.isFinished) - swipe_refresh_layout.isRefreshing = false + binding.swipeRefreshLayout.isRefreshing = false }) } eventsViewModel.events.observe(viewLifecycleOwner, Observer { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsViewModel.kt index d21cdfc..b437386 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/details_events/EventsViewModel.kt @@ -10,7 +10,7 @@ import de.sebse.fuplanner2.database.Event class EventsViewModelFactory(private val courseId: Long): ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = EventsViewModel(courseId) as T + override fun create(modelClass: Class): T = EventsViewModel(courseId) as T } class EventsViewModel(courseId: Long) : ViewModel() { diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/notification/NotificationFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/notification/NotificationFragment.kt index 7ea107f..facf0f1 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/notification/NotificationFragment.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/notification/NotificationFragment.kt @@ -10,7 +10,8 @@ import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import de.sebse.fuplanner2.R import de.sebse.fuplanner2.database.AppDatabase -import kotlinx.android.synthetic.main.fragment_recycler.view.* +import de.sebse.fuplanner2.databinding.FragmentRecyclerBinding +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -19,6 +20,7 @@ class NotificationFragment : Fragment() { private lateinit var viewModel: NotificationViewModel private lateinit var navController: NavController + private lateinit var binding: FragmentRecyclerBinding init { setHasOptionsMenu(true) @@ -43,7 +45,7 @@ class NotificationFragment : Fragment() { }) return inflater.inflate(R.layout.fragment_recycler, container, false).apply { - recycler_view.apply { + binding.recyclerView.apply { //setHasFixedSize(true) layoutManager = viewManager adapter = viewAdapter diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleFragment.kt b/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleFragment.kt index b72ddb2..1160167 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleFragment.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleFragment.kt @@ -9,15 +9,16 @@ import androidx.annotation.ColorInt import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController import com.alamkanak.weekview.WeekView import com.alamkanak.weekview.WeekViewDisplayable +import com.alamkanak.weekview.WeekViewEntity import com.alamkanak.weekview.WeekViewEvent import de.sebse.fuplanner2.R import de.sebse.fuplanner2.database.AppDatabase import de.sebse.fuplanner2.database.Course import de.sebse.fuplanner2.database.Event +import de.sebse.fuplanner2.ui.schedule.MyCustomPagingAdapter.LoadMoreHandler import de.sebse.fuplanner2.utils.getHtmlSpannedString import de.sebse.fuplanner2.utils.toTimeString import kotlinx.coroutines.Dispatchers @@ -32,7 +33,7 @@ class ScheduleFragment : Fragment() { private val scheduleViewModel: ScheduleViewModel by viewModels { ScheduleViewModelFactory() } private var currentDate = Calendar.getInstance() - private var weekView: WeekView? = null + private var weekView: WeekView? = null init { setHasOptionsMenu(true) @@ -48,11 +49,6 @@ class ScheduleFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - scheduleViewModel.eventModel.observe(viewLifecycleOwner, Observer { - weekView?.submit(it.map { ContextEvent(requireContext(), it) }) - weekView?.notifyDataSetChanged() - }) - val navController = findNavController() var alertCourse: Course? = null val alertDialog = AlertDialog.Builder(requireContext()) @@ -69,50 +65,56 @@ class ScheduleFragment : Fragment() { return inflater.inflate(R.layout.fragment_schedule, container, false).apply { - weekView = findViewById>(R.id.weekView).apply { - setOnLoadMoreListener { start, end -> - scheduleViewModel.loadBetween(start.timeInMillis, end.timeInMillis) - } + weekView = findViewById(R.id.weekView).apply { + val pagingAdapter = MyCustomPagingAdapter(object : LoadMoreHandler { + override fun setOnLoadMoreListener(startDate: Calendar, endDate: Calendar) = + scheduleViewModel.loadBetween(startDate.timeInMillis, endDate.timeInMillis) - setOnRangeChangeListener { firstVisibleDate, _ -> - currentDate = firstVisibleDate.clone() as Calendar - } + override fun setOnRangeChangeListener( + firstVisibleDate: Calendar, + lastVisibleDate: Calendar + ) { + currentDate = firstVisibleDate.clone() as Calendar + } - setOnEventClickListener { data, _ -> - alertDialog.run { - GlobalScope.launch { - val course: Course? = withContext(Dispatchers.IO) { - AppDatabase.getInstance().courseDao().getCourseById2(data.event.courseId) - } - @Suppress("BlockingMethodInNonBlockingContext") - val cs: CharSequence = SpannableStringBuilder().apply { - if (course != null) { + override fun setOnEventClickListener(data: ContextEvent) { + alertDialog.run { + GlobalScope.launch { + val course: Course = withContext(Dispatchers.IO) { + AppDatabase.getInstance().courseDao().getCourseById2(data.event.courseId) + } + @Suppress("BlockingMethodInNonBlockingContext") + val cs: CharSequence = SpannableStringBuilder().apply { this.append(context.getHtmlSpannedString(R.string.dialog_course_br, course.title)) + if (!data.event.location.isNullOrBlank()) { + this.append(context.getHtmlSpannedString(R.string.dialog_location_br, data.event.location)) + } + this.append(context.getHtmlSpannedString( + R.string.dialog_time, + data.event.startDateTime.toTimeString(context), + (data.event.startDateTime + data.event.duration).toTimeString(context) + )) } - if (!data.event.location.isNullOrBlank()) { - this.append(context.getHtmlSpannedString(R.string.dialog_location_br, data.event.location)) + withContext(Dispatchers.Main) { + alertCourse = course + setTitle(data.event.title) + setMessage(cs) + show() } - this.append(context.getHtmlSpannedString( - R.string.dialog_time, - data.event.startDateTime.toTimeString(context), - (data.event.startDateTime + data.event.duration).toTimeString(context) - )) - } - withContext(Dispatchers.Main) { - alertCourse = course - setTitle(data.event.title) - setMessage(cs) - show() } } } - } + }) + + scheduleViewModel.eventModel.observe(viewLifecycleOwner, { + pagingAdapter.updateEntries(it.map { event -> ContextEvent(requireContext(), event) }) + }) val todayDate = Calendar.getInstance() todayDate.firstDayOfWeek = Calendar.SATURDAY todayDate.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY) todayDate.add(Calendar.DAY_OF_WEEK, 2) - goToDate(todayDate) + scrollToDate(todayDate) } } } @@ -124,18 +126,18 @@ class ScheduleFragment : Fragment() { currentDate.firstDayOfWeek = Calendar.TUESDAY currentDate.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY) currentDate.add(Calendar.DAY_OF_WEEK, -1) - weekView?.goToDate(currentDate) + weekView?.scrollToDate(currentDate) true } R.id.next_week -> { currentDate.firstDayOfWeek = Calendar.MONDAY currentDate.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY) currentDate.add(Calendar.DAY_OF_WEEK, 7) - weekView?.goToDate(currentDate) + weekView?.scrollToDate(currentDate) true } R.id.go_to_today -> { - weekView?.goToToday() + weekView?.scrollToDate(Calendar.getInstance()) true } else -> super.onOptionsItemSelected(item) @@ -143,22 +145,21 @@ class ScheduleFragment : Fragment() { } } -data class ContextEvent(val actCtx: Context, val event: Event): WeekViewDisplayable { - override fun toWeekViewEvent(): WeekViewEvent { +data class ContextEvent(val actCtx: Context, val event: Event) { + fun toWeekViewEvent(): WeekViewEntity { // Build the styling of the event, for instance background color and strike-through - val style = WeekViewEvent.Style.Builder() + val style = WeekViewEntity.Style.Builder() .setBackgroundColor(getColor(actCtx, event.courseId)) - .setTextStrikeThrough(false) .setTextColorResource(R.color.scheduleOtherText) .build() // Build the WeekViewEvent via the Builder - return WeekViewEvent.Builder(this) + return WeekViewEntity.Event.Builder(this) .setId(event.getHash().toLong()) .setTitle(event.title) .setStartTime(Calendar.getInstance().apply { timeInMillis = event.startDateTime }) .setEndTime(Calendar.getInstance().apply { timeInMillis = event.startDateTime + event.duration }) - .setLocation(event.location ?: "") + .setSubtitle(event.location ?: "") .setAllDay(false) .setStyle(style) .build() @@ -214,3 +215,35 @@ data class ContextEvent(val actCtx: Context, val event: Event): WeekViewDisplaya return 0xFF000000.toInt() + (r*0xFF).roundToInt() * 0x10000 + (g*0xFF).roundToInt() * 0x100 + (b*0xFF).roundToInt() } } + +class MyCustomPagingAdapter( + private val loadMoreHandler: LoadMoreHandler +) : WeekView.PagingAdapter() { + + interface LoadMoreHandler { + fun setOnLoadMoreListener(startDate: Calendar, endDate: Calendar) + fun setOnRangeChangeListener(firstVisibleDate: Calendar, lastVisibleDate: Calendar) + fun setOnEventClickListener(data: ContextEvent) + } + + override fun onCreateEntity(item: ContextEvent): WeekViewEntity { + return item.toWeekViewEvent() + } + + override fun onLoadMore(startDate: Calendar, endDate: Calendar) { + loadMoreHandler.setOnLoadMoreListener(startDate, endDate) + } + + override fun onRangeChanged(firstVisibleDate: Calendar, lastVisibleDate: Calendar) { + loadMoreHandler.setOnRangeChangeListener(firstVisibleDate, lastVisibleDate) + } + + override fun onEventClick(data: ContextEvent) { + loadMoreHandler.setOnEventClickListener(data) + } + + fun updateEntries(elements: List) { + this.submitList(elements) + this.refresh() + } +} diff --git a/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleViewModel.kt b/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleViewModel.kt index 3f2bd76..bc6aeb8 100644 --- a/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleViewModel.kt +++ b/app/src/main/java/de/sebse/fuplanner2/ui/schedule/ScheduleViewModel.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class ScheduleViewModelFactory(): ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = ScheduleViewModel() as T + override fun create(modelClass: Class): T = ScheduleViewModel() as T } class ScheduleViewModel() : ViewModel() { diff --git a/app/src/main/java/de/sebse/fuplanner2/whiteboard/Whiteboard.kt b/app/src/main/java/de/sebse/fuplanner2/whiteboard/Whiteboard.kt index 6db4f5b..b209ad0 100644 --- a/app/src/main/java/de/sebse/fuplanner2/whiteboard/Whiteboard.kt +++ b/app/src/main/java/de/sebse/fuplanner2/whiteboard/Whiteboard.kt @@ -40,7 +40,7 @@ object Whiteboard: FUAuthModule() { override suspend fun login(ctx: Context, name: String, password: String, user: User) { val requester = Requester(ctx) val request = requester.get(LOGIN_URL, cookies = getCookies(user, shib = true)) - val samlUri = request.headers["Location"] + val samlUri = request.headers?.get("Location") ?: throw invalidResponse(102100, "Location header not set!") if (samlUri == RESTORE_ON_REDIRECT_TO) { updateCookies(user, request) @@ -60,7 +60,7 @@ object Whiteboard: FUAuthModule() { updateCookies(user, response) // Finish BB & Start Session response = requester.get( - response.networkResponse.headers["Location"] ?: throw invalidResponse(102101, "No Location header to finish Blackboard"), + response.networkResponse.headers?.get("Location") ?: throw invalidResponse(102101, "No Location header to finish Blackboard"), getCookies(user, shib = true) ) @@ -77,13 +77,13 @@ object Whiteboard: FUAuthModule() { } private fun updateCookies(user: User, response: NetData) { - val setCookies = parseCookies(response.networkResponse.allHeaders) - setCookies["JSESSIONID"]?.let { + val setCookies = response.networkResponse.allHeaders?.let { parseCookies(it) } + setCookies?.get("JSESSIONID")?.let { user.cookies.wbJsessionId = it } setCookies - .filter{ (key, _) -> key.startsWith("_shibsession_") } - .forEach { (key, value) -> + ?.filter{ (key, _) -> key.startsWith("_shibsession_") } + ?.forEach { (key, value) -> user.cookies.wbShibKey = key user.cookies.wbShibValue = value return@forEach diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8e69e22..19d21dc 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -9,6 +9,7 @@ tools:openDrawer="start"> diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index a13b5d1..47fadaf 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -7,7 +7,7 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/app_bar_main"> -