Git Product home page Git Product logo

watchlater's People

Contributors

maxhille avatar strooooke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

strooooke

watchlater's Issues

Not all playlists shown

The app does not show all playlists in case the user has more than 50 playlists. The reason is that the app requests only max 50 playlists (hardcoded constant).

Quick fix would be to increase the value eg to 100. This obviously would not solve the problem per se but would be quick and somewhat safe.

Proper fix would be some sort of paging mechanism which involves much more code and maybe even some sorting or searching of playlists.

Empty screen without Google account

When the device does not have a Google account added, the app will only display an empty screen. The app should display a message explaining that such an account is needed.

UI tests are flaky

android.support.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'is displayed on the screen to the user' doesn't match the selected view.
Expected: is displayed on the screen to the user
Got: "TextView{id=-1, visibility=VISIBLE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Successfully added video, input-type=0, ime-target=false, has-links=false}"

Handle configuration change etc. correctly

Observe: use watchlater to add some video. After success, change orientation, and observe how the whole process starts anew, culminating in video_already_in_playlist, obviously.

YouTube API changes

As documented in [1], the Watch Later playlist ID will be just "WL" in one month. We already heard about this magic ID, but now at least we have some reference.

So it seems there is good and bad in this:

Bad:
We only have one month to update the app. Slower updating users will have a broken app.

Good:
We will save one (or even two?) requests from now on.

[1] https://developers.google.com/youtube/v3/revision_history

Responses from YouTube API may fail in unexpected ways

Observed:

12-17 15:24:01.123 10900-11271/com.lambdasoup.watchlater D/Retrofit: ---> HTTP GET https://www.googleapis.com/youtube/v3/channels?part=contentDetails,snippet&maxResults=50&mine=true
12-17 15:24:01.123 10900-11271/com.lambdasoup.watchlater D/Retrofit: Authorization: Bearer XXXXXXXXXXXXXXXXX
12-17 15:24:01.123 10900-11271/com.lambdasoup.watchlater D/Retrofit: ---> END HTTP (no body)
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: <--- HTTP 200 https://www.googleapis.com/youtube/v3/channels?part=contentDetails,snippet&maxResults=50&mine=true (132ms)
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: OkHttp-Selected-Protocol: h2
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: expires: Thu, 17 Dec 2015 14:24:08 GMT
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: date: Thu, 17 Dec 2015 14:24:08 GMT
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: cache-control: private, max-age=300, must-revalidate, no-transform
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: etag: "kuL0kDMAqRo3pU7O0pwlO-Lfzp4/TjcejXDE4MTg8Poo7zs699673FI"
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: vary: Origin
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: vary: X-Origin
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: content-type: application/json; charset=UTF-8
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: x-content-type-options: nosniff
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: x-frame-options: SAMEORIGIN
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: x-xss-protection: 1; mode=block
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: content-length: 191
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: server: GSE
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: alternate-protocol: 443:quic,p=1
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: alt-svc: quic=":443"; ma=604800; v="30,29,28,27,26,25"
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: OkHttp-Sent-Millis: 1450362241124
12-17 15:24:01.256 10900-11271/com.lambdasoup.watchlater D/Retrofit: OkHttp-Received-Millis: 1450362241256
12-17 15:24:01.257 10900-11271/com.lambdasoup.watchlater D/Retrofit: {
                                                                      "kind": "youtube#channelListResponse",
                                                                      "etag": "\"kuL0kDMAqRo3pU7O0pwlO-Lfzp4/TjcejXDE4MTg8Poo7zs699673FI\"",
                                                                      "pageInfo": {
                                                                       "totalResults": 0,
                                                                       "resultsPerPage": 0
                                                                      },
                                                                      "items": []
                                                                     }
12-17 15:24:01.257 10900-11271/com.lambdasoup.watchlater D/Retrofit: <--- END HTTP (191-byte body)
12-17 15:24:01.264 10900-10900/com.lambdasoup.watchlater D/AndroidRuntime: Shutting down VM
12-17 15:24:01.264 10900-10900/com.lambdasoup.watchlater E/AndroidRuntime: FATAL EXCEPTION: main
                                                                           Process: com.lambdasoup.watchlater, PID: 10900
                                                                           java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
                                                                               at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
                                                                               at java.util.ArrayList.get(ArrayList.java:308)
                                                                               at com.lambdasoup.watchlater.AddActivity$3.success(AddActivity.java:473)
                                                                               at com.lambdasoup.watchlater.AddActivity$3.success(AddActivity.java:470)
                                                                               at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
                                                                               at android.os.Handler.handleCallback(Handler.java:739)
                                                                               at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                               at android.os.Looper.loop(Looper.java:148)
                                                                               at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                               at java.lang.reflect.Method.invoke(Native Method)
                                                                               at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                               at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Redesign main activity to show state, not process

