SapuSeven / BetterUntis

An alternative mobile client for the Untis timetable system.
GNU General Public License v3.0
240 stars 34 forks source link

[Feature Request] Wear Support #61

Open Domi04151309 opened 4 years ago

Domi04151309 commented 4 years ago

Describe the solution you'd like It would be nice if there was an option to see the current day on your watch.

Domi04151309 commented 4 years ago

I am currently working on that feature.

Domi04151309 commented 4 years ago

I am running into an issue with the communication between the watch and the phone. The watch only receives messages when it feels like it. This is absolutely random.

SapuSeven commented 4 years ago

Sorry to hear that. Unfortunately I cannot help you as I don't own a smartwatch.

Domi04151309 commented 4 years ago

I have kind of figured it out now. Can you describe or show me code that gets the timetable data from Untis? That would speed up development a lot.

SapuSeven commented 4 years ago

Construct a TimetableLoader

val timetableLoader = TimetableLoader(WeakReference(context), timetableDisplay, profileUser, timetableDatabaseInterface)

You need to specify a profileId or load the currently selected profile id like this:

preferenceManager.currentProfileId()

preferenceManager is just a PreferenceManager(context) object.


Specify a target timetable

val target = TimetableLoader.TimetableLoaderTarget(...)

see https://github.com/SapuSeven/BetterUntis/blob/master/app/src/main/java/com/sapuseven/untis/helpers/timetable/TimetableLoader.kt#L160-L165


Load the timetable

timetableLoader.load(target, flags, proxyHost)

For proxyHost, use the preferenceManager from above:

val proxyHost = preferenceManager.defaultPrefs.getString("preference_connectivity_proxy_host", null)

flags can be an OR-combination of TimetableLoader.FLAG_LOAD_CACHE TimetableLoader.FLAG_LOAD_SERVER.

I recommend you use only FLAG_LOAD_CACHE and check for error codes like this:

override fun onTimetableLoadingError(requestId: Int, code: Int?, message: String?) {
    when (code) {
        TimetableLoader.CODE_CACHE_MISSING -> timetableLoader.repeat(requestId, TimetableLoader.FLAG_LOAD_SERVER, proxyHost)
        else -> {
            // show error message
        }
    }
}

That way cache is used if available and only loads from the server if necessary.


Let's hope I didn't forget anything :)

Domi04151309 commented 4 years ago

Thanks @SapuSeven that's already a lot of information. What do you think is the best method for copying a profile to the watch? I'd suggest to only copy the currently selected profile to the watch. It would be nice to copy as less as possible like only the username, school and password.

SapuSeven commented 4 years ago

I really don't know how smartwatch development works. Fact is that the app uses more than just authentication details to display the timetable, as the API provides IDs for every element instead of plain text names. So to view the timetable, you'd need at least data from every table in the userdata.db database with the _user_id field corresponding to the current profile.

Domi04151309 commented 4 years ago

It basically works the same way as with phone development. I am currently trying to copy over the logic from the LoginDataInputActivity so that I can create a user on the watch.

Domi04151309 commented 4 years ago

Some stuff will likely be duplicated so it is worth considering to export the basic logic to a module and only leave the data adaption in the app modules.

Domi04151309 commented 4 years ago

I am having issues with getting the timetable. Can you help me print the lessons for the current day to the logs?

Code

Most of the classes are just copied over but i have to use a modified TimetableLoader and TimetableGridItem because i don't use the weekview.

SapuSeven commented 4 years ago

Alright, first of all we should definitely avoid these duplicate files in the project later on.

Anyways, here's what I would do in the mentioned code section to quickly test it:

        val today = UntisDate.fromLocalDate(LocalDate.now())

        timetableLoader.load(TimetableLoader.TimetableLoaderTarget(today, today, profileUser.userData.elemId, profileUser.userData.elemType ?: ""), TimetableLoader.FLAG_LOAD_SERVER)

and set a breakpoint inside the addTimetableItems function to inspect the items parameter of said function.

Domi04151309 commented 4 years ago

With the code above the app crashes with the following error

2020-03-18 15:55:36.344 25526-25526/com.sapuseven.untis.debug E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.sapuseven.untis.debug, PID: 25526
    kotlinx.serialization.json.JsonUnknownKeyException: Strict JSON encountered unknown key: jsonrpc
    You can disable strict mode to skip unknown keys
        at kotlinx.serialization.json.internal.StreamingJsonInput.decodeElementIndex(StreamingJsonInput.kt:96)
        at com.sapuseven.untis.models.untis.response.TimetableResponse$$serializer.deserialize(Unknown Source:18)
        at com.sapuseven.untis.models.untis.response.TimetableResponse$$serializer.deserialize(TimetableResponse.kt:8)
        at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:33)
        at kotlinx.serialization.json.internal.StreamingJsonInput.decodeSerializableValue(StreamingJsonInput.kt:29)
        at kotlinx.serialization.CoreKt.decode(Core.kt:79)
        at kotlinx.serialization.json.Json.parse(Json.kt:148)
        at com.sapuseven.untis.wear.helpers.TimetableLoader.loadFromServer(TimetableLoader.kt:102)
        at com.sapuseven.untis.wear.helpers.TimetableLoader$loadFromServer$1.invokeSuspend(Unknown Source:13)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6680)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

This is really strange because as far as I understand the code strict mode is already disabled and the key jsonrpc is known.

SapuSeven commented 4 years ago

Look at #14 , it's a weird bug, just clean the project before you build it.

Domi04151309 commented 4 years ago

Quick Status Update

Screenshots

Setup Connecting Timetable Timetable 2
1 2 3 4

Status:

SapuSeven commented 4 years ago

That already looks really nice! As I mentioned, there's still some work to do to avoid duplicate code between the main and the smartwatch code.

Domi04151309 commented 4 years ago

I would recommend merging the wear-support branch when I have implemented everything and then creating a new branch and copying over the logic to a new module which can be included in the app and the wear module.

SapuSeven commented 4 years ago

That sounds like a great idea.

I think you don't need to worry about merging multihour lessons as this will be implemented in that shared module.

I'll start working on it after your PR.

Domi04151309 commented 4 years ago

I think i have found quite an effective method for merging multihour lessons. I first sort the array by the time the lesson starts and then cycle through the array and check if there is an item with the same name and a start time that matches the end time.

SapuSeven commented 4 years ago

Only problem with that is if you have something like a 5 minute break between the lessons. Start and end time won't match in that case.

That's why I went with sorting the items into a timegrid.

Domi04151309 commented 4 years ago

I think we can have two methods for that. One optimized for a list and one optimized for the weekview.

Domi04151309 commented 4 years ago

Some planning

Modules:

app

All things related directly to the app, e.g.:

wear

All things related to the wear app, e.g.:

weekview

No changes there

untis

All things related to the "deeper workings" of the app, e.g.:

SapuSeven commented 4 years ago

That looks great to me. How do you plan on integrating these changes? You can submit pull requests for a new branch if you like.

Domi04151309 commented 4 years ago

I plan to wait until you finished the teacher specific features to not cause unnecessary extra work.

H4K0N42 commented 1 year ago

Will the Galaxy store for the Samsung Watches supported too?

SapuSeven commented 1 year ago

I'm sorry but currently there are no plans to support any other platform than Wear OS. Even then it will probably be a while until smart watches are supported, as this PR is quite out of date by now and I'm not actively working on it myself.