diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 71677bbf..e970e9a6 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -80,6 +80,9 @@ android {
kotlinOptions {
jvmTarget = "17"
}
+ buildFeatures {
+ viewBinding = true
+ }
}
flutter {
@@ -87,6 +90,9 @@ flutter {
}
dependencies {
+ implementation("com.squareup.okhttp3:okhttp:4.11.0")
+ implementation("androidx.preference:preference-ktx:1.2.1")
+ implementation("androidx.constraintlayout:constraintlayout:2.2.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test:runner:1.6.2")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index df6bec9f..400dfb04 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,49 +1,78 @@
+
-
-
+ FlutterApplication and put your custom class here.
+ -->
+
-
-
+ android:maxSdkVersion="28" />
+
+
+ android:fullBackupContent="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="OTL">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:windowSoftInputMode="adjustResize">
-
-
+
+
+
-
+
+
-
+
-
+
+ android:value="2" />
+
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/NextLectureWidget.kt b/android/app/src/main/java/org/sparcs/otlplus/NextLectureWidget.kt
new file mode 100644
index 00000000..04787307
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/NextLectureWidget.kt
@@ -0,0 +1,48 @@
+package org.sparcs.otlplus
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.content.Context
+import android.widget.RemoteViews
+import org.sparcs.otlplus.api.NextLectureData
+
+/**
+ * Implementation of App Widget functionality.
+ */
+
+class NextLectureWidget : AppWidgetProvider() {
+ override fun onUpdate(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray
+ ) {
+ // There may be multiple widgets active, so update all of them
+ for (appWidgetId in appWidgetIds) {
+ updateNextLectureWidget(context, appWidgetManager, appWidgetId)
+ }
+ }
+
+ override fun onEnabled(context: Context) {
+ // Enter relevant functionality for when the first widget is created
+ }
+
+ override fun onDisabled(context: Context) {
+ // Enter relevant functionality for when the last widget is disabled
+ }
+}
+
+internal fun updateNextLectureWidget(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int
+) {
+ // Construct the RemoteViews object
+ RemoteViews(context.packageName, R.layout.next_lecture_widget).let {
+ it.setTextViewText(R.id.nextLectureDate, NextLectureData.nextLectureDate)
+ it.setTextViewText(R.id.nextLectureName, NextLectureData.nextLectureName)
+ it.setTextViewText(R.id.nextLecturePlace, NextLectureData.nextLecturePlace)
+ it.setTextViewText(R.id.nextLectureProfessor, NextLectureData.nextLectureProfessor)
+ // Instruct the widget manager to update the widget
+ appWidgetManager.updateAppWidget(appWidgetId, it)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/SharedPreferenceUpdateListener.kt b/android/app/src/main/java/org/sparcs/otlplus/SharedPreferenceUpdateListener.kt
new file mode 100644
index 00000000..7307f701
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/SharedPreferenceUpdateListener.kt
@@ -0,0 +1,30 @@
+package org.sparcs.otlplus
+
+import android.appwidget.AppWidgetManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+
+class SharedPreferenceUpdateListener(context: Context) {
+ private val sharedPreferences = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+
+ private val appWidgetManager = AppWidgetManager.getInstance(context)
+ private val componentName = ComponentName(context, TimetableWidget::class.java)
+
+ private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
+ val intent = Intent(context, TimetableWidget::class.java).apply {
+ action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetManager.getAppWidgetIds(componentName))
+ }
+ context.sendBroadcast(intent)
+ }
+
+ fun register() {
+ sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
+ }
+
+ fun unregister() {
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/TimetableWidget.kt b/android/app/src/main/java/org/sparcs/otlplus/TimetableWidget.kt
new file mode 100644
index 00000000..fc0504fb
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/TimetableWidget.kt
@@ -0,0 +1,116 @@
+package org.sparcs.otlplus
+
+import android.annotation.SuppressLint
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.content.Context
+import android.util.TypedValue
+import android.widget.RemoteViews
+import org.sparcs.otlplus.api.ApiLoader
+import org.sparcs.otlplus.api.Lecture
+import org.sparcs.otlplus.api.LocalTime
+import org.sparcs.otlplus.api.TimetableData
+import org.sparcs.otlplus.api.WeekDays
+import org.sparcs.otlplus.constants.BlockColor
+
+val timeTableColumns = listOf(
+ R.id.time_table_column_1,
+ R.id.time_table_column_2,
+ R.id.time_table_column_3,
+ R.id.time_table_column_4,
+ R.id.time_table_column_5,
+)
+
+data class TimeTableElement(
+ val length: Float,
+ val lecture: Lecture?
+)
+
+/**
+ * Implementation of App Widget functionality.
+ */
+class TimetableWidget : AppWidgetProvider() {
+ private val CHANNEL = "https://otl.sparcs.org"
+
+ override fun onUpdate(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray
+ ) {
+ val apiLoader = ApiLoader(context)
+ val sessionUrl = "$CHANNEL/session/info"
+
+ apiLoader.get(sessionUrl) { dataString ->
+// println(dataString)
+ val timetableData = TimetableData(dataString)
+
+ for (appWidgetId in appWidgetIds) {
+ updateTimetableWidget(context, appWidgetManager, appWidgetId, timetableData)
+ }
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+internal fun updateTimetableWidget(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ timetableData: TimetableData,
+) {
+ val views = RemoteViews(context.packageName, R.layout.timetable_widget)
+
+ for (timetableColumn in timeTableColumns) {
+ views.removeAllViews(timetableColumn)
+ }
+
+ val weekTimetable = createTimeTable(timetableData.lectures)
+
+ for ((weekday, dayTimetable) in weekTimetable.withIndex()) {
+ for (timeTableElement in dayTimetable) {
+ val blockView = when(timeTableElement.lecture) {
+ null -> RemoteViews(context.packageName, R.layout.blank_view)
+ else -> RemoteViews(context.packageName, BlockColor.getLayout(timeTableElement.lecture)).apply {
+ setTextViewText(R.id.timetable_block_lecture_name, timeTableElement.lecture.name)
+ }
+ }
+
+ blockView.setViewLayoutHeight(
+ R.id.timetable_block_root,
+ timeTableElement.length * 36,
+ TypedValue.COMPLEX_UNIT_DIP)
+
+ views.addView(timeTableColumns[weekday], blockView)
+ }
+ }
+
+ // Instruct the widget manager to update the widget
+ appWidgetManager.updateAppWidget(appWidgetId, views)
+}
+
+fun createTimeTable(lectures: List): List> {
+ val timetable = List(5) { mutableListOf() }
+
+ for (dayIndex in WeekDays.entries.toTypedArray().indices) {
+ if (dayIndex == 5) break
+ val day = WeekDays.entries[dayIndex]
+
+ val dailyLectures = lectures.flatMap { lecture ->
+ lecture.timeBlocks.filter { it.weekday == day }.map { it to lecture }
+ }.sortedBy { it.first.start.hoursFloat }
+
+ var currentTime = LocalTime(9, 0)
+
+ for ((timeBlock, lecture) in dailyLectures) {
+ if (timeBlock.start.hoursFloat > currentTime.hoursFloat) {
+ val freeTimeLength = timeBlock.start.hoursFloat - currentTime.hoursFloat
+ timetable[dayIndex].add(TimeTableElement(freeTimeLength, null))
+ }
+ val lectureLength = timeBlock.end.hoursFloat - timeBlock.start.hoursFloat
+ timetable[dayIndex].add(TimeTableElement(lectureLength, lecture))
+ currentTime = timeBlock.end
+ }
+ }
+
+ return timetable
+}
diff --git a/android/app/src/main/java/org/sparcs/otlplus/api/ApiLoader.kt b/android/app/src/main/java/org/sparcs/otlplus/api/ApiLoader.kt
new file mode 100644
index 00000000..884cf537
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/api/ApiLoader.kt
@@ -0,0 +1,50 @@
+package org.sparcs.otlplus.api
+
+import android.content.Context
+import okhttp3.*
+import java.io.IOException
+import java.util.concurrent.TimeUnit
+
+class ApiLoader(context: Context) {
+ private val cookies = Cookies(context)
+ private val client = OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS) // Connection timeout
+ .readTimeout(30, TimeUnit.SECONDS) // Read timeout
+ .writeTimeout(30, TimeUnit.SECONDS) // Write timeout
+ .build()
+
+ private val cookieHeader = cookies.header
+ private val csrfToken = cookies.token
+
+ fun get(url: String, then: (String) -> Unit) {
+ val request = Request.Builder()
+ .url(url)
+ .addHeader("cookie", cookieHeader ?: "")
+ .addHeader("X-CSRFToken", csrfToken ?: "")
+ .build()
+
+// println("--------WIDGET: Call sent--------")
+// println("Cookie: $cookieHeader")
+// println("Token: $csrfToken")
+// println("URL: $url")
+
+ client.newCall(request).enqueue(object: Callback {
+ override fun onFailure(call: Call, e: IOException) {
+// println("--------WIDGET: Api call failed--------")
+// e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ if (!response.isSuccessful) {
+// println("--------WIDGET: Api call failed--------")
+// println(response.body?.string())
+ return
+ }
+// println("--------WIDGET: Got response--------")
+ val responseText = response.body?.string() ?: ""
+// println(responseText)
+ then(responseText)
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/api/Cookies.kt b/android/app/src/main/java/org/sparcs/otlplus/api/Cookies.kt
new file mode 100644
index 00000000..5ec20f35
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/api/Cookies.kt
@@ -0,0 +1,14 @@
+package org.sparcs.otlplus.api
+
+import android.content.Context
+
+class Cookies(context: Context) {
+ var header: String?
+ var token: String?
+
+ init {
+ val sharedPreferences = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+ header = sharedPreferences.getString("flutter.cookie_header", null)
+ token = sharedPreferences.getString("flutter.csrf_token", null)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/api/Lecture.kt b/android/app/src/main/java/org/sparcs/otlplus/api/Lecture.kt
new file mode 100644
index 00000000..058890a2
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/api/Lecture.kt
@@ -0,0 +1,32 @@
+package org.sparcs.otlplus.api
+
+enum class WeekDays {
+ Mon,
+ Tue,
+ Wed,
+ Thu,
+ Fri,
+ Undef,
+}
+
+data class LocalTime(
+ val hours: Int,
+ val minutes: Int,
+) {
+ val hoursFloat: Float
+ get() = hours + minutes / 60f
+}
+
+data class TimeBlock(
+ val weekday: WeekDays,
+ val start: LocalTime,
+ val end: LocalTime
+)
+
+data class Lecture(
+ val name: String,
+ val timeBlocks: List,
+ val place: String,
+ val professor: String,
+ val course: Int,
+)
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/api/NextLectureData.kt b/android/app/src/main/java/org/sparcs/otlplus/api/NextLectureData.kt
new file mode 100644
index 00000000..2222f20a
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/api/NextLectureData.kt
@@ -0,0 +1,15 @@
+package org.sparcs.otlplus.api
+
+object NextLectureData { // TODO: Get data using api
+ val nextLectureDate
+ get() = "다음주 월요일"
+
+ val nextLectureName
+ get() = "일반물리학"
+
+ val nextLecturePlace
+ get() = "(E11)창의학습관 311"
+
+ val nextLectureProfessor
+ get() = "김교수"
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/api/TimetableData.kt b/android/app/src/main/java/org/sparcs/otlplus/api/TimetableData.kt
new file mode 100644
index 00000000..950cba84
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/api/TimetableData.kt
@@ -0,0 +1,64 @@
+package org.sparcs.otlplus.api
+
+import org.json.JSONArray
+import org.json.JSONObject
+
+class TimetableData(jsonString: String) {
+ var lectures: List = listOf()
+
+ init {
+ try {
+ val jsonObject = JSONObject(jsonString)
+ val myTimetableLectures = jsonObject.getJSONArray("my_timetable_lectures")
+
+ lectures = (0 until myTimetableLectures.length()).mapNotNull { index ->
+ val lectureJsonObject = myTimetableLectures.getJSONObject(index)
+ if (lectureJsonObject.getInt("year") != 2024 || lectureJsonObject.getInt("semester") != 3) {
+ null
+ } else {
+ Lecture(
+ name = lectureJsonObject.getString("title"),
+ timeBlocks = toTimeBlocks(
+ lectureJsonObject.getJSONArray("classtimes")
+ ),
+ place = lectureJsonObject.getJSONArray("classtimes")
+ .getJSONObject(0)
+ .getString("classroom_short"),
+ professor = lectureJsonObject.getJSONArray("professors")
+ .getJSONObject(0)
+ .getString("name"),
+ course = lectureJsonObject.getInt("id")
+ )
+ }
+ }
+ } catch (e: Exception) {
+// e.printStackTrace()
+ }
+ }
+
+ private fun toTimeBlocks(classTimes: JSONArray): List =
+ (0 until classTimes.length()).map { index ->
+ val date = classTimes.getJSONObject(index).getInt("day")
+ val begin = classTimes.getJSONObject(index).getInt("begin")
+ val end = classTimes.getJSONObject(index).getInt("end")
+
+ TimeBlock(
+ weekday = when (date) {
+ 0 -> WeekDays.Mon
+ 1 -> WeekDays.Tue
+ 2 -> WeekDays.Wed
+ 3 -> WeekDays.Thu
+ 4 -> WeekDays.Fri
+ else -> WeekDays.Undef
+ },
+ start = LocalTime(
+ hours = begin / 60,
+ minutes = begin % 60
+ ),
+ end = LocalTime(
+ hours = end / 60,
+ minutes = end % 60
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/sparcs/otlplus/constants/BlockColor.kt b/android/app/src/main/java/org/sparcs/otlplus/constants/BlockColor.kt
new file mode 100644
index 00000000..55d9409d
--- /dev/null
+++ b/android/app/src/main/java/org/sparcs/otlplus/constants/BlockColor.kt
@@ -0,0 +1,27 @@
+package org.sparcs.otlplus.constants
+
+import org.sparcs.otlplus.R
+import org.sparcs.otlplus.api.Lecture
+
+object BlockColor {
+ val blockColorsLayout = arrayOf(
+ R.layout.timetable_block0,
+ R.layout.timetable_block1,
+ R.layout.timetable_block2,
+ R.layout.timetable_block3,
+ R.layout.timetable_block4,
+ R.layout.timetable_block5,
+ R.layout.timetable_block6,
+ R.layout.timetable_block7,
+ R.layout.timetable_block8,
+ R.layout.timetable_block9,
+ R.layout.timetable_block10,
+ R.layout.timetable_block11,
+ R.layout.timetable_block12,
+ R.layout.timetable_block13,
+ R.layout.timetable_block14,
+ R.layout.timetable_block15,
+ )
+
+ fun getLayout(lecture: Lecture) = blockColorsLayout[lecture.course % 16]
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/org/sparcs/otlplus/MainActivity.kt b/android/app/src/main/kotlin/org/sparcs/otlplus/MainActivity.kt
index 1a80ff32..5dc4111f 100644
--- a/android/app/src/main/kotlin/org/sparcs/otlplus/MainActivity.kt
+++ b/android/app/src/main/kotlin/org/sparcs/otlplus/MainActivity.kt
@@ -3,6 +3,7 @@ package org.sparcs.otlplus
import android.content.ContentValues
import android.content.Intent
import android.os.Build
+import android.os.Bundle
import android.provider.MediaStore
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
@@ -16,6 +17,21 @@ import java.io.IOException
class MainActivity : FlutterActivity() {
private val CHANNEL = "org.sparcs.otlplus"
+ private lateinit var preferenceUpdateListener: SharedPreferenceUpdateListener
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ preferenceUpdateListener = SharedPreferenceUpdateListener(this)
+ preferenceUpdateListener.register()
+ }
+
+ override fun onDestroy() {
+ preferenceUpdateListener.unregister()
+
+ super.onDestroy()
+ }
+
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
diff --git a/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png b/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png
new file mode 100644
index 00000000..894b069a
Binary files /dev/null and b/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png differ
diff --git a/android/app/src/main/res/drawable-v21/app_widget_background.xml b/android/app/src/main/res/drawable-v21/app_widget_background.xml
new file mode 100644
index 00000000..785445c6
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/app_widget_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml b/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml
new file mode 100644
index 00000000..007e2872
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/block_background0.xml b/android/app/src/main/res/drawable/block_background0.xml
new file mode 100644
index 00000000..3efaa66a
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background0.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background1.xml b/android/app/src/main/res/drawable/block_background1.xml
new file mode 100644
index 00000000..b680461b
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background1.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background10.xml b/android/app/src/main/res/drawable/block_background10.xml
new file mode 100644
index 00000000..ab1fac44
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background10.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background11.xml b/android/app/src/main/res/drawable/block_background11.xml
new file mode 100644
index 00000000..105a458e
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background11.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background12.xml b/android/app/src/main/res/drawable/block_background12.xml
new file mode 100644
index 00000000..24022ccc
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background12.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background13.xml b/android/app/src/main/res/drawable/block_background13.xml
new file mode 100644
index 00000000..6ad119ad
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background13.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background14.xml b/android/app/src/main/res/drawable/block_background14.xml
new file mode 100644
index 00000000..da328021
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background14.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background15.xml b/android/app/src/main/res/drawable/block_background15.xml
new file mode 100644
index 00000000..f3dc42e4
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background15.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background2.xml b/android/app/src/main/res/drawable/block_background2.xml
new file mode 100644
index 00000000..0417e154
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background2.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background3.xml b/android/app/src/main/res/drawable/block_background3.xml
new file mode 100644
index 00000000..b60f8bc7
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background3.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background4.xml b/android/app/src/main/res/drawable/block_background4.xml
new file mode 100644
index 00000000..d5c47b09
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background4.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background5.xml b/android/app/src/main/res/drawable/block_background5.xml
new file mode 100644
index 00000000..e122882b
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background5.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background6.xml b/android/app/src/main/res/drawable/block_background6.xml
new file mode 100644
index 00000000..2df97f56
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background6.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background7.xml b/android/app/src/main/res/drawable/block_background7.xml
new file mode 100644
index 00000000..8f70a114
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background7.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background8.xml b/android/app/src/main/res/drawable/block_background8.xml
new file mode 100644
index 00000000..0794434e
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background8.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/block_background9.xml b/android/app/src/main/res/drawable/block_background9.xml
new file mode 100644
index 00000000..93446273
--- /dev/null
+++ b/android/app/src/main/res/drawable/block_background9.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/circle.xml b/android/app/src/main/res/drawable/circle.xml
new file mode 100644
index 00000000..2c81e758
--- /dev/null
+++ b/android/app/src/main/res/drawable/circle.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/dotted_guideline.xml b/android/app/src/main/res/drawable/dotted_guideline.xml
new file mode 100644
index 00000000..405288d9
--- /dev/null
+++ b/android/app/src/main/res/drawable/dotted_guideline.xml
@@ -0,0 +1,12 @@
+
+ -
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/next_lecture_appwidget_preview.png b/android/app/src/main/res/drawable/next_lecture_appwidget_preview.png
new file mode 100644
index 00000000..7d6a8a85
Binary files /dev/null and b/android/app/src/main/res/drawable/next_lecture_appwidget_preview.png differ
diff --git a/android/app/src/main/res/drawable/rounded_corner_background.xml b/android/app/src/main/res/drawable/rounded_corner_background.xml
new file mode 100644
index 00000000..0641904c
--- /dev/null
+++ b/android/app/src/main/res/drawable/rounded_corner_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/solid_guideline.xml b/android/app/src/main/res/drawable/solid_guideline.xml
new file mode 100644
index 00000000..50bdb419
--- /dev/null
+++ b/android/app/src/main/res/drawable/solid_guideline.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/timeline_appwidget_preview.png b/android/app/src/main/res/drawable/timeline_appwidget_preview.png
new file mode 100644
index 00000000..9bb7faf5
Binary files /dev/null and b/android/app/src/main/res/drawable/timeline_appwidget_preview.png differ
diff --git a/android/app/src/main/res/drawable/timetable_appwidget_preview.png b/android/app/src/main/res/drawable/timetable_appwidget_preview.png
new file mode 100644
index 00000000..f3fc1406
Binary files /dev/null and b/android/app/src/main/res/drawable/timetable_appwidget_preview.png differ
diff --git a/android/app/src/main/res/layout-v28/next_lecture_widget.xml b/android/app/src/main/res/layout-v28/next_lecture_widget.xml
new file mode 100644
index 00000000..9ed658fc
--- /dev/null
+++ b/android/app/src/main/res/layout-v28/next_lecture_widget.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/blank_view.xml b/android/app/src/main/res/layout/blank_view.xml
new file mode 100644
index 00000000..938be224
--- /dev/null
+++ b/android/app/src/main/res/layout/blank_view.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/next_lecture_widget.xml b/android/app/src/main/res/layout/next_lecture_widget.xml
new file mode 100644
index 00000000..934a6d02
--- /dev/null
+++ b/android/app/src/main/res/layout/next_lecture_widget.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block0.xml b/android/app/src/main/res/layout/timetable_block0.xml
new file mode 100644
index 00000000..a1b6e32f
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block0.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block1.xml b/android/app/src/main/res/layout/timetable_block1.xml
new file mode 100644
index 00000000..781ab10d
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block1.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block10.xml b/android/app/src/main/res/layout/timetable_block10.xml
new file mode 100644
index 00000000..713687bf
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block10.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block11.xml b/android/app/src/main/res/layout/timetable_block11.xml
new file mode 100644
index 00000000..dc0169ee
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block11.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block12.xml b/android/app/src/main/res/layout/timetable_block12.xml
new file mode 100644
index 00000000..b1efe8b1
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block12.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block13.xml b/android/app/src/main/res/layout/timetable_block13.xml
new file mode 100644
index 00000000..fdb19e5d
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block13.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block14.xml b/android/app/src/main/res/layout/timetable_block14.xml
new file mode 100644
index 00000000..a6ab67d3
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block14.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block15.xml b/android/app/src/main/res/layout/timetable_block15.xml
new file mode 100644
index 00000000..72757fe5
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block15.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block2.xml b/android/app/src/main/res/layout/timetable_block2.xml
new file mode 100644
index 00000000..6f9bc997
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block2.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block3.xml b/android/app/src/main/res/layout/timetable_block3.xml
new file mode 100644
index 00000000..3b3ab394
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block3.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block4.xml b/android/app/src/main/res/layout/timetable_block4.xml
new file mode 100644
index 00000000..f1b8f0e8
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block4.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block5.xml b/android/app/src/main/res/layout/timetable_block5.xml
new file mode 100644
index 00000000..e4b90842
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block5.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block6.xml b/android/app/src/main/res/layout/timetable_block6.xml
new file mode 100644
index 00000000..8360135b
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block6.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block7.xml b/android/app/src/main/res/layout/timetable_block7.xml
new file mode 100644
index 00000000..05df1774
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block7.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block8.xml b/android/app/src/main/res/layout/timetable_block8.xml
new file mode 100644
index 00000000..a0c2999f
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block8.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_block9.xml b/android/app/src/main/res/layout/timetable_block9.xml
new file mode 100644
index 00000000..55bd5683
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_block9.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/timetable_widget.xml b/android/app/src/main/res/layout/timetable_widget.xml
new file mode 100644
index 00000000..7dcb2752
--- /dev/null
+++ b/android/app/src/main/res/layout/timetable_widget.xml
@@ -0,0 +1,542 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-night-v31/themes.xml b/android/app/src/main/res/values-night-v31/themes.xml
new file mode 100644
index 00000000..f253c9da
--- /dev/null
+++ b/android/app/src/main/res/values-night-v31/themes.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-v21/styles.xml b/android/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 00000000..0b35f7d8
--- /dev/null
+++ b/android/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml
index ea2ecb03..9150fd3a 100644
--- a/android/app/src/main/res/values-v31/styles.xml
+++ b/android/app/src/main/res/values-v31/styles.xml
@@ -18,4 +18,18 @@
+
+
+
+
diff --git a/android/app/src/main/res/values-v31/themes.xml b/android/app/src/main/res/values-v31/themes.xml
new file mode 100644
index 00000000..badd306a
--- /dev/null
+++ b/android/app/src/main/res/values-v31/themes.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/attrs.xml b/android/app/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..7781ac86
--- /dev/null
+++ b/android/app/src/main/res/values/attrs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
index 44f705b6..f196f0bd 100644
--- a/android/app/src/main/res/values/colors.xml
+++ b/android/app/src/main/res/values/colors.xml
@@ -1,4 +1,18 @@
+
+ #F9F0F0
+ #E54C65
+ #000000
+ #757575
+ #B54896
+ #40000000
+
+
+
#F9F0F0
+ #FFE1F5FE
+ #FF81D4FA
+ #FF039BE5
+ #FF01579B
\ No newline at end of file
diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..4db8c590
--- /dev/null
+++ b/android/app/src/main/res/values/dimens.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..815510b0
--- /dev/null
+++ b/android/app/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+
+
+ EXAMPLE
+ Add widget
+ This is an app widget description
+ 월
+ 화
+ 수
+ 목
+ 금
+ 토
+ 일
+ 다음 강의의 세부 정보를 확인하세요.
+ 다음 강의
+ 6시간 뒤
+ 교양분관
+ OTL 개론
+ ○○○ 교수님
+ {placeholder}
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
index 4c573225..d2bc3314 100644
--- a/android/app/src/main/res/values/styles.xml
+++ b/android/app/src/main/res/values/styles.xml
@@ -1,8 +1,54 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..dcd8899e
--- /dev/null
+++ b/android/app/src/main/res/values/themes.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/xml/next_lecture_widget_info.xml b/android/app/src/main/res/xml/next_lecture_widget_info.xml
new file mode 100644
index 00000000..f9b82c9d
--- /dev/null
+++ b/android/app/src/main/res/xml/next_lecture_widget_info.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/xml/timetable_widget_info.xml b/android/app/src/main/res/xml/timetable_widget_info.xml
new file mode 100644
index 00000000..4e610e39
--- /dev/null
+++ b/android/app/src/main/res/xml/timetable_widget_info.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index 9f9d60b6..6cf1b1fe 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -19,7 +19,7 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
- id("com.android.application") version "8.7.1" apply false
+ id("com.android.application") version "8.6.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.23" apply false
}
diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart
index 665f87c5..1e89514b 100644
--- a/lib/pages/main_page.dart
+++ b/lib/pages/main_page.dart
@@ -1,3 +1,5 @@
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:otlplus/constants/text_styles.dart';
@@ -23,6 +25,7 @@ import 'package:otlplus/widgets/timetable_block.dart';
import 'package:otlplus/widgets/today_timetable.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_widgetkit/flutter_widgetkit.dart';
+import 'package:shared_preferences/shared_preferences.dart';
import 'package:webview_cookie_manager/webview_cookie_manager.dart';
import '../models/lecture.dart';
@@ -40,37 +43,61 @@ class _MainPageState extends State {
@override
void initState() {
super.initState();
- initWidgetKitData();
+ initWidgetData();
+ }
+
+ Future initWidgetData() async {
+ final cookieManager = WebviewCookieManager();
+ final cookies = await cookieManager.getCookies('https://otl.sparcs.org');
+ initAndroidWidgetData(cookies);
+ initWidgetKitData(cookies);
}
- Future initWidgetKitData() async {
+ Future initAndroidWidgetData(List cookies) async {
+ final prefs = await SharedPreferences.getInstance();
+ final cookieHeader =
+ cookies.map((cookie) => "${cookie.name}=${cookie.value}").join("; ");
+ final csrfToken =
+ cookies.firstWhere((cookie) => cookie.name == "csrftoken").value;
+ await prefs.setString("cookie_header", cookieHeader);
+ await prefs.setString("csrf_token", csrfToken);
+ }
+
+ Future initWidgetKitData(List cookies) async {
try {
- final cookieManager = WebviewCookieManager();
- final cookies = await cookieManager.getCookies('https://otl.sparcs.org');
for (var cookie in cookies) {
if (cookie.name == 'refreshToken') {
- WidgetKit.setItem(
- 'refreshToken', cookie.value, 'group.org.sparcs.otl');
- WidgetKit.reloadAllTimelines();
+ if (Platform.isIOS) {
+ WidgetKit.setItem(
+ 'refreshToken', cookie.value, 'group.org.sparcs.otl');
+ WidgetKit.reloadAllTimelines();
+ }
}
if (cookie.name == 'csrftoken') {
- WidgetKit.setItem('csrftoken', cookie.value, 'group.org.sparcs.otl');
- WidgetKit.reloadAllTimelines();
+ if (Platform.isIOS) {
+ WidgetKit.setItem(
+ 'csrftoken', cookie.value, 'group.org.sparcs.otl');
+ WidgetKit.reloadAllTimelines();
+ }
}
if (cookie.name == 'accessToken') {
- WidgetKit.setItem(
- 'accessToken', cookie.value, 'group.org.sparcs.otl');
- WidgetKit.reloadAllTimelines();
+ if (Platform.isIOS) {
+ WidgetKit.setItem(
+ 'accessToken', cookie.value, 'group.org.sparcs.otl');
+ WidgetKit.reloadAllTimelines();
+ }
}
}
final infoModel = InfoModel();
await infoModel.getInfo();
if (infoModel.hasData) {
- WidgetKit.setItem(
- 'uid', infoModel.user.id.toString(), 'group.org.sparcs.otl');
- WidgetKit.reloadAllTimelines();
+ if (Platform.isIOS) {
+ WidgetKit.setItem(
+ 'uid', infoModel.user.id.toString(), 'group.org.sparcs.otl');
+ WidgetKit.reloadAllTimelines();
+ }
}
} catch (exception) {
print(exception);