Main activity should still be a dialog. On create, fetch status of video with regards to watchlater playlist of chosen channel/account:

  • account choice first (if necessary)
  • fetch video description (possibly in parallel to the other requests)
  • fetch playlist id
  • fetch info whether in playlist or not (playlistItems.list request with playlistId and videoId -> 0 or 1 result. Comes with title and description, but of course only if the video is actually in the playlist)
  • show video info and two buttons: "open with youtube" and, if video not in playlist, "add to watch later". (possibly: "remove from watch later" if in playlist).
  • after add successfull, show info screen again, with new state
  • or error state with try again as usual.

Channel selection

Re-evalutate the situation regarding multiple/default YouTube channels

Switching account leaves invalid playlist in place

If account and playlist are already selected, and then a new account is added and switched to, the state is definitely not the desired one: the old playlist still gets shown, and trying to select a different one seems to result in a bad request to the YouTube API.

App stopped working

One second I was adding videos. The next second none of the videos will add anymore. I cleared the data from the app and then tried again, added my YouTube account and still it won't work.

Multiple YouTube accounts

The app should also let you choose which account to use in case you have multiple YouTube accounts/channels.

Android 10 default apps setting

Android 10 has a somewhat broken behaviour regarding "default apps": When YouTube is set to "Always ask" it will in fact always ask, regardless of whether Watch Later is set to "Always open".

The instructions in the Launcher Activity should at least display a message for Android 10 users noting that.
Also look into whether this can be detected by Watch Later.

Layout flipping

Launcher Activity in 2.2.0 shortly shows a dissappearing card. Fix/improvement is likely to adjust default visibility for that UI item.

Improve error message: Video already in playlist

Currently, on 403 the message "Your Watch Later playlist is either full or already contains this video" is shown. This is likely misleading, as the problem is an authorization problem in that case.

If the playlist actually contains the video already,

"error": {
"errors": [
{
"domain": "youtube.playlistItem",
"reason": "videoAlreadyInPlaylist",
"message": "Video already in playlist."
}
],
"code": 409,
"message": "Video already in playlist."
}
}

is returned. An appropriate message should be shown.

Pass-through to Youtube App

User request: Wants to set Watchlater as default; allow opening item with youtube via button or something (after always adding it to the watchlater list, if applicable?)

Check with Marshmallow

Android 6.0 Marshmallow has been released. We need to check AppLinks and runtime permissions.

Vimeo

Vimeo support should be added

Error message for removed video

When a video is not available anymore (at your location?), the current message "Check your Internet connection" is misleading.

Let the user set a default account

To start off, great app, thanks!

I do have 4 accounts on my phone, but use only one to watch YouTube. Currently it asks everytime which account to use to send the video.

It would be great to be able to set a default account.

Handling playlists

The URI schema forces us at least in some occasions (e.g. http://www.youtube.com/attribution_link?u=/playlist%3Flist%3DPL0INsTTU1k2UO-2-AwomFmAs4nuZU9ht3%26feature%3Dem-share_playlist_user ) to accept intents for adding playlists.

Those could be handled by adding all videos in the playlist to watchlater, or by saving the playlist for the user (arguably outside the watchlater scope and can easily be done by just using the Youtube app for the intent and pressing the + icon there).

Launcher activity with settings

  1. We want a launcher activity to deconfuse users who installed the app and do not realize how it is supposed to be used. Explanatory text and some diagnosis and advice on configuring defaults/app link settings appropriately.

  2. This is an appropriate settings activity, reachable via the options menu from the AddActivity.

  3. Display and reset the default youtube account (see #23, do setting in the account chooser with "always use this account" checkbox).

Add "autoadd" option

Especially people coming from v1 might miss the automatic add behaviour after getting the v2 update. We could make a "add automatically" option for users who want to keep the old usage pattern.

fix crash when video not found

when the video is not found, the YouTube API does not (anymore?) answer with an HTTP error status but an empty list response. This crashes the app.

Support for different YouTube clients

We got asked to support other YouTube clients like NewPipr, Vanced, SmartTube, too. At the moment, the "Watch Now" button hardcodedly opens the official YouTube app.

Just as an idea: Maybe take all apps with youtube-URL intent filters and remove "Watch Later" from it.

UI test problems

UI tests are still intermittently failing. Problems seen so far:

  • test_add_success does not think that the found view is displayed
  • MockWebServer.shutdown throws Exception about Executor not terminating fast enough

Check Android M

Check compatibility with Android M, especially:

  • default app links, will the official YouTube app prevent us from intercepting the URL intents?
  • optional app permissions

Improved account selection

The Google/YouTube account chooser should be improved:

  1. Should display a message (and suggested action?) in case no account is found
  2. Should display a headline/message why the user is prompted right now

AddActivityTest.test_add failing on API level 19

java.lang.IllegalStateException: Cannot add header view to list -- setAdapter has already been called.
at android.widget.ListView.addHeaderView(ListView.java:258)
at android.widget.ListView.addHeaderView(ListView.java:287)
at com.lambdasoup.watchlater.AddActivity.onMultipleAccounts(AddActivity.java:224)

AddActivity crashes when no internet on open

Have account, playlist and permissions set up. Go in airplane mode. Launch (example) add screen.

Observe crash due to:

    java.lang.RuntimeException: Tea task executor threw exception
        at com.lambdasoup.tea.Tea$runBg$1$2.invoke(Tea.kt:81)
        at com.lambdasoup.tea.Tea$runBg$1$2.invoke(Tea.kt:80)
        at com.lambdasoup.tea.Tea$DefaultEngine.post$lambda-2$lambda-1(Tea.kt:137)
        at com.lambdasoup.tea.Tea$DefaultEngine.$r8$lambda$D5grVRbrpz6uzL7Zldln94I-EHs(Unknown Source:0)
        at com.lambdasoup.tea.Tea$DefaultEngine$$ExternalSyntheticLambda1.run(Unknown Source:2)
        at android.os.Handler.handleCallback(Handler.java:942)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7898)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
     Caused by: java.lang.RuntimeException: could not get token
        at com.lambdasoup.watchlater.data.AccountRepository.getAuthToken(AccountRepository.kt:94)
        at com.lambdasoup.watchlater.viewmodel.AddViewModel$getAuthToken$1.invoke(AddViewModel.kt:54)
        at com.lambdasoup.watchlater.viewmodel.AddViewModel$getAuthToken$1.invoke(AddViewModel.kt:53)
        at com.lambdasoup.tea.Cmd$Companion$task$1$1.invoke(Cmd.kt:51)
        at com.lambdasoup.tea.Tea$runBg$1.invoke(Tea.kt:75)
        at com.lambdasoup.tea.Tea$runBg$1.invoke(Tea.kt:73)
        at com.lambdasoup.tea.Tea$DefaultEngine.execute$lambda-0(Tea.kt:136)
        at com.lambdasoup.tea.Tea$DefaultEngine.$r8$lambda$10Qi51QdNYyh2l4FRDX_Qwxlrbw(Unknown Source:0)
        at com.lambdasoup.tea.Tea$DefaultEngine$$ExternalSyntheticLambda0.run(Unknown Source:2)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:463)
        at java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:307)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
        at java.lang.Thread.run(Thread.java:1012)
     Caused by: java.io.IOException: NetworkError
        at android.accounts.AccountManager.convertErrorToException(AccountManager.java:2628)
        at android.accounts.AccountManager.-$$Nest$mconvertErrorToException(Unknown Source:0)
        at android.accounts.AccountManager$AmsTask$Response.onError(AccountManager.java:2479)
        at android.accounts.IAccountManagerResponse$Stub.onTransact(IAccountManagerResponse.java:107)
        at android.os.Binder.execTransactInternal(Binder.java:1285)
        at android.os.Binder.execTransact(Binder.java:1244)

Crashes due to untimely commit of fragment transactions (and other UI modifications)

java.lang.IllegalStateException: Activity has been destroyed
    at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1433)
    at android.app.BackStackRecord.commitInternal(BackStackRecord.java:687)
    at android.app.BackStackRecord.commit(BackStackRecord.java:663)
    at com.lambdasoup.watchlater.AddActivity$FragmentCoordinator.showFragment(AddActivity.java:684)
    at com.lambdasoup.watchlater.AddActivity$FragmentCoordinator.showProgress(AddActivity.java:660)
    at com.lambdasoup.watchlater.AddActivity.setPlaylistIdAndRetry(AddActivity.java:430)
    at com.lambdasoup.watchlater.AddActivity.addToWatchLaterAndShow(AddActivity.java:189)
    at com.lambdasoup.watchlater.AddActivity.lambda$setAuthTokenAndRetry$5(AddActivity.java:269)
    at com.lambdasoup.watchlater.AddActivity.access$lambda$2(AddActivity.java)
    at com.lambdasoup.watchlater.AddActivity$$Lambda$5.run(Unknown Source)
    at android.accounts.AccountManager$18.run(AccountManager.java:1846)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1280)
    at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1291)
    at android.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
    at android.app.BackStackRecord.commit(BackStackRecord.java:532)
    at com.lambdasoup.watchlater.AddActivity$FragmentCoordinator.showFragment(AddActivity.java:684)
    at com.lambdasoup.watchlater.AddActivity$FragmentCoordinator.showError(AddActivity.java:668)
    at com.lambdasoup.watchlater.AddActivity.showError(AddActivity.java:388)
    at com.lambdasoup.watchlater.AddActivity.access$lambda$1(AddActivity.java)
    at com.lambdasoup.watchlater.AddActivity$$Lambda$4.apply(Unknown Source)
    at com.lambdasoup.watchlater.AddActivity$WatchlaterResult.apply(AddActivity.java:561)
    at com.lambdasoup.watchlater.AddActivity.addToWatchLaterAndShow(AddActivity.java:173)
    at com.lambdasoup.watchlater.AddActivity.onResult(AddActivity.java:352)
    at com.lambdasoup.watchlater.AddActivity.lambda$setAuthTokenAndRetry$5(AddActivity.java:271)
    at com.lambdasoup.watchlater.AddActivity.access$lambda$2(AddActivity.java)
    at com.lambdasoup.watchlater.AddActivity$$Lambda$5.run(Unknown Source)
    at android.accounts.AccountManager$11.run(AccountManager.java:1327)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4827)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
    at dalvik.system.NativeStart.main(Native Method)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